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