Skip to content

Commit

Permalink
Export existing Fantom tests
Browse files Browse the repository at this point in the history
Summary: Changelog: [internal]

Differential Revision: D66702625
  • Loading branch information
rubennorte authored and facebook-github-bot committed Dec 4, 2024
1 parent 6bbca83 commit ab7db35
Show file tree
Hide file tree
Showing 10 changed files with 4,502 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/

import '../../../Core/InitializeCore.js';
import * as ReactNativeTester from '../../../../src/private/__tests__/ReactNativeTester';
import TextInput from '../TextInput';
import * as React from 'react';
import {useEffect, useLayoutEffect, useRef} from 'react';

describe('TextInput', () => {
it('creates view before dispatching view command from ref function', () => {
const root = ReactNativeTester.createRoot();

ReactNativeTester.runTask(() => {
root.render(
<TextInput
ref={node => {
if (node) {
node.focus();
}
}}
/>,
);
});

const mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(2);
expect(mountingLogs[0]).toBe('create view type: `AndroidTextInput`');
expect(mountingLogs[1]).toBe(
'dispatch command `focus` on component `AndroidTextInput`',
);
});

it('creates view before dispatching view command from useLayoutEffect', () => {
const root = ReactNativeTester.createRoot();

function Component() {
const textInputRef = useRef<null | React.ElementRef<typeof TextInput>>(
null,
);

useLayoutEffect(() => {
textInputRef.current?.focus();
});

return <TextInput ref={textInputRef} />;
}
ReactNativeTester.runTask(() => {
root.render(<Component />);
});

const mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(2);
expect(mountingLogs[0]).toBe('create view type: `AndroidTextInput`');
expect(mountingLogs[1]).toBe(
'dispatch command `focus` on component `AndroidTextInput`',
);
});

it('creates view before dispatching view command from useEffect', () => {
const root = ReactNativeTester.createRoot();

function Component() {
const textInputRef = useRef<null | React.ElementRef<typeof TextInput>>(
null,
);

useEffect(() => {
textInputRef.current?.focus();
});

return <TextInput ref={textInputRef} />;
}
ReactNativeTester.runTask(() => {
root.render(<Component />);
});

const mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(2);
expect(mountingLogs[0]).toBe('create view type: `AndroidTextInput`');
expect(mountingLogs[1]).toBe(
'dispatch command `focus` on component `AndroidTextInput`',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/

import '../../Core/InitializeCore.js';
import * as ReactNativeTester from '../../../src/private/__tests__/ReactNativeTester';
import View from '../../Components/View/View';
import * as React from 'react';
import {Suspense, startTransition, use} from 'react';

let resolveFunction: (() => void) | null = null;

type SquareData = {
color: 'red' | 'green',
};

enum SquareId {
Green = 'green-square',
Red = 'red-square',
}

async function getGreenSquareData(): Promise<SquareData> {
await new Promise(resolve => {
resolveFunction = resolve;
});
return {
color: 'green',
};
}

async function getRedSquareData(): Promise<SquareData> {
await new Promise(resolve => {
resolveFunction = resolve;
});
return {
color: 'red',
};
}

const cache = new Map<SquareId, SquareData>();

async function getData(squareId: SquareId): Promise<SquareData> {
switch (squareId) {
case SquareId.Green:
return await getGreenSquareData();
case SquareId.Red:
return await getRedSquareData();
}
}

async function fetchData(squareId: SquareId): Promise<SquareData> {
const data = await getData(squareId);
cache.set(squareId, data);
return data;
}

function Square(props: {squareId: SquareId}) {
let data = cache.get(props.squareId);
if (data == null) {
data = use(fetchData(props.squareId));
}
return <View key={data.color} nativeID={'square with data: ' + data.color} />;
}

function GreenSquare() {
return <Square squareId={SquareId.Green} />;
}

function RedSquare() {
return <Square squareId={SquareId.Red} />;
}

function Fallback() {
return <View nativeID="suspense fallback" />;
}

describe('Suspense', () => {
it('shows fallback if data is not available', () => {
cache.clear();
const root = ReactNativeTester.createRoot();

ReactNativeTester.runTask(() => {
root.render(
<Suspense fallback={<Fallback />}>
<GreenSquare />
</Suspense>,
);
});

let mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(1);
expect(mountingLogs[0]).toBe(
'create view type: `View` nativeId: `suspense fallback`',
);

expect(resolveFunction).not.toBeNull();
ReactNativeTester.runTask(() => {
resolveFunction?.();
resolveFunction = null;
});

mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(1);
expect(mountingLogs[0]).toBe(
'create view type: `View` nativeId: `square with data: green`',
);

ReactNativeTester.runTask(() => {
root.render(
<Suspense fallback={<Fallback />}>
<RedSquare />
</Suspense>,
);
});

mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(1);
expect(mountingLogs[0]).toBe(
'create view type: `View` nativeId: `suspense fallback`',
);

expect(resolveFunction).not.toBeNull();
ReactNativeTester.runTask(() => {
resolveFunction?.();
resolveFunction = null;
});

mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(1);
expect(mountingLogs[0]).toBe(
'create view type: `View` nativeId: `square with data: red`',
);

ReactNativeTester.runTask(() => {
root.render(
<Suspense fallback={<Fallback />}>
<GreenSquare />
</Suspense>,
);
});

mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(1);
expect(mountingLogs[0]).toBe(
'create view type: `View` nativeId: `square with data: green`',
);

expect(resolveFunction).toBeNull();

root.destroy();
});

// TODO(T207868872): this test only succeeds with enableFabricCompleteRootInCommitPhase enabled.
// enableFabricCompleteRootInCommitPhase is hardcoded to true in the testing environment.
it('shows stale data while transition is happening', () => {
cache.clear();
cache.set(SquareId.Green, {color: 'green'});

const root = ReactNativeTester.createRoot();

function App(props: {color: 'red' | 'green'}) {
return (
<Suspense fallback={<Fallback />}>
{props.color === 'green' ? <GreenSquare /> : <RedSquare />}
</Suspense>
);
}

ReactNativeTester.runTask(() => {
root.render(<App color="green" />);
});

let mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(1);
expect(mountingLogs[0]).toBe(
'create view type: `View` nativeId: `square with data: green`',
);

expect(resolveFunction).toBeNull();
ReactNativeTester.runTask(() => {
startTransition(() => {
root.render(<App color="red" />);
});
});

mountingLogs = root.getMountingLogs();

// Green square is still mounted. Fallback is not shown to the user.
expect(mountingLogs.length).toBe(0);

expect(resolveFunction).not.toBeNull();
ReactNativeTester.runTask(() => {
resolveFunction?.();
resolveFunction = null;
});

mountingLogs = root.getMountingLogs();

expect(mountingLogs.length).toBe(1);
expect(mountingLogs[0]).toBe(
'create view type: `View` nativeId: `square with data: red`',
);

root.destroy();
});
});
Loading

0 comments on commit ab7db35

Please sign in to comment.