diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..3eb29c53
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,8 @@
+{
+ "explorer.fileNesting.enabled": true,
+ "explorer.fileNesting.patterns": {
+ "*.ts": "${capture}.test.ts, ${capture}.test.ts.snap",
+ "*.tsx": "${capture}.test.tsx, ${capture}.test.tsx.snap",
+ ".eslintrc.js": "*.eslintrc.js"
+ }
+}
diff --git a/package.json b/package.json
index e273b40c..103e05c4 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"@babel/preset-react": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@react-native-community/eslint-config": "^3.2.0",
+ "@testing-library/react-native": "^12.4.3",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@typescript-eslint/eslint-plugin": "^5.60.1",
diff --git a/packages/lib/jest.config.js b/packages/lib/jest.config.js
index eff32b94..906061c2 100644
--- a/packages/lib/jest.config.js
+++ b/packages/lib/jest.config.js
@@ -7,6 +7,10 @@
*
* If encountering a syntax error during tests with a new package, add it to this list
*/
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const path = require('path');
+
const packagesToTransform = [
'react-native',
'react-native-(.*)',
@@ -30,7 +34,10 @@ const config = {
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
testRegex: '\\.test\\.[jt]sx?$',
transform: {
- '\\.[jt]sx?$': ['babel-jest', { configFile: './babel.jest.config.js' }],
+ '\\.[jt]sx?$': [
+ 'babel-jest',
+ { configFile: path.resolve(__dirname, './babel.jest.config.js') },
+ ],
},
transformIgnorePatterns: [`node_modules/(?!(${packagesToTransform.join('|')})/)`],
cacheDirectory: '.cache/jest',
@@ -40,6 +47,7 @@ const config = {
// tools
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
reporters: ['default', 'github-actions'], // Remove this line if your CI is not on Github actions
+ snapshotResolver: './jestSnapshotResolver.js',
};
module.exports = config;
diff --git a/packages/lib/jestSnapshotResolver.js b/packages/lib/jestSnapshotResolver.js
new file mode 100644
index 00000000..e8e19480
--- /dev/null
+++ b/packages/lib/jestSnapshotResolver.js
@@ -0,0 +1,11 @@
+module.exports = {
+ testPathForConsistencyCheck: 'some/__tests__/example.test.js',
+ /** resolves from test to snapshot path */
+ resolveSnapshotPath: (testPath, snapshotExtension) => {
+ return testPath + snapshotExtension;
+ },
+ /** resolves from snapshot to test path */
+ resolveTestPath: (snapshotFilePath, snapshotExtension) => {
+ return snapshotFilePath.slice(0, -snapshotExtension.length);
+ },
+};
diff --git a/packages/lib/package.json b/packages/lib/package.json
index d645307b..de136a04 100644
--- a/packages/lib/package.json
+++ b/packages/lib/package.json
@@ -8,7 +8,6 @@
},
"license": "MIT",
"devDependencies": {
- "@testing-library/jest-native": "^5.4.3",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/react-native": "^12.3.1",
"@types/jest": "^29.5.3",
diff --git a/packages/lib/src/spatial-navigation/components/tests/SpatialNavigation.test.tsx b/packages/lib/src/spatial-navigation/components/tests/SpatialNavigation.test.tsx
index 56b248ba..213edf0b 100644
--- a/packages/lib/src/spatial-navigation/components/tests/SpatialNavigation.test.tsx
+++ b/packages/lib/src/spatial-navigation/components/tests/SpatialNavigation.test.tsx
@@ -30,7 +30,7 @@ const TestScreen = ({ onDirectionHandledWithoutMovement = () => undefined }) =>
export const expectButtonToHaveFocus = (component: RenderResult, text: string) => {
const element = component.getByRole('button', { name: text });
- expect(element).toHaveAccessibilityState({ selected: true });
+ expect(element).toBeSelected();
};
describe('Spatial Navigation Movement', () => {
diff --git a/packages/lib/src/spatial-navigation/components/tests/TestButton.tsx b/packages/lib/src/spatial-navigation/components/tests/TestButton.tsx
index 97a1d3d6..88568fd8 100644
--- a/packages/lib/src/spatial-navigation/components/tests/TestButton.tsx
+++ b/packages/lib/src/spatial-navigation/components/tests/TestButton.tsx
@@ -2,7 +2,7 @@ import styled from '@emotion/native';
import { Text } from 'react-native';
import { SpatialNavigationNode } from '../Node';
-type PropsTestButton = {
+export type PropsTestButton = {
onSelect: () => void;
title: string;
};
diff --git a/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.test.tsx b/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.test.tsx
new file mode 100644
index 00000000..2745ea57
--- /dev/null
+++ b/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.test.tsx
@@ -0,0 +1,152 @@
+import { RenderResult, act, render, screen } from '@testing-library/react-native';
+import { ItemWithIndex } from '../virtualizedList/VirtualizedList';
+import { PropsTestButton, TestButton } from '../tests/TestButton';
+import { SpatialNavigationRoot } from '../Root';
+import '../tests/helpers/configureTestRemoteControl';
+
+import { DefaultFocus } from '../../context/DefaultFocusContext';
+import { SpatialNavigationVirtualizedGrid } from '../virtualizedGrid/SpatialNavigationVirtualizedGrid';
+import testRemoteControlManager from '../tests/helpers/testRemoteControlManager';
+
+export const expectButtonToHaveFocus = (component: RenderResult, text: string) => {
+ const element = component.getByRole('button', { name: text });
+ expect(element).toBeSelected();
+};
+
+describe('SpatialNavigationVirtualizedGrid', () => {
+ const renderItem = ({ item }: { item: PropsTestButton & ItemWithIndex }) => (
+
+ );
+
+ function createDataArray(numberOfItems: number) {
+ const data = [];
+ for (let i = 0; i < numberOfItems; i++) {
+ data.push({
+ title: `button ${i + 1}`,
+ onSelect: () => undefined,
+ index: i,
+ });
+ }
+ return data;
+ }
+
+ const renderGrid = () =>
+ render(
+
+
+
+
+ ,
+ );
+
+ it('renders the correct number of item', () => {
+ const component = renderGrid();
+ act(() => jest.runAllTimers());
+
+ expect(screen).toMatchSnapshot();
+
+ expect(screen.getByText('button 1')).toBeTruthy();
+ expectButtonToHaveFocus(component, 'button 1');
+ expect(screen.getByText('button 2')).toBeTruthy();
+ expect(screen.getByText('button 3')).toBeTruthy();
+ expect(screen.getByText('button 4')).toBeTruthy();
+ expect(screen.getByText('button 5')).toBeTruthy();
+ expect(screen.getByText('button 6')).toBeTruthy();
+ expect(screen.getByText('button 7')).toBeTruthy();
+ expect(screen.getByText('button 8')).toBeTruthy();
+ expect(screen.getByText('button 9')).toBeTruthy();
+ expect(screen.getByText('button 10')).toBeTruthy();
+ expect(screen.getByText('button 11')).toBeTruthy();
+ expect(screen.getByText('button 12')).toBeTruthy();
+ expect(screen.getByText('button 13')).toBeTruthy();
+ expect(screen.getByText('button 14')).toBeTruthy();
+ expect(screen.getByText('button 15')).toBeTruthy();
+ expect(screen.queryByText('button 16')).toBeFalsy();
+ });
+
+ it('renders the correct number of item', () => {
+ const component = renderGrid();
+ act(() => jest.runAllTimers());
+
+ const listElement = component.getByTestId('test-grid');
+ expect(listElement).toHaveStyle({ transform: [{ translateY: 0 }] });
+
+ testRemoteControlManager.handleRight();
+ act(() => jest.runAllTimers());
+
+ expect(screen.getByText('button 1')).toBeTruthy();
+ expect(screen.getByText('button 2')).toBeTruthy();
+ expectButtonToHaveFocus(component, 'button 2');
+ expect(screen.getByText('button 3')).toBeTruthy();
+ expect(screen.getByText('button 4')).toBeTruthy();
+ expect(screen.getByText('button 5')).toBeTruthy();
+ expect(screen.getByText('button 6')).toBeTruthy();
+ expect(screen.getByText('button 7')).toBeTruthy();
+ expect(screen.getByText('button 8')).toBeTruthy();
+ expect(screen.getByText('button 9')).toBeTruthy();
+ expect(screen.getByText('button 10')).toBeTruthy();
+ expect(screen.getByText('button 11')).toBeTruthy();
+ expect(screen.getByText('button 12')).toBeTruthy();
+ expect(screen.getByText('button 13')).toBeTruthy();
+ expect(screen.getByText('button 14')).toBeTruthy();
+ expect(screen.getByText('button 15')).toBeTruthy();
+ expect(screen.queryByText('button 16')).toBeFalsy();
+
+ testRemoteControlManager.handleDown();
+ act(() => jest.runAllTimers());
+ expect(listElement).toHaveStyle({ transform: [{ translateY: -100 }] });
+
+ expect(screen.getByText('button 1')).toBeTruthy();
+ expect(screen.getByText('button 2')).toBeTruthy();
+ expect(screen.getByText('button 3')).toBeTruthy();
+ expect(screen.getByText('button 4')).toBeTruthy();
+ expect(screen.getByText('button 5')).toBeTruthy();
+ expectButtonToHaveFocus(component, 'button 5');
+ expect(screen.getByText('button 6')).toBeTruthy();
+ expect(screen.getByText('button 7')).toBeTruthy();
+ expect(screen.getByText('button 8')).toBeTruthy();
+ expect(screen.getByText('button 9')).toBeTruthy();
+ expect(screen.getByText('button 10')).toBeTruthy();
+ expect(screen.getByText('button 11')).toBeTruthy();
+ expect(screen.getByText('button 12')).toBeTruthy();
+ expect(screen.getByText('button 13')).toBeTruthy();
+ expect(screen.getByText('button 14')).toBeTruthy();
+ expect(screen.getByText('button 15')).toBeTruthy();
+ expect(screen.queryByText('button 16')).toBeFalsy();
+
+ testRemoteControlManager.handleDown();
+ act(() => jest.runAllTimers());
+ expect(listElement).toHaveStyle({ transform: [{ translateY: -200 }] });
+
+ expect(screen.queryByText('button 1')).toBeFalsy();
+ expect(screen.queryByText('button 2')).toBeFalsy();
+ expect(screen.queryByText('button 3')).toBeFalsy();
+ expect(screen.getByText('button 4')).toBeTruthy();
+ expect(screen.getByText('button 5')).toBeTruthy();
+ expect(screen.getByText('button 6')).toBeTruthy();
+ expect(screen.getByText('button 7')).toBeTruthy();
+ expect(screen.getByText('button 8')).toBeTruthy();
+ expectButtonToHaveFocus(component, 'button 8');
+ expect(screen.getByText('button 9')).toBeTruthy();
+ expect(screen.getByText('button 10')).toBeTruthy();
+ expect(screen.getByText('button 11')).toBeTruthy();
+ expect(screen.getByText('button 12')).toBeTruthy();
+ expect(screen.getByText('button 13')).toBeTruthy();
+ expect(screen.getByText('button 14')).toBeTruthy();
+ expect(screen.getByText('button 15')).toBeTruthy();
+ expect(screen.getByText('button 16')).toBeTruthy();
+ expect(screen.getByText('button 17')).toBeTruthy();
+ expect(screen.getByText('button 18')).toBeTruthy();
+ expect(screen.queryByText('button 19')).toBeFalsy();
+
+ expect(screen).toMatchSnapshot();
+ });
+});
diff --git a/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.test.tsx.snap b/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.test.tsx.snap
new file mode 100644
index 00000000..72db9f9e
--- /dev/null
+++ b/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.test.tsx.snap
@@ -0,0 +1,1135 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SpatialNavigationVirtualizedGrid renders the correct number of item 1`] = `
+
+
+
+
+
+
+
+ button 1
+
+
+
+
+
+
+ button 2
+
+
+
+
+
+
+ button 3
+
+
+
+
+
+
+
+
+
+
+ button 4
+
+
+
+
+
+
+ button 5
+
+
+
+
+
+
+ button 6
+
+
+
+
+
+
+
+
+
+
+ button 7
+
+
+
+
+
+
+ button 8
+
+
+
+
+
+
+ button 9
+
+
+
+
+
+
+
+
+
+
+ button 10
+
+
+
+
+
+
+ button 11
+
+
+
+
+
+
+ button 12
+
+
+
+
+
+
+
+
+
+
+ button 13
+
+
+
+
+
+
+ button 14
+
+
+
+
+
+
+ button 15
+
+
+
+
+
+
+
+`;
+
+exports[`SpatialNavigationVirtualizedGrid renders the correct number of item 2`] = `
+
+
+
+
+
+
+
+ button 4
+
+
+
+
+
+
+ button 5
+
+
+
+
+
+
+ button 6
+
+
+
+
+
+
+
+
+
+
+ button 7
+
+
+
+
+
+
+ button 8
+
+
+
+
+
+
+ button 9
+
+
+
+
+
+
+
+
+
+
+ button 10
+
+
+
+
+
+
+ button 11
+
+
+
+
+
+
+ button 12
+
+
+
+
+
+
+
+
+
+
+ button 13
+
+
+
+
+
+
+ button 14
+
+
+
+
+
+
+ button 15
+
+
+
+
+
+
+
+
+
+
+ button 16
+
+
+
+
+
+
+ button 17
+
+
+
+
+
+
+ button 18
+
+
+
+
+
+
+
+`;
diff --git a/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.tsx b/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.tsx
index ef4cac94..8dd579ed 100644
--- a/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.tsx
+++ b/packages/lib/src/spatial-navigation/components/virtualizedGrid/SpatialNavigationVirtualizedGrid.tsx
@@ -20,6 +20,7 @@ type SpatialNavigationVirtualizedGridProps = Pick<
| 'nbMaxOfItems'
| 'scrollBehavior'
| 'scrollDuration'
+ | 'testID'
> & {
itemHeight: number;
/** How many rows are RENDERED (virtualization size) */
diff --git a/packages/lib/src/spatial-navigation/components/virtualizedList/SpatialNavigationVirtualizedList.test.tsx b/packages/lib/src/spatial-navigation/components/virtualizedList/SpatialNavigationVirtualizedList.test.tsx
new file mode 100644
index 00000000..d14c5491
--- /dev/null
+++ b/packages/lib/src/spatial-navigation/components/virtualizedList/SpatialNavigationVirtualizedList.test.tsx
@@ -0,0 +1,112 @@
+import { RenderResult, act, render, screen } from '@testing-library/react-native';
+import { ItemWithIndex } from '../virtualizedList/VirtualizedList';
+import { PropsTestButton, TestButton } from '../tests/TestButton';
+import { SpatialNavigationRoot } from '../Root';
+import '../tests/helpers/configureTestRemoteControl';
+import { SpatialNavigationVirtualizedList } from './SpatialNavigationVirtualizedList';
+import { DefaultFocus } from '../../context/DefaultFocusContext';
+import testRemoteControlManager from '../tests/helpers/testRemoteControlManager';
+
+export const expectButtonToHaveFocus = (component: RenderResult, text: string) => {
+ const element = component.getByRole('button', { name: text });
+ expect(element).toBeSelected();
+};
+
+describe('SpatialNavigationVirtualizedList', () => {
+ const renderItem = ({ item }: { item: PropsTestButton & ItemWithIndex }) => (
+
+ );
+
+ const data = [
+ { title: 'button 1', onSelect: () => undefined, index: 0 },
+ { title: 'button 2', onSelect: () => undefined, index: 1 },
+ { title: 'button 3', onSelect: () => undefined, index: 2 },
+ { title: 'button 4', onSelect: () => undefined, index: 3 },
+ { title: 'button 5', onSelect: () => undefined, index: 4 },
+ { title: 'button 6', onSelect: () => undefined, index: 5 },
+ { title: 'button 7', onSelect: () => undefined, index: 6 },
+ { title: 'button 8', onSelect: () => undefined, index: 7 },
+ { title: 'button 9', onSelect: () => undefined, index: 8 },
+ { title: 'button 10', onSelect: () => undefined, index: 9 },
+ ];
+
+ const renderList = () =>
+ render(
+
+
+
+
+ ,
+ );
+
+ it('renders the correct number of item', () => {
+ const component = renderList();
+ act(() => jest.runAllTimers());
+
+ expect(screen).toMatchSnapshot();
+
+ const button1 = screen.getByText('button 1');
+ expect(button1).toBeTruthy();
+ expectButtonToHaveFocus(component, 'button 1');
+
+ const button2 = screen.getByText('button 2');
+ expect(button2).toBeTruthy();
+
+ const button3 = screen.getByText('button 3');
+ expect(button3).toBeTruthy();
+
+ const button4 = screen.getByText('button 4');
+ expect(button4).toBeTruthy();
+
+ const button5 = screen.getByText('button 5');
+ expect(button5).toBeTruthy();
+
+ const button6 = screen.queryByText('button 6');
+ expect(button6).toBeFalsy();
+ });
+
+ it('handles correctly RIGHT and RENDERS new elements accordingly while deleting elements that are too far from scroll', async () => {
+ const component = renderList();
+ act(() => jest.runAllTimers());
+
+ const listElement = component.getByTestId('test-list');
+ expect(listElement).toHaveStyle({ transform: [{ translateX: 0 }] });
+
+ testRemoteControlManager.handleRight();
+ act(() => jest.runAllTimers());
+ expectButtonToHaveFocus(component, 'button 2');
+ expect(listElement).toHaveStyle({ transform: [{ translateX: -100 }] });
+
+ expect(screen.getByText('button 1')).toBeTruthy();
+ expect(screen.getByText('button 5')).toBeTruthy();
+ expect(screen.queryByText('button 6')).toBeFalsy();
+
+ testRemoteControlManager.handleRight();
+ act(() => jest.runAllTimers());
+ expectButtonToHaveFocus(component, 'button 3');
+ expect(listElement).toHaveStyle({ transform: [{ translateX: -200 }] });
+
+ expect(screen.queryByText('button 1')).toBeFalsy();
+ expect(screen.getByText('button 2')).toBeTruthy();
+ expect(screen.getByText('button 6')).toBeTruthy();
+ expect(screen.queryByText('button 7')).toBeFalsy();
+
+ testRemoteControlManager.handleRight();
+ act(() => jest.runAllTimers());
+ expectButtonToHaveFocus(component, 'button 4');
+ expect(listElement).toHaveStyle({ transform: [{ translateX: -300 }] });
+
+ expect(screen.queryByText('button 2')).toBeFalsy();
+ expect(screen.getByText('button 3')).toBeTruthy();
+ expect(screen.getByText('button 7')).toBeTruthy();
+ expect(screen.queryByText('button 8')).toBeFalsy();
+ });
+});
diff --git a/packages/lib/src/spatial-navigation/components/virtualizedList/SpatialNavigationVirtualizedList.test.tsx.snap b/packages/lib/src/spatial-navigation/components/virtualizedList/SpatialNavigationVirtualizedList.test.tsx.snap
new file mode 100644
index 00000000..6dcd7072
--- /dev/null
+++ b/packages/lib/src/spatial-navigation/components/virtualizedList/SpatialNavigationVirtualizedList.test.tsx.snap
@@ -0,0 +1,223 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SpatialNavigationVirtualizedList renders the correct number of item 1`] = `
+
+
+
+
+
+ button 1
+
+
+
+
+
+
+ button 2
+
+
+
+
+
+
+ button 3
+
+
+
+
+
+
+ button 4
+
+
+
+
+
+
+ button 5
+
+
+
+
+
+`;
diff --git a/packages/lib/src/spatial-navigation/components/virtualizedList/VirtualizedList.tsx b/packages/lib/src/spatial-navigation/components/virtualizedList/VirtualizedList.tsx
index a10bd742..9b7e44f1 100644
--- a/packages/lib/src/spatial-navigation/components/virtualizedList/VirtualizedList.tsx
+++ b/packages/lib/src/spatial-navigation/components/virtualizedList/VirtualizedList.tsx
@@ -49,6 +49,7 @@ export interface VirtualizedListProps {
/** Custom width for the VirtualizedList container */
width?: number;
scrollBehavior?: ScrollBehavior;
+ testID?: string;
}
const useOnEndReached = ({
@@ -136,6 +137,7 @@ export const VirtualizedList = typedMemo(
scrollBehavior = 'stick-to-start',
height = screen.height,
width = screen.width,
+ testID,
}: VirtualizedListProps) => {
const range = getRange({
data,
@@ -226,6 +228,7 @@ export const VirtualizedList = typedMemo(
return (
{dataSliceToRender.map((item) => {
diff --git a/packages/lib/src/testing/constants.ts b/packages/lib/src/testing/constants.ts
new file mode 100644
index 00000000..f0a6301f
--- /dev/null
+++ b/packages/lib/src/testing/constants.ts
@@ -0,0 +1,11 @@
+/**
+ * UTC+1 in Winter (the default date is in Winter)
+ */
+export const TEST_DEFAULT_TZ = 'Europe/Paris';
+
+/**
+ * 15:23 UTC -> 16:23 In Paris
+ */
+export const TEST_DEFAULT_DATE = '2023-03-05T15:23:49.294Z';
+
+export const TEST_DEFAULT_MATH_RANDOM = 0.372294134538401;
diff --git a/packages/lib/src/testing/jest-setupAfterEnv.ts b/packages/lib/src/testing/jest-setupAfterEnv.ts
index 66c8b3c1..2239118a 100644
--- a/packages/lib/src/testing/jest-setupAfterEnv.ts
+++ b/packages/lib/src/testing/jest-setupAfterEnv.ts
@@ -1,3 +1,38 @@
-import '@testing-library/jest-native/extend-expect';
+import '@testing-library/react-native/extend-expect';
-export {};
+import { TEST_DEFAULT_DATE, TEST_DEFAULT_MATH_RANDOM } from './constants';
+
+/**
+ * Some globals have no reason to not ever be mocked if we want to have reproducible tests.
+ * Put those things here: Date, Math.random, etc.
+ *
+ * You can still customize the mock in an isolated way for a given test or test suite
+ * BEWARE that your customizations in tests and test suites don't apply to top-level module code (it runs before the `beforeEach`)
+ */
+const setupPermanentMocks = () => {
+ // Note: Timezone is set in src/testing/jest-globalSetup.ts (it wouldn't work to set it here)
+ jest.useFakeTimers({
+ // We're not really interested in stopping the microtasks queue, what we want to mock is "timers"
+ doNotFake: [
+ 'setImmediate', // see https://github.com/callstack/react-native-testing-library/issues/1347
+ 'clearImmediate',
+ 'nextTick',
+ 'queueMicrotask',
+ 'requestIdleCallback',
+ 'cancelIdleCallback',
+ 'requestAnimationFrame',
+ 'cancelAnimationFrame',
+ 'hrtime',
+ 'performance',
+ ],
+ now: new Date(TEST_DEFAULT_DATE), // To customize in a test, use `jest.setSystemTime`
+ });
+
+ Math.random = () => TEST_DEFAULT_MATH_RANDOM; // To customize in a given test, use `jest.spyOn(Math, "random").mockReturnValue(xx)`
+};
+
+// Some code runs before `beforeEach` (top-level code from imported modules), so this line is needed
+setupPermanentMocks();
+
+// And then this one is needed to re-set the mocks after the automatic `clearMocks`
+beforeEach(setupPermanentMocks);
diff --git a/yarn.lock b/yarn.lock
index 25a83765..e2a4d7d9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4835,23 +4835,6 @@ __metadata:
languageName: node
linkType: hard
-"@testing-library/jest-native@npm:^5.4.3":
- version: 5.4.3
- resolution: "@testing-library/jest-native@npm:5.4.3"
- dependencies:
- chalk: ^4.1.2
- jest-diff: ^29.0.1
- jest-matcher-utils: ^29.0.1
- pretty-format: ^29.0.3
- redent: ^3.0.0
- peerDependencies:
- react: ">=16.0.0"
- react-native: ">=0.59"
- react-test-renderer: ">=16.0.0"
- checksum: 2a4ebfeff09523860771cfddac6fcc3faa2f855dc63255b9efc016e727132320f16f935cec9717d6d79cfa6715fce6ded877215c8ec85d236a5c3136a65b1020
- languageName: node
- linkType: hard
-
"@testing-library/react-hooks@npm:^8.0.1":
version: 8.0.1
resolution: "@testing-library/react-hooks@npm:8.0.1"
@@ -4893,6 +4876,25 @@ __metadata:
languageName: node
linkType: hard
+"@testing-library/react-native@npm:^12.4.3":
+ version: 12.4.3
+ resolution: "@testing-library/react-native@npm:12.4.3"
+ dependencies:
+ jest-matcher-utils: ^29.7.0
+ pretty-format: ^29.7.0
+ redent: ^3.0.0
+ peerDependencies:
+ jest: ">=28.0.0"
+ react: ">=16.8.0"
+ react-native: ">=0.59"
+ react-test-renderer: ">=16.8.0"
+ peerDependenciesMeta:
+ jest:
+ optional: true
+ checksum: bd1c06b200063ffad26245cbc61b7256b5bfcc2c69a32f7e6dc9ff1bf44c3743117d5010b8dbc8f9a1b660c3ee22ccf9ecc320c03db52f9ec75ba7e52c1d8ba3
+ languageName: node
+ linkType: hard
+
"@tootallnate/once@npm:2":
version: 2.0.0
resolution: "@tootallnate/once@npm:2.0.0"
@@ -11149,18 +11151,6 @@ __metadata:
languageName: node
linkType: hard
-"jest-diff@npm:^29.0.1, jest-diff@npm:^29.7.0":
- version: 29.7.0
- resolution: "jest-diff@npm:29.7.0"
- dependencies:
- chalk: ^4.0.0
- diff-sequences: ^29.6.3
- jest-get-type: ^29.6.3
- pretty-format: ^29.7.0
- checksum: 08e24a9dd43bfba1ef07a6374e5af138f53137b79ec3d5cc71a2303515335898888fa5409959172e1e05de966c9e714368d15e8994b0af7441f0721ee8e1bb77
- languageName: node
- linkType: hard
-
"jest-diff@npm:^29.6.0":
version: 29.6.0
resolution: "jest-diff@npm:29.6.0"
@@ -11185,6 +11175,18 @@ __metadata:
languageName: node
linkType: hard
+"jest-diff@npm:^29.7.0":
+ version: 29.7.0
+ resolution: "jest-diff@npm:29.7.0"
+ dependencies:
+ chalk: ^4.0.0
+ diff-sequences: ^29.6.3
+ jest-get-type: ^29.6.3
+ pretty-format: ^29.7.0
+ checksum: 08e24a9dd43bfba1ef07a6374e5af138f53137b79ec3d5cc71a2303515335898888fa5409959172e1e05de966c9e714368d15e8994b0af7441f0721ee8e1bb77
+ languageName: node
+ linkType: hard
+
"jest-docblock@npm:^29.4.3":
version: 29.4.3
resolution: "jest-docblock@npm:29.4.3"
@@ -11303,18 +11305,6 @@ __metadata:
languageName: node
linkType: hard
-"jest-matcher-utils@npm:^29.0.1, jest-matcher-utils@npm:^29.7.0":
- version: 29.7.0
- resolution: "jest-matcher-utils@npm:29.7.0"
- dependencies:
- chalk: ^4.0.0
- jest-diff: ^29.7.0
- jest-get-type: ^29.6.3
- pretty-format: ^29.7.0
- checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd
- languageName: node
- linkType: hard
-
"jest-matcher-utils@npm:^29.6.0":
version: 29.6.0
resolution: "jest-matcher-utils@npm:29.6.0"
@@ -11339,6 +11329,18 @@ __metadata:
languageName: node
linkType: hard
+"jest-matcher-utils@npm:^29.7.0":
+ version: 29.7.0
+ resolution: "jest-matcher-utils@npm:29.7.0"
+ dependencies:
+ chalk: ^4.0.0
+ jest-diff: ^29.7.0
+ jest-get-type: ^29.6.3
+ pretty-format: ^29.7.0
+ checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd
+ languageName: node
+ linkType: hard
+
"jest-message-util@npm:^29.6.0":
version: 29.6.0
resolution: "jest-message-util@npm:29.6.0"
@@ -14073,25 +14075,25 @@ __metadata:
languageName: node
linkType: hard
-"pretty-format@npm:^29.0.3, pretty-format@npm:^29.7.0":
- version: 29.7.0
- resolution: "pretty-format@npm:29.7.0"
+"pretty-format@npm:^29.6.2":
+ version: 29.6.2
+ resolution: "pretty-format@npm:29.6.2"
dependencies:
- "@jest/schemas": ^29.6.3
+ "@jest/schemas": ^29.6.0
ansi-styles: ^5.0.0
react-is: ^18.0.0
- checksum: 032c1602383e71e9c0c02a01bbd25d6759d60e9c7cf21937dde8357aa753da348fcec5def5d1002c9678a8524d5fe099ad98861286550ef44de8808cc61e43b6
+ checksum: a0f972a44f959023c0df9cdfe9eed7540264d7f7ddf74667db8a5294444d5aa153fd47d20327df10ae86964e2ceec10e46ea06b1a5c9c12e02348b78c952c9fc
languageName: node
linkType: hard
-"pretty-format@npm:^29.6.2":
- version: 29.6.2
- resolution: "pretty-format@npm:29.6.2"
+"pretty-format@npm:^29.7.0":
+ version: 29.7.0
+ resolution: "pretty-format@npm:29.7.0"
dependencies:
- "@jest/schemas": ^29.6.0
+ "@jest/schemas": ^29.6.3
ansi-styles: ^5.0.0
react-is: ^18.0.0
- checksum: a0f972a44f959023c0df9cdfe9eed7540264d7f7ddf74667db8a5294444d5aa153fd47d20327df10ae86964e2ceec10e46ea06b1a5c9c12e02348b78c952c9fc
+ checksum: 032c1602383e71e9c0c02a01bbd25d6759d60e9c7cf21937dde8357aa753da348fcec5def5d1002c9678a8524d5fe099ad98861286550ef44de8808cc61e43b6
languageName: node
linkType: hard
@@ -14525,6 +14527,7 @@ __metadata:
"@react-native-community/eslint-config": ^3.2.0
"@react-native-tvos/config-tv": ^0.0.4
"@react-navigation/bottom-tabs": ^6.5.11
+ "@testing-library/react-native": ^12.4.3
"@types/react": ^18.2.14
"@types/react-dom": ^18.2.6
"@typescript-eslint/eslint-plugin": ^5.60.1
@@ -14562,7 +14565,6 @@ __metadata:
resolution: "react-tv-space-navigation@workspace:packages/lib"
dependencies:
"@bam.tech/lrud": ^8.0.1
- "@testing-library/jest-native": ^5.4.3
"@testing-library/react-hooks": ^8.0.1
"@testing-library/react-native": ^12.3.1
"@types/jest": ^29.5.3