diff --git a/packages/react-native-fantom/src/__tests__/Fantom-itest.js b/packages/react-native-fantom/src/__tests__/Fantom-itest.js index e16fdeffc33408..d4a2f97216bd82 100644 --- a/packages/react-native-fantom/src/__tests__/Fantom-itest.js +++ b/packages/react-native-fantom/src/__tests__/Fantom-itest.js @@ -10,6 +10,7 @@ */ import 'react-native/Libraries/Core/InitializeCore'; + import {createRoot, runTask} from '..'; import * as React from 'react'; import {Text, View} from 'react-native'; @@ -70,6 +71,32 @@ describe('Fantom', () => { expect(completed).toBe(true); }); + + // TODO: when error handling is fixed, this should verify using `toThrow` + it('should throw when running a task inside another task', () => { + let lastCallbackExecuted = 0; + runTask(() => { + lastCallbackExecuted = 1; + runTask(() => { + lastCallbackExecuted = 2; + throw new Error('Recursive runTask should be unreachable'); + }); + }); + expect(lastCallbackExecuted).toBe(1); + + runTask(() => { + queueMicrotask(() => { + lastCallbackExecuted = 3; + runTask(() => { + lastCallbackExecuted = 4; + throw new Error( + 'Recursive runTask from micro-task should be unreachable', + ); + }); + }); + }); + expect(lastCallbackExecuted).toBe(3); + }); }); describe('getRenderedOutput', () => { diff --git a/packages/react-native-fantom/src/index.js b/packages/react-native-fantom/src/index.js index 606be08ad430de..0c3b79f7dbcd3b 100644 --- a/packages/react-native-fantom/src/index.js +++ b/packages/react-native-fantom/src/index.js @@ -60,17 +60,31 @@ class Root { // TODO: add an API to check if all surfaces were deallocated when tests are finished. } +let flushingQueue = false; + /* * Runs a task on on the event loop. To be used together with root.render. * * React must run inside of event loop to ensure scheduling environment is closer to production. */ export function runTask(task: () => void | Promise) { + if (flushingQueue) { + throw new Error( + 'Nested runTask calls are not allowed. If you want to schedule a task from inside another task, use scheduleTask instead.', + ); + } + nativeRuntimeScheduler.unstable_scheduleCallback( schedulerPriorityImmediate, task, ); - global.$$JSTesterModuleName$$.flushMessageQueue(); + + try { + flushingQueue = true; + global.$$JSTesterModuleName$$.flushMessageQueue(); + } finally { + flushingQueue = false; + } } // TODO: Add option to define surface props and pass it to startSurface