introduce Fantom.dispatchNativeEvent (#48793)

Summary:
changelog: [internal]

Pull Request resolved: https://github.com/facebook/react-native/pull/48793

Adds new method `dispatchNativeEvent` to Fantom give option to dispatch fake native events.

The API is expanded in subsequent diffs. The version introduced in this diff supports only dispatching event without payload and without any options.

Reviewed By: rubennorte

Differential Revision: D68331986

fbshipit-source-id: 075360e1d6874794ba6df966087fbe6a0a820cbc
This commit is contained in:
Samuel Susla
2025-01-22 06:55:18 -08:00
committed by Facebook GitHub Bot
parent a24f9ef825
commit ff0bcb2427
5 changed files with 67 additions and 3 deletions
+43 -2
View File
@@ -14,9 +14,15 @@ import 'react-native/Libraries/Core/InitializeCore';
import type {Root} from '..';
import {createRoot, runTask} from '..';
import {
createRoot,
dispatchNativeEvent,
runOnUIThread,
runTask,
runWorkLoop,
} from '..';
import * as React from 'react';
import {Text, View} from 'react-native';
import {Text, TextInput, View} from 'react-native';
import ensureInstance from 'react-native/src/private/utilities/ensureInstance';
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
@@ -369,4 +375,39 @@ describe('Fantom', () => {
});
});
});
describe('runOnUIThread + dispatchNativeEvent', () => {
it('sends focus event', () => {
const root = createRoot();
let maybeNode;
let focusEvent = jest.fn();
runTask(() => {
root.render(
<TextInput
onFocus={focusEvent}
ref={node => {
maybeNode = node;
}}
/>,
);
});
const element = ensureInstance(maybeNode, ReactNativeElement);
expect(focusEvent).toHaveBeenCalledTimes(0);
runOnUIThread(() => {
dispatchNativeEvent(element, 'focus');
});
// The tasks have not run.
expect(focusEvent).toHaveBeenCalledTimes(0);
runWorkLoop();
expect(focusEvent).toHaveBeenCalledTimes(1);
});
});
});
+16 -1
View File
@@ -14,6 +14,8 @@ import type {
} from './getFantomRenderedOutput';
import type {MixedElement} from 'react';
import ReactNativeElement from '../../react-native/src/private/webapis/dom/nodes/ReadOnlyNode';
import {getShadowNode} from '../../react-native/src/private/webapis/dom/nodes/ReadOnlyNode';
import * as Benchmark from './Benchmark';
import getFantomRenderedOutput from './getFantomRenderedOutput';
import ReactFabric from 'react-native/Libraries/Renderer/shims/ReactFabric';
@@ -100,7 +102,7 @@ export function scheduleTask(task: () => void | Promise<void>) {
let flushingQueue = false;
/*
* Runs a task on on the event loop. To be used together with root.render.
* Runs a task 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.
*/
@@ -115,6 +117,14 @@ export function runTask(task: () => void | Promise<void>) {
runWorkLoop();
}
/*
* Simmulates running a task on the UI thread and forces side effect to drain the event queue, dispatching events to JavaScript.
*/
export function runOnUIThread(task: () => void) {
task();
NativeFantom.flushEventQueue();
}
/**
* Runs the event loop until all tasks are executed.
*/
@@ -139,6 +149,11 @@ export function createRoot(rootConfig?: RootConfig): Root {
return new Root(rootConfig);
}
export function dispatchNativeEvent(node: ReactNativeElement, type: string) {
const shadowNode = getShadowNode(node);
NativeFantom.dispatchNativeEvent(shadowNode, type);
}
export const unstable_benchmark = Benchmark;
type FantomConstants = $ReadOnly<{
@@ -10916,8 +10916,10 @@ interface Spec extends TurboModule {
devicePixelRatio: number
) => void;
stopSurface: (surfaceId: number) => void;
dispatchNativeEvent: (shadowNode: mixed, type: string) => void;
getMountingManagerLogs: (surfaceId: number) => Array<string>;
flushMessageQueue: () => void;
flushEventQueue: () => void;
getRenderedOutput: (surfaceId: number, config: RenderFormatOptions) => string;
reportTestSuiteResultsJSON: (results: string) => void;
}
@@ -10,6 +10,7 @@
#include <react/renderer/dom/DOM.h>
#include <react/renderer/uimanager/PointerEventsProcessor.h>
#include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/renderer/uimanager/primitives.h>
#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
#include "Plugins.h"
@@ -26,8 +26,13 @@ interface Spec extends TurboModule {
devicePixelRatio: number,
) => void;
stopSurface: (surfaceId: number) => void;
dispatchNativeEvent: (
shadowNode: mixed /* ShadowNode */,
type: string,
) => void;
getMountingManagerLogs: (surfaceId: number) => Array<string>;
flushMessageQueue: () => void;
flushEventQueue: () => void;
getRenderedOutput: (surfaceId: number, config: RenderFormatOptions) => string;
reportTestSuiteResultsJSON: (results: string) => void;
}