diff --git a/packages/polyfills/__tests__/console-itest.js b/packages/polyfills/__tests__/console-itest.js index a58937f6a156..61f1e5ade4d4 100644 --- a/packages/polyfills/__tests__/console-itest.js +++ b/packages/polyfills/__tests__/console-itest.js @@ -18,333 +18,261 @@ const LOG_LEVELS = { describe('console', () => { describe('.table(data, rows)', () => { - it('should print the passed array as a Markdown table', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); + let originalNativeLoggingHook; + let logFn; - // TODO: replace with `beforeEach` when supported. - try { - console.table([ - {name: 'First', value: 500}, - {name: 'Second', value: 600}, - {name: 'Third', value: 700}, - {name: 'Fourth', value: 800, extraValue: true}, - ]); + beforeEach(() => { + originalNativeLoggingHook = global.nativeLoggingHook; + logFn = global.nativeLoggingHook = jest.fn(); + }); - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([ - ` + afterEach(() => { + global.nativeLoggingHook = originalNativeLoggingHook; + }); + + it('should print the passed array as a Markdown table', () => { + console.table([ + {name: 'First', value: 500}, + {name: 'Second', value: 600}, + {name: 'Third', value: 700}, + {name: 'Fourth', value: 800, extraValue: true}, + ]); + + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` | (index) | name | value | extraValue | | ------- | -------- | ----- | ---------- | | 0 | 'First' | 500 | | | 1 | 'Second' | 600 | | | 2 | 'Third' | 700 | | | 3 | 'Fourth' | 800 | true |`, - LOG_LEVELS.info, - ]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + LOG_LEVELS.info, + ]); }); it('should print the passed dictionary as a Markdown table', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); - - // TODO: replace with `beforeEach` when supported. - try { - console.table({ - first: {name: 'First', value: 500}, - second: {name: 'Second', value: 600}, - third: {name: 'Third', value: 700}, - fourth: {name: 'Fourth', value: 800, extraValue: true}, - }); - - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([ - ` + console.table({ + first: {name: 'First', value: 500}, + second: {name: 'Second', value: 600}, + third: {name: 'Third', value: 700}, + fourth: {name: 'Fourth', value: 800, extraValue: true}, + }); + + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` | (index) | name | value | extraValue | | ------- | -------- | ----- | ---------- | | first | 'First' | 500 | | | second | 'Second' | 600 | | | third | 'Third' | 700 | | | fourth | 'Fourth' | 800 | true |`, - LOG_LEVELS.info, - ]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + LOG_LEVELS.info, + ]); }); it('should work with different types of values', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); - - // TODO: replace with `beforeEach` when supported. - try { - console.table([ - { - string: '', - number: 0, - boolean: true, - function: () => {}, - object: {a: 1, b: 2}, - null: null, - undefined: undefined, - }, - { - string: 'a', - number: 1, - boolean: true, - function: () => {}, - object: {a: 1, b: 2}, - null: null, - undefined: undefined, - }, - { - string: 'aa', - number: 2, - boolean: false, - function: () => {}, - object: {a: 1, b: 2}, - null: null, - undefined: undefined, - }, - { - string: 'aaa', - number: 3, - boolean: false, - function: () => {}, - object: {a: 1, b: 2}, - null: null, - undefined: undefined, - }, - ]); - - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([ - ` + console.table([ + { + string: '', + number: 0, + boolean: true, + function: () => {}, + object: {a: 1, b: 2}, + null: null, + undefined: undefined, + }, + { + string: 'a', + number: 1, + boolean: true, + function: () => {}, + object: {a: 1, b: 2}, + null: null, + undefined: undefined, + }, + { + string: 'aa', + number: 2, + boolean: false, + function: () => {}, + object: {a: 1, b: 2}, + null: null, + undefined: undefined, + }, + { + string: 'aaa', + number: 3, + boolean: false, + function: () => {}, + object: {a: 1, b: 2}, + null: null, + undefined: undefined, + }, + ]); + + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` | (index) | string | number | boolean | function | object | null | undefined | | ------- | ------ | ------ | ------- | -------- | ------ | ---- | --------- | | 0 | '' | 0 | true | ƒ | {…} | null | undefined | | 1 | 'a' | 1 | true | ƒ | {…} | null | undefined | | 2 | 'aa' | 2 | false | ƒ | {…} | null | undefined | | 3 | 'aaa' | 3 | false | ƒ | {…} | null | undefined |`, - LOG_LEVELS.info, - ]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + LOG_LEVELS.info, + ]); }); it('should print the keys in all the objects', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); - - // TODO: replace with `beforeEach` when supported. - try { - console.table([ - {name: 'foo'}, - {name: 'bar', value: 1}, - {value: 2, surname: 'baz'}, - {address: 'other'}, - ]); - - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([ - ` + console.table([ + {name: 'foo'}, + {name: 'bar', value: 1}, + {value: 2, surname: 'baz'}, + {address: 'other'}, + ]); + + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` | (index) | name | value | surname | address | | ------- | ----- | ----- | ------- | ------- | | 0 | 'foo' | | | | | 1 | 'bar' | 1 | | | | 2 | | 2 | 'baz' | | | 3 | | | | 'other' |`, - LOG_LEVELS.info, - ]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + LOG_LEVELS.info, + ]); }); it('should print an empty string for empty arrays', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); - - // TODO: replace with `beforeEach` when supported. - try { - console.table([]); + console.table([]); - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([``, LOG_LEVELS.info]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([``, LOG_LEVELS.info]); }); it('should print an empty string for empty dictionaries', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); + console.table({}); - // TODO: replace with `beforeEach` when supported. - try { - console.table({}); - - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([``, LOG_LEVELS.info]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([``, LOG_LEVELS.info]); }); // This test is currently failing it('should print an indices table for an array of empty objects', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); - - // TODO: replace with `beforeEach` when supported. - try { - console.table([{}, {}, {}, {}]); + console.table([{}, {}, {}, {}]); - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([ - ` + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` | (index) | | ------- | | 0 | | 1 | | 2 | | 3 |`, - LOG_LEVELS.info, - ]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + LOG_LEVELS.info, + ]); }); it('should print an indices table for a dictionary of empty objects', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); - - // TODO: replace with `beforeEach` when supported. - try { - console.table({ - first: {}, - second: {}, - third: {}, - fourth: {}, - }); - - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([ - ` + console.table({ + first: {}, + second: {}, + third: {}, + fourth: {}, + }); + + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` | (index) | | ------- | | first | | second | | third | | fourth |`, - LOG_LEVELS.info, - ]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + LOG_LEVELS.info, + ]); }); it('should not modify the logged value', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; global.nativeLoggingHook = jest.fn(); - // TODO: replace with `beforeEach` when supported. - try { - const array = [ - {name: 'First', value: 500}, - {name: 'Second', value: 600}, - {name: 'Third', value: 700}, - {name: 'Fourth', value: 800, extraValue: true}, - ]; - const originalArrayValue = JSON.parse(JSON.stringify(array)); + const array = [ + {name: 'First', value: 500}, + {name: 'Second', value: 600}, + {name: 'Third', value: 700}, + {name: 'Fourth', value: 800, extraValue: true}, + ]; + const originalArrayValue = JSON.parse(JSON.stringify(array)); - console.table(array); + console.table(array); - expect(array).toEqual(originalArrayValue); + expect(array).toEqual(originalArrayValue); - const object = { - first: {name: 'First', value: 500}, - second: {name: 'Second', value: 600}, - third: {name: 'Third', value: 700}, - fourth: {name: 'Fourth', value: 800, extraValue: true}, - }; + const object = { + first: {name: 'First', value: 500}, + second: {name: 'Second', value: 600}, + third: {name: 'Third', value: 700}, + fourth: {name: 'Fourth', value: 800, extraValue: true}, + }; - const originalObjectValue = JSON.parse(JSON.stringify(object)); + const originalObjectValue = JSON.parse(JSON.stringify(object)); - console.table(object); + console.table(object); - expect(object).toEqual(originalObjectValue); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + expect(object).toEqual(originalObjectValue); }); it('should only print the selected columns, if specified (arrays)', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); - - try { - console.table( - [ - {first: 1, second: 2, third: 3}, - {first: 4, second: 5}, - {third: 7, fourth: 8}, - {fifth: 9}, - ], - // $FlowExpectedError[extra-arg] - ['first', 'fifth'], - ); - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([ - ` + console.table( + [ + {first: 1, second: 2, third: 3}, + {first: 4, second: 5}, + {third: 7, fourth: 8}, + {fifth: 9}, + ], + // $FlowExpectedError[extra-arg] + ['first', 'fifth'], + ); + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` | (index) | first | fifth | | ------- | ----- | ----- | | 0 | 1 | | | 1 | 4 | | | 2 | | | | 3 | | 9 |`, - LOG_LEVELS.info, - ]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + LOG_LEVELS.info, + ]); }); it('should only print the selected columns, if specified (dictionaries)', () => { - const originalNativeLoggingHook = global.nativeLoggingHook; - const logFn = (global.nativeLoggingHook = jest.fn()); - - try { - console.table( - { - a: {first: 1, second: 2, third: 3}, - b: {first: 4, second: 5}, - c: {third: 7, fourth: 8}, - d: {fifth: 9}, - }, - // $FlowExpectedError[extra-arg] - ['first', 'fifth'], - ); - expect(logFn).toHaveBeenCalledTimes(1); - expect(logFn.mock.lastCall).toEqual([ - ` + console.table( + { + a: {first: 1, second: 2, third: 3}, + b: {first: 4, second: 5}, + c: {third: 7, fourth: 8}, + d: {fifth: 9}, + }, + // $FlowExpectedError[extra-arg] + ['first', 'fifth'], + ); + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` | (index) | first | fifth | | ------- | ----- | ----- | | a | 1 | | | b | 4 | | | c | | | | d | | 9 |`, - LOG_LEVELS.info, - ]); - } finally { - global.nativeLoggingHook = originalNativeLoggingHook; - } + LOG_LEVELS.info, + ]); }); }); }); diff --git a/packages/react-native-fantom/runtime/setup.js b/packages/react-native-fantom/runtime/setup.js index 740f6e20288e..c19e4ee234da 100644 --- a/packages/react-native-fantom/runtime/setup.js +++ b/packages/react-native-fantom/runtime/setup.js @@ -14,7 +14,6 @@ import type {SnapshotConfig, TestSnapshotResults} from './snapshotContext'; import expect from './expect'; import {createMockFunction} from './mocks'; import {setupSnapshotConfig, snapshotContext} from './snapshotContext'; -import nullthrows from 'nullthrows'; import NativeFantom from 'react-native/src/private/specs/modules/NativeFantom'; export type TestCaseResult = { @@ -40,16 +39,43 @@ export type TestSuiteResult = }, }; -const tests: Array<{ +type FocusState = { + focused: boolean, + skipped: boolean, +}; + +type Spec = { + ...FocusState, title: string, - ancestorTitles: Array, + parentContext: Context, implementation: () => mixed, - isFocused: boolean, - isSkipped: boolean, - result?: TestCaseResult, -}> = []; +}; + +type Suite = Spec | Context; -const ancestorTitles: Array = []; +type Hook = () => void; + +type Context = { + ...FocusState, + title?: string, + afterAllHooks: Hook[], + afterEachHooks: Hook[], + beforeAllHooks: Hook[], + beforeEachHooks: Hook[], + parentContext?: Context, + children: Array, +}; + +const rootContext: Context = { + beforeAllHooks: [], + beforeEachHooks: [], + afterAllHooks: [], + afterEachHooks: [], + children: [], + focused: false, + skipped: false, +}; +let currentContext: Context = rootContext; const globalModifiers: Array<'focused' | 'skipped'> = []; @@ -57,26 +83,64 @@ const globalDescribe = (global.describe = ( title: string, implementation: () => mixed, ) => { - ancestorTitles.push(title); + const parentContext = currentContext; + const {focused, skipped} = getFocusState(); + const childContext: Context = { + title, + parentContext, + afterAllHooks: [], + afterEachHooks: [], + beforeAllHooks: [], + beforeEachHooks: [], + children: [], + focused: focused, + skipped: skipped, + }; + currentContext.children.push(childContext); + currentContext = childContext; implementation(); - ancestorTitles.pop(); + currentContext = parentContext; }); +global.afterAll = (implementation: () => void) => { + currentContext.afterAllHooks.push(implementation); +}; + +global.afterEach = (implementation: () => void) => { + currentContext.afterEachHooks.push(implementation); +}; + +global.beforeAll = (implementation: () => void) => { + currentContext.beforeAllHooks.push(implementation); +}; + +global.beforeEach = (implementation: () => void) => { + currentContext.beforeEachHooks.push(implementation); +}; + +function getFocusState(): {focused: boolean, skipped: boolean} { + const focused = + globalModifiers.length > 0 && + globalModifiers[globalModifiers.length - 1] === 'focused'; + const skipped = + globalModifiers.length > 0 && + globalModifiers[globalModifiers.length - 1] === 'skipped'; + return {focused, skipped}; +} + const globalIt = (global.it = global.test = - (title: string, implementation: () => mixed) => - tests.push({ + (title: string, implementation: () => mixed) => { + const {focused, skipped} = getFocusState(); + currentContext.children.push({ title, + parentContext: currentContext, implementation, - ancestorTitles: ancestorTitles.slice(), - isFocused: - globalModifiers.length > 0 && - globalModifiers[globalModifiers.length - 1] === 'focused', - isSkipped: - globalModifiers.length > 0 && - globalModifiers[globalModifiers.length - 1] === 'skipped', - })); + focused: focused, + skipped: skipped, + }); + }); // $FlowExpectedError[prop-missing] global.fdescribe = global.describe.only = ( @@ -142,52 +206,177 @@ function runWithGuard(fn: () => void) { } } -function executeTests() { - const hasFocusedTests = tests.some(test => test.isFocused); - - for (const test of tests) { - const result: TestCaseResult = { - title: test.title, - fullName: [...test.ancestorTitles, test.title].join(' '), - ancestorTitles: test.ancestorTitles, - status: 'pending', - duration: 0, - failureMessages: [], - numPassingAsserts: 0, - snapshotResults: {}, - }; +const focusCache = new Map(); + +function isFocusedSuite(suite: Suite): boolean { + const cached = focusCache.get(suite); + if (cached != null) { + return cached; + } + + if (isSkipped(suite)) { + focusCache.set(suite, false); + return false; + } + + if ('children' in suite) { + const hasFocused = suite.children.some(isFocusedSuite); + focusCache.set(suite, hasFocused); + return hasFocused; + } + + focusCache.set(suite, suite.focused); + return suite.focused; +} - test.result = result; - snapshotContext.setTargetTest(result.fullName); +const skippedCache = new Map(); +function isSkipped(suite: Suite): boolean { + const cached = skippedCache.get(suite); + if (cached != null) { + return cached; + } - if (!test.isSkipped && (!hasFocusedTests || test.isFocused)) { - let status; - let error; + if (suite.skipped) { + skippedCache.set(suite, true); + return true; + } - const start = Date.now(); + if (suite.parentContext != null) { + const skipped = isSkipped(suite.parentContext); + skippedCache.set(suite, skipped); + return skipped; + } + + skippedCache.set(suite, false); + return false; +} - try { - test.implementation(); - status = 'passed'; - } catch (e) { - error = e; - status = 'failed'; - } +function getContextTitle(context: Context): string[] { + if (context.parentContext == null) { + return []; + } - result.status = status; - result.duration = Date.now() - start; - result.failureMessages = - status === 'failed' && error - ? [error.stack ?? error.message ?? String(error)] - : []; + const titles = context.title != null ? [context.title] : []; + if (context.parentContext) { + titles.push(...getContextTitle(context.parentContext)); + } + return titles.reverse(); +} - result.snapshotResults = snapshotContext.getSnapshotResults(); +function invokeHooks( + context: Context, + hookType: 'beforeEachHooks' | 'afterEachHooks', +) { + const contextStack = []; + let current: ?Context = context; + while (current != null) { + if (hookType === 'beforeEachHooks') { + contextStack.unshift(current); + } else { + contextStack.push(current); } + current = current.parentContext; } - reportTestSuiteResult({ - testResults: tests.map(test => nullthrows(test.result)), - }); + for (const c of contextStack) { + for (const hook of c[hookType]) { + hook(); + } + } +} + +function shouldRunSuite(suite: Suite): boolean { + if (isSkipped(suite)) { + return false; + } + + if (isFocusedSuite(suite)) { + return true; + } + + // there is a focused suite in the root at some point + // but not in this suite hence we should not run it + if (isFocusedSuite(rootContext)) { + return false; + } + + return true; +} + +function runSpec(spec: Spec): TestCaseResult { + const ancestorTitles = getContextTitle(spec.parentContext); + const result: TestCaseResult = { + title: spec.title, + ancestorTitles, + fullName: [...ancestorTitles, spec.title].join(' '), + status: 'pending', + duration: 0, + failureMessages: [], + numPassingAsserts: 0, + snapshotResults: {}, + }; + + if (!shouldRunSuite(spec)) { + return result; + } + + let status; + let error; + + const start = Date.now(); + snapshotContext.setTargetTest(result.fullName); + + try { + invokeHooks(spec.parentContext, 'beforeEachHooks'); + spec.implementation(); + invokeHooks(spec.parentContext, 'afterEachHooks'); + + status = 'passed'; + } catch (e) { + error = e; + status = 'failed'; + } + + result.status = status; + result.duration = Date.now() - start; + result.failureMessages = + status === 'failed' && error + ? [error.stack ?? error.message ?? String(error)] + : []; + + result.snapshotResults = snapshotContext.getSnapshotResults(); + return result; +} + +function runContext(context: Context): TestCaseResult[] { + const shouldRunHooks = shouldRunSuite(context); + + if (shouldRunHooks) { + for (const beforeAllHook of context.beforeAllHooks) { + beforeAllHook(); + } + } + + const testResults: TestCaseResult[] = []; + for (const child of context.children) { + testResults.push(...runSuite(child)); + } + + if (shouldRunHooks) { + for (const afterAllHook of context.afterAllHooks) { + afterAllHook(); + } + } + + return testResults; +} + +function runSuite(suite: Suite): TestCaseResult[] { + if ('children' in suite) { + return runContext(suite); + } else { + return [runSpec(suite)]; + } } function reportTestSuiteResult(testSuiteResult: TestSuiteResult): void { @@ -200,7 +389,9 @@ function reportTestSuiteResult(testSuiteResult: TestSuiteResult): void { } global.$$RunTests$$ = () => { - executeTests(); + reportTestSuiteResult({ + testResults: runSuite(currentContext), + }); }; export function registerTest( diff --git a/packages/react-native-fantom/src/__tests__/setup/focused-hooks-itest.js b/packages/react-native-fantom/src/__tests__/setup/focused-hooks-itest.js new file mode 100644 index 000000000000..6e097bdf793c --- /dev/null +++ b/packages/react-native-fantom/src/__tests__/setup/focused-hooks-itest.js @@ -0,0 +1,83 @@ +/** + * 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 + */ + +const logs: string[] = []; + +afterAll(() => { + expect(logs).toEqual([ + '1 - beforeAll', + '1.1 - beforeAll', + '1.1 - test 1', + '1.1 - afterAll', + '1 - test 2', + '1 - afterAll', + ]); +}); + +describe('1', () => { + beforeAll(() => { + logs.push('1 - beforeAll'); + }); + + afterAll(() => { + logs.push('1 - afterAll'); + }); + + describe('1.1', () => { + beforeAll(() => { + // this is what we want to test + logs.push('1.1 - beforeAll'); + }); + + afterAll(() => { + // this is what we want to test + logs.push('1.1 - afterAll'); + }); + + // this is part of the test suite + // eslint-disable-next-line jest/no-focused-tests + it.only('1.1 - test 1', () => { + logs.push('1.1 - test 1'); + }); + + it('1.1 - test 2', () => { + logs.push('1.1 - test 2'); + }); + }); + + // this is part of the test suite + // eslint-disable-next-line jest/no-disabled-tests + describe.skip('1.2', () => { + beforeAll(() => { + logs.push('1.2 - beforeAll'); + }); + + // this is part of the test suite + // eslint-disable-next-line jest/no-focused-tests + it.only('1.2 - test 1', () => { + logs.push('1.2 - test 1'); + }); + + it('1.2 - test 2', () => { + logs.push('1.2 - test 2'); + }); + }); + + it('1 - test 1', () => { + logs.push('1 - test 1'); + }); + + // this is part of the test suite + // eslint-disable-next-line jest/no-focused-tests + it.only('1 - test 2', () => { + logs.push('1 - test 2'); + }); +}); diff --git a/packages/react-native-fantom/src/__tests__/setup/order-of-execution-itest.js b/packages/react-native-fantom/src/__tests__/setup/order-of-execution-itest.js new file mode 100644 index 000000000000..5e097eb85f61 --- /dev/null +++ b/packages/react-native-fantom/src/__tests__/setup/order-of-execution-itest.js @@ -0,0 +1,51 @@ +/** + * 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 + */ + +const logs: string[] = []; +const log = (message: string): void => { + logs.push(message); +}; + +afterAll(() => { + // Source https://jestjs.io/docs/setup-teardown#order-of-execution + expect(logs).toEqual([ + 'describe outer-a', + 'describe inner 1', + 'describe outer-b', + 'describe inner 2', + 'describe outer-c', + 'test 1', + 'test 2', + 'test 3', + ]); +}); + +describe('describe outer', () => { + log('describe outer-a'); + + describe('describe inner 1', () => { + log('describe inner 1'); + + test('test 1', () => log('test 1')); + }); + + log('describe outer-b'); + + test('test 2', () => log('test 2')); + + describe('describe inner 2', () => { + log('describe inner 2'); + + test('test 3', () => log('test 3')); + }); + + log('describe outer-c'); +}); diff --git a/packages/react-native-fantom/src/__tests__/setup/order-of-hooks-executions-itest.js b/packages/react-native-fantom/src/__tests__/setup/order-of-hooks-executions-itest.js new file mode 100644 index 000000000000..5ad0a9e8eb8b --- /dev/null +++ b/packages/react-native-fantom/src/__tests__/setup/order-of-hooks-executions-itest.js @@ -0,0 +1,48 @@ +/** + * 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 + */ + +const logs: string[] = []; +const log = (message: string): void => { + logs.push(message); +}; + +beforeEach(() => log('connection setup')); +beforeEach(() => log('database setup')); + +afterEach(() => log('database teardown')); +afterEach(() => log('connection teardown')); +afterAll(() => { + // Source https://jestjs.io/docs/setup-teardown#order-of-execution + expect(logs).toEqual([ + 'connection setup', + 'database setup', + 'test 1', + 'database teardown', + 'connection teardown', + + 'connection setup', + 'database setup', + 'extra database setup', + 'test 2', + 'extra database teardown', + 'database teardown', + 'connection teardown', + ]); +}); + +test('test 1', () => log('test 1')); + +describe('extra', () => { + beforeEach(() => log('extra database setup')); + afterEach(() => log('extra database teardown')); + + test('test 2', () => log('test 2')); +}); diff --git a/packages/react-native-fantom/src/__tests__/setup/scoping-itest.js b/packages/react-native-fantom/src/__tests__/setup/scoping-itest.js new file mode 100644 index 000000000000..5b9f723fa987 --- /dev/null +++ b/packages/react-native-fantom/src/__tests__/setup/scoping-itest.js @@ -0,0 +1,48 @@ +/** + * 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 + */ + +const logs: string[] = []; +const log = (message: string): void => { + logs.push(message); +}; + +beforeAll(() => log('1 - beforeAll')); +afterAll(() => { + log('1 - afterAll'); + // Source https://jestjs.io/docs/setup-teardown#scoping + expect(logs).toEqual([ + '1 - beforeAll', + '1 - beforeEach', + '1 - test', + '1 - afterEach', + '2 - beforeAll', + '1 - beforeEach', + '2 - beforeEach', + '2 - test', + '2 - afterEach', + '1 - afterEach', + '2 - afterAll', + '1 - afterAll', + ]); +}); +beforeEach(() => log('1 - beforeEach')); +afterEach(() => log('1 - afterEach')); + +test('root', () => log('1 - test')); + +describe('Scoped / Nested block', () => { + beforeAll(() => log('2 - beforeAll')); + afterAll(() => log('2 - afterAll')); + beforeEach(() => log('2 - beforeEach')); + afterEach(() => log('2 - afterEach')); + + test('inner', () => log('2 - test')); +});