From 03f93e491ced546f8f248aae7f9a63ad7afeb6b9 Mon Sep 17 00:00:00 2001 From: Konrad Rydygier Date: Thu, 3 Sep 2026 09:44:19 +0200 Subject: [PATCH 1/4] fix: remove derived testIDs --- src/components/Appbar/AppbarContent.tsx | 1 - .../BottomNavigation/BottomNavigationBar.tsx | 6 +- src/components/Button/Button.tsx | 7 +- src/components/Card/Card.tsx | 2 - src/components/Checkbox/CheckboxItem.tsx | 1 - src/components/Chip/Chip.tsx | 1 - src/components/CrossFadeIcon.tsx | 11 +- src/components/DataTable/DataTableCell.tsx | 8 +- src/components/Drawer/DrawerCollapsedItem.tsx | 6 +- src/components/FAB/Content.tsx | 3 - src/components/FAB/Menu.tsx | 1 - src/components/FAB/Shell.tsx | 2 - src/components/IconButton/IconButton.tsx | 8 +- src/components/List/ListItem.tsx | 5 +- src/components/Menu/Menu.tsx | 3 +- src/components/Menu/MenuItem.tsx | 1 - src/components/Modal.tsx | 3 - src/components/ProgressBar.tsx | 1 - src/components/Searchbar.tsx | 6 - .../SegmentedButtons/SegmentedButtonItem.tsx | 11 +- src/components/Snackbar.tsx | 1 - .../__tests__/Appbar/Appbar.test.tsx | 37 +- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 1040 +++ .../__tests__/BottomNavigation.test.tsx | 53 +- src/components/__tests__/Button.test.tsx | 36 +- src/components/__tests__/Card/Card.test.tsx | 20 +- .../Card/__snapshots__/Card.test.tsx.snap | 248 + .../__tests__/Checkbox/CheckboxItem.test.tsx | 15 +- .../__snapshots__/CheckboxItem.test.tsx.snap | 275 + src/components/__tests__/DataTable.test.tsx | 31 +- src/components/__tests__/Dialog.test.tsx | 6 +- .../Drawer/DrawerCollapsedItem.test.tsx | 96 +- .../DrawerCollapsedItem.test.tsx.snap | 637 ++ src/components/__tests__/FABExtended.test.tsx | 16 +- src/components/__tests__/IconButton.test.tsx | 14 +- src/components/__tests__/ListItem.test.tsx | 4 +- src/components/__tests__/Menu.test.tsx | 38 +- src/components/__tests__/MenuItem.test.tsx | 8 +- src/components/__tests__/Modal.test.tsx | 175 +- src/components/__tests__/ProgressBar.test.tsx | 6 +- src/components/__tests__/Searchbar.test.tsx | 77 +- .../__tests__/SegmentedButton.test.tsx | 265 +- .../BottomNavigation.test.tsx.snap | 7010 ++++++++++++++--- .../__snapshots__/Button.test.tsx.snap | 2548 +++++- .../__snapshots__/DataTable.test.tsx.snap | 304 + .../__snapshots__/FABMenu.test.tsx.snap | 16 - .../__snapshots__/IconButton.test.tsx.snap | 246 + .../__snapshots__/ListItem.test.tsx.snap | 151 +- .../__snapshots__/ListSection.test.tsx.snap | 6 - .../__snapshots__/Menu.test.tsx.snap | 1358 ++++ .../__snapshots__/Modal.test.tsx.snap | 4576 +++++++++++ .../__snapshots__/ProgressBar.test.tsx.snap | 46 + .../__snapshots__/Searchbar.test.tsx.snap | 403 + .../SegmentedButton.test.tsx.snap | 2170 +++++ 54 files changed, 20289 insertions(+), 1730 deletions(-) create mode 100644 src/components/__tests__/Drawer/__snapshots__/DrawerCollapsedItem.test.tsx.snap create mode 100644 src/components/__tests__/__snapshots__/Modal.test.tsx.snap diff --git a/src/components/Appbar/AppbarContent.tsx b/src/components/Appbar/AppbarContent.tsx index 386f64158f..37c3b28e2c 100644 --- a/src/components/Appbar/AppbarContent.tsx +++ b/src/components/Appbar/AppbarContent.tsx @@ -135,7 +135,6 @@ const AppbarContent = ({ numberOfLines={1} accessible role={onPress ? 'none' : 'heading'} - testID={testID ? `${testID}-title-text` : undefined} maxFontSizeMultiplier={titleMaxFontSizeMultiplier} > {title} diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index eff4e9780b..c53545b39a 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -493,10 +493,7 @@ const BottomNavigationBar = ({ ]} onLayout={onLayout} > - + ({ }, ]} role={'tablist'} - testID={testID ? `${testID}-content-wrapper` : undefined} > {routes.map((route, index) => { const focused = navigationState.index === index; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 11cda00ccc..82b327dc74 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -292,7 +292,6 @@ const Button = ({ {icon && loading !== true ? ( - + {isMode('outlined') && ( diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 40cb460ffb..bb1bd30ed1 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -281,7 +281,6 @@ const Chip = ({ elevation={elevation} transitionDuration={elevationTransitionDuration} {...rest} - testID={testID ? `${testID}-container` : undefined} theme={theme} > {hasPreviousIcon ? ( - + ) : null} - + diff --git a/src/components/DataTable/DataTableCell.tsx b/src/components/DataTable/DataTableCell.tsx index d1d730e0b3..af332b3ae4 100644 --- a/src/components/DataTable/DataTableCell.tsx +++ b/src/components/DataTable/DataTableCell.tsx @@ -84,7 +84,6 @@ const DataTableCell = ({ > {children} @@ -97,11 +96,7 @@ const CellContent = ({ children, textStyle, maxFontSizeMultiplier, - testID, -}: Pick< - Props, - 'children' | 'textStyle' | 'testID' | 'maxFontSizeMultiplier' ->) => { +}: Pick) => { if (React.isValidElement(children)) { return children; } @@ -111,7 +106,6 @@ const CellContent = ({ style={textStyle} numberOfLines={1} maxFontSizeMultiplier={maxFontSizeMultiplier} - testID={testID ? `${testID}-text-container` : undefined} > {children} diff --git a/src/components/Drawer/DrawerCollapsedItem.tsx b/src/components/Drawer/DrawerCollapsedItem.tsx index dccb8cc555..9ffd121dca 100644 --- a/src/components/Drawer/DrawerCollapsedItem.tsx +++ b/src/components/Drawer/DrawerCollapsedItem.tsx @@ -195,13 +195,9 @@ const DrawerCollapsedItem = ({ style, animatedOutlineStyle, ]} - testID={testID ? `${testID}-outline` : undefined} /> - + {badge !== false && ( {typeof badge === 'boolean' ? ( diff --git a/src/components/FAB/Content.tsx b/src/components/FAB/Content.tsx index 6114bcd62c..2055405688 100644 --- a/src/components/FAB/Content.tsx +++ b/src/components/FAB/Content.tsx @@ -27,7 +27,6 @@ export type ContentProps = { labelAnimatedStyle?: StyleProp>; labelNumberOfLines?: number; labelEllipsisMode?: 'clip' | 'tail' | 'head' | 'middle'; - testID?: string; }; /** @@ -50,7 +49,6 @@ const Content = ({ labelAnimatedStyle, labelNumberOfLines, labelEllipsisMode, - testID, }: ContentProps) => { const hasLabel = label !== undefined && label !== ''; const colorStyle = { color: contentColor }; @@ -86,7 +84,6 @@ const Content = ({ ellipsizeMode={labelEllipsisMode} maxFontSizeMultiplier={labelMaxFontSizeMultiplier} style={colorStyle} - testID={testID ? `${testID}-text` : undefined} > {label} diff --git a/src/components/FAB/Menu.tsx b/src/components/FAB/Menu.tsx index 438d7c2f1a..e4884647d6 100644 --- a/src/components/FAB/Menu.tsx +++ b/src/components/FAB/Menu.tsx @@ -284,7 +284,6 @@ const MenuItem = ({ leading={leading} trailing={trailing} iconLabelGap={iconLabelGap} - testID={testID} /> diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx index 6a07babb47..83a71f72ed 100644 --- a/src/components/FAB/Shell.tsx +++ b/src/components/FAB/Shell.tsx @@ -319,7 +319,6 @@ const Shell = ({ visible ? styles.pointerEventsAuto : styles.pointerEventsNone, ]} elevation={elevation} - testID={testID ? `${testID}-container` : undefined} theme={theme} > @@ -357,7 +356,6 @@ const Shell = ({ labelAnimatedStyle={labelAnimatedStyle} labelNumberOfLines={labelAnimatedStyle ? 1 : undefined} labelEllipsisMode={labelAnimatedStyle ? 'clip' : undefined} - testID={testID} /> )} diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index a55c75e6c6..1d673a3d27 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -161,7 +161,6 @@ const IconButton = ({ return ( ) : ( - + )} diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 740b3df5d2..feb0f19f12 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -245,10 +245,7 @@ const ListItem = ({ style: getLeftStyles(alignToTop, description), }) : null} - + {renderTitle()} {description diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 12b3c536c7..0c7d68eae8 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -691,7 +691,6 @@ const Menu = ({ style={[styles.wrapper, positionStyle, style]} pointerEvents={pointerEvents} onAccessibilityEscape={onDismiss} - testID={testID ? `${testID}-view` : undefined} > diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index da0975a44a..5cc802df95 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -235,7 +235,6 @@ function Modal({ onPress={dismissable ? onDismissCallback : undefined} importantForAccessibility="no" style={[styles.backdrop, backdropStyle, backdropTransitionStyle]} - testID={testID ? `${testID}-backdrop` : undefined} /> {width ? ( @@ -257,7 +256,6 @@ const Searchbar = ({ } theme={theme} aria-label={searchAccessibilityLabel} - testID={testID ? `${testID}-icon` : undefined} /> )) } - testID={testID ? `${testID}-clear-icon` : undefined} role="button" theme={theme} /> @@ -331,7 +327,6 @@ const Searchbar = ({ iconColor={traileringIconColor || colors.onSurfaceVariant} icon={traileringIcon} aria-label={traileringIconAccessibilityLabel} - testID={testID ? `${testID}-trailering-icon` : undefined} /> ) : null} {isBarMode && @@ -345,7 +340,6 @@ const Searchbar = ({ backgroundColor: colors.outline, }, ]} - testID={testID ? `${testID}-divider` : undefined} /> )} diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 4b851de75a..33886d7ec1 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -223,18 +223,12 @@ const SegmentedButtonItem = ({ style={[styles.content, { paddingVertical, opacity: textOpacity }]} > {showCheckedIcon ? ( - + ) : null} {showIcon ? ( - + ) : null} @@ -244,7 +238,6 @@ const SegmentedButtonItem = ({ selectable={false} numberOfLines={1} maxFontSizeMultiplier={labelMaxFontSizeMultiplier} - testID={testID ? `${testID}-label` : undefined} > {label} diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index fc43a08bf2..aa23b4b2a5 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -339,7 +339,6 @@ const Snackbar = ({ } aria-label={iconAccessibilityLabel} style={styles.icon} - testID={testID ? `${testID}-icon` : undefined} /> ) : null} diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 396ffd6734..df2c63a56f 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -190,58 +190,39 @@ describe('renderAppbarContent', () => { describe('AppbarAction', () => { it('should be rendered with default theme color', async () => { - await render( + const { toJSON } = await render( ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') - .props.children; - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarActionIcon.props.color).toBe( - getTheme().colors.onSurfaceVariant - ); + expect(toJSON()).toMatchSnapshot(); }); it('should be rendered with specific theme color if is leading', async () => { - await render( + const { toJSON } = await render( ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') - .props.children; - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarActionIcon.props.color).toBe(getTheme().colors.onSurface); + expect(toJSON()).toMatchSnapshot(); }); it('should be rendered with custom color', async () => { - await render( + const { toJSON } = await render( ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') - .props.children; - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarActionIcon.props.color).toBe('purple'); + expect(toJSON()).toMatchSnapshot(); }); it('should render AppbarBackAction with custom color', async () => { - await render( + const { toJSON } = await render( ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarBackActionIcon = screen.getByTestId( - 'appbar-action-icon-current' - ).props.children; - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarBackActionIcon.props.color).toBe('purple'); + expect(toJSON()).toMatchSnapshot(); }); }); @@ -254,7 +235,7 @@ describe('AppbarContent', () => { ); - expect(screen.getByTestId('appbar-content-title-text')).toHaveStyle( + expect(screen.getByText('Title')).toHaveStyle( getTheme().fonts[modeTextVariant[mode]] ); }) diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index ad5805e1e6..faeb89916b 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -956,3 +956,1043 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A `; + +exports[`AppbarAction should be rendered with custom color 1`] = ` + + + + + + + + + menu + + + + + + + +`; + +exports[`AppbarAction should be rendered with default theme color 1`] = ` + + + + + + + + + menu + + + + + + + +`; + +exports[`AppbarAction should be rendered with specific theme color if is leading 1`] = ` + + + + + + + + + menu + + + + + + + +`; + +exports[`AppbarAction should render AppbarBackAction with custom color 1`] = ` + + + + + + + + + + + + + + + + +`; diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index 6a9f14c0a5..dbf663beb1 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -428,7 +428,7 @@ it('should have labelMaxFontSizeMultiplier passed to label', async () => { }); it('renders custom background color passed to barStyle property', async () => { - await render( + const { toJSON } = await render( { /> ); - const wrapper = screen.getByTestId('bottom-navigation-bar-content'); - expect(wrapper).toHaveStyle({ backgroundColor: Palette.error60 }); + expect(toJSON()).toMatchSnapshot(); }); it('uses the rendered bar height when hiding it for the keyboard', async () => { @@ -528,7 +527,7 @@ it('renders bottom navigation with getLazy', async () => { }); it('applies maxTabBarWidth styling if compact prop is truthy', async () => { - await render( + const { toJSON } = await render( { /> ); - expect( - screen.getByTestId('bottom-navigation-bar-content-wrapper') - ).toHaveStyle({ - maxWidth: 480, - }); + expect(toJSON()).toMatchSnapshot(); }); it('does not apply maxTabBarWidth styling if compact prop is falsy', async () => { - await render( + const { toJSON } = await render( /> ); - expect( - screen.getByTestId('bottom-navigation-bar-content-wrapper') - ).not.toHaveStyle({ - maxWidth: 480, - }); -}); - -it('renders bar content when shifting is enabled', async () => { - await render( - route.key === 'key-2'} - shifting - /> - ); - - expect(screen.getByTestId('bottom-navigation-bar-content')).toBeOnTheScreen(); -}); - -it('does not render legacy ripple overlay when shifting is disabled', async () => { - await render( - route.key === 'key-2'} - shifting={false} - /> - ); - - expect( - screen.queryByTestId('bottom-navigation-bar-content-ripple') - ).not.toBeOnTheScreen(); + expect(toJSON()).toMatchSnapshot(); }); describe('getActiveTintColor', () => { diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index be3d573d29..f9857c31d5 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -180,7 +180,7 @@ describe('button text styles', () => { ); - expect(screen.getByTestId('button-text')).toHaveStyle({ + expect(screen.getByText('Test')).toHaveStyle({ textTransform: 'uppercase', }); }); @@ -192,7 +192,7 @@ describe('button text styles', () => { ); - expect(screen.getByTestId('button-text')).not.toHaveStyle({ + expect(screen.getByText('Test')).not.toHaveStyle({ textTransform: 'uppercase', }); }); @@ -200,60 +200,44 @@ describe('button text styles', () => { describe('button icon styles', () => { it('should return correct icon styles for compact text button', async () => { - await render( + const { toJSON } = await render( ); - expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle({ - marginLeft: 6, - marginRight: 0, - }); + expect(toJSON()).toMatchSnapshot(); }); (['outlined', 'contained', 'contained-tonal', 'elevated'] as const).forEach( (mode) => it(`should return correct icon styles for compact ${mode} button`, async () => { - await render( + const { toJSON } = await render( ); - expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle( - { - marginLeft: 8, - marginRight: 0, - } - ); + expect(toJSON()).toMatchSnapshot(); }) ); it('should return correct icon styles for text button', async () => { - await render( + const { toJSON } = await render( ); - expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle({ - marginLeft: 12, - marginRight: -8, - }); + expect(toJSON()).toMatchSnapshot(); }); (['outlined', 'contained', 'contained-tonal', 'elevated'] as const).forEach( (mode) => it(`should return correct icon styles for compact ${mode} button`, async () => { - await render( + const { toJSON } = await render( ); - expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle( - { - marginLeft: 16, - marginRight: -16, - } - ); + expect(toJSON()).toMatchSnapshot(); }) ); }); diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index 16163ba257..006251e08e 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -33,43 +33,31 @@ describe('Card', () => { }); it('renders an outlined card with a custom outline color', async () => { - const testID = 'custom-outline-card'; - - await render( + const { toJSON } = await render( {null} ); - expect(screen.getByTestId(`${testID}-outline`)).toHaveStyle({ - borderColor: 'purple', - borderWidth: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); it('renders an outlined card with custom border color', async () => { - const testID = 'custom-border-card'; - - await render( + const { toJSON } = await render( {null} ); - expect(screen.getByTestId(`${testID}-outline`)).toHaveStyle({ - borderColor: Palette.error50, - borderWidth: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); it('renders with a custom theme background color', async () => { diff --git a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap index 0554292cee..216e5b5d08 100644 --- a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap +++ b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap @@ -121,3 +121,251 @@ exports[`Card renders an outlined card 1`] = ` /> `; + +exports[`Card renders an outlined card with a custom outline color 1`] = ` + + + + + +`; + +exports[`Card renders an outlined card with custom border color 1`] = ` + + + + + +`; diff --git a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx index b1a9c38925..ee52c1b3e4 100644 --- a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx +++ b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx @@ -59,14 +59,13 @@ it('disables the row when the prop disabled is true', async () => { }); it('should have maxFontSizeMultiplier set to 1.5 by default', async () => { - await render( - - ); - const checkboxItemText = screen.getByTestId('checkbox-item-text', { - includeHiddenElements: true, - }); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(checkboxItemText.props.maxFontSizeMultiplier).toBe(1.5); + const tree = ( + await render( + + ) + ).toJSON(); + + expect(tree).toMatchSnapshot(); }); it('should execute onLongPress', async () => { diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index bc5b884910..e2d338f791 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -551,3 +551,278 @@ exports[`renders unchecked 1`] = ` `; + +exports[`should have maxFontSizeMultiplier set to 1.5 by default 1`] = ` + + + + + + + + + + + + + + + + + + +`; diff --git a/src/components/__tests__/DataTable.test.tsx b/src/components/__tests__/DataTable.test.tsx index 49f863720a..7a29a72800 100644 --- a/src/components/__tests__/DataTable.test.tsx +++ b/src/components/__tests__/DataTable.test.tsx @@ -66,25 +66,28 @@ describe('DataTable.Cell', () => { expect(tree).toMatchSnapshot(); }); - it('renders data table cell with text container', async () => { - await render( - Table cell - ); + it('renders data table cell with text content', async () => { + const tree = ( + await render( + Table cell + ) + ).toJSON(); expect(screen.getByText('Table cell')).toBeOnTheScreen(); - expect(screen.getByTestId('table-cell-text-container')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); - it('renders data table cell children without text container', async () => { - await render( - - - - ); + it('renders data table cell children without wrapping text component', async () => { + const tree = ( + await render( + + + + ) + ).toJSON(); - expect( - screen.queryByTestId('table-cell-text-container') - ).not.toBeOnTheScreen(); + expect(screen.getByTestId('table-cell-checkbox')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); }); diff --git a/src/components/__tests__/Dialog.test.tsx b/src/components/__tests__/Dialog.test.tsx index 32477e5fe1..48d22a587b 100644 --- a/src/components/__tests__/Dialog.test.tsx +++ b/src/components/__tests__/Dialog.test.tsx @@ -41,7 +41,7 @@ describe('Dialog', () => { ); - await userEvent.press(screen.getByTestId('dialog-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); await act(() => { jest.runAllTimers(); @@ -57,7 +57,7 @@ describe('Dialog', () => { ); - await userEvent.press(screen.getByTestId('dialog-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); await act(() => { jest.runAllTimers(); @@ -80,7 +80,7 @@ describe('Dialog', () => { ); - await userEvent.press(screen.getByTestId('dialog-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); await act(() => { jest.runAllTimers(); diff --git a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx index fd0263b5e2..b301d39062 100644 --- a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx +++ b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx @@ -1,80 +1,62 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../test-utils'; +import { render } from '../../../test-utils'; import DrawerCollapsedItem from '../../Drawer/DrawerCollapsedItem'; describe('DrawerCollapsedItem', () => { it('should have regular outline if label is specified', async () => { - await render( - - ); - - expect(screen.getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ - height: 32, - }); + const tree = ( + await render( + + ) + ).toJSON(); + + expect(tree).toMatchSnapshot(); }); it('should have rounded outline if label is not specified', async () => { - await render( - - ); + const tree = ( + await render( + + ) + ).toJSON(); - expect(screen.getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ - height: 56, - }); + expect(tree).toMatchSnapshot(); }); it('should display unfocused icon in inactive state, if unfocused icon is specified', async () => { - await render( - - ); + const tree = ( + await render( + + ) + ).toJSON(); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('drawer-collapsed-item-container').props.children[1] - .props.source - ).toBe('star-outline'); + expect(tree).toMatchSnapshot(); }); it('should display focused icon in inactive state, if unfocused icon is not specified', async () => { - await render( - - ); + const tree = ( + await render() + ).toJSON(); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('drawer-collapsed-item-container').props.children[1] - .props.source - ).toBe('star'); + expect(tree).toMatchSnapshot(); }); it('should display focused icon in active state', async () => { - await render( - - ); - - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('drawer-collapsed-item-container').props.children[1] - .props.source - ).toBe('star'); + const tree = ( + await render( + + ) + ).toJSON(); + + expect(tree).toMatchSnapshot(); }); }); diff --git a/src/components/__tests__/Drawer/__snapshots__/DrawerCollapsedItem.test.tsx.snap b/src/components/__tests__/Drawer/__snapshots__/DrawerCollapsedItem.test.tsx.snap new file mode 100644 index 0000000000..1bdc1cec4f --- /dev/null +++ b/src/components/__tests__/Drawer/__snapshots__/DrawerCollapsedItem.test.tsx.snap @@ -0,0 +1,637 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DrawerCollapsedItem should display focused icon in active state 1`] = ` + + + + + + + star + + + + + +`; + +exports[`DrawerCollapsedItem should display focused icon in inactive state, if unfocused icon is not specified 1`] = ` + + + + + + + star + + + + + +`; + +exports[`DrawerCollapsedItem should display unfocused icon in inactive state, if unfocused icon is specified 1`] = ` + + + + + + + star-outline + + + + + +`; + +exports[`DrawerCollapsedItem should have regular outline if label is specified 1`] = ` + + + + + + + star-outline + + + + starred + + + + +`; + +exports[`DrawerCollapsedItem should have rounded outline if label is not specified 1`] = ` + + + + + + + star-outline + + + + + +`; diff --git a/src/components/__tests__/FABExtended.test.tsx b/src/components/__tests__/FABExtended.test.tsx index e8e530484d..e16ca3854b 100644 --- a/src/components/__tests__/FABExtended.test.tsx +++ b/src/components/__tests__/FABExtended.test.tsx @@ -1,4 +1,5 @@ -import { Platform } from 'react-native'; +import * as React from 'react'; +import { Platform, View } from 'react-native'; import { afterEach, expect, it, jest } from '@jest/globals'; import { fireEvent, userEvent } from '@testing-library/react-native'; @@ -43,20 +44,25 @@ it('expands to fit the measured label width', async () => { pageY: 0, }); + const ref = React.createRef(); await render( ); await jest.runAllTimersAsync(); - expect( - Reanimated.getAnimatedStyle(screen.getByTestId('extended-fab-container')) - ).toMatchObject({ width: 144 }); + if (!ref.current) { + throw new Error('Expected FAB ref to be attached'); + } + + expect(Reanimated.getAnimatedStyle(ref.current)).toMatchObject({ + width: 144, + }); }); it('renders extended FAB collapsed', async () => { diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index a8bc540aa9..96a4969aeb 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import { describe, expect, it } from '@jest/globals'; import { getTheme } from '../../core/theming'; -import { render, screen } from '../../test-utils'; +import { render } from '../../test-utils'; import { pink500 } from '../../theme/colors'; import { tokens } from '../../theme/tokens'; import IconButton from '../IconButton/IconButton'; @@ -53,7 +53,7 @@ it('renders icon change animated', async () => { }); it('renders icon button with custom border radius', async () => { - await render( + const { toJSON } = await render( { /> ); - expect(screen.getByTestId('icon-button-container')).toHaveStyle({ - borderRadius: 0, - }); + expect(toJSON()).toMatchSnapshot(); }); it('renders icon button with small border radius', async () => { - await render( + const { toJSON } = await render( { /> ); - expect(screen.getByTestId('icon-button-container')).toHaveStyle({ - borderRadius: 4, - }); + expect(toJSON()).toMatchSnapshot(); }); describe('getIconButtonColor - icon color', () => { diff --git a/src/components/__tests__/ListItem.test.tsx b/src/components/__tests__/ListItem.test.tsx index e0e1cb5303..98e6e0f0fe 100644 --- a/src/components/__tests__/ListItem.test.tsx +++ b/src/components/__tests__/ListItem.test.tsx @@ -166,7 +166,7 @@ it('calling onPress on ListItem right component', async () => { }); it('renders list item with custom content style', async () => { - await render( + const { toJSON } = await render( { /> ); - expect(screen.getByTestId('list-item-content')).toHaveStyle(styles.content); + expect(toJSON()).toMatchSnapshot(); }); diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index fc6369599c..7429b58c8c 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -71,7 +71,7 @@ elevations.forEach((elevation) => ); - expect(screen.getByTestId(`${testID}-surface`)).toHaveStyle({ + expect(screen.getByTestId(testID)).toHaveStyle({ backgroundColor: theme.colors.elevation[`level${elevation}`], }); }) @@ -109,7 +109,7 @@ it('uses the default anchorPosition of top', async () => { ); } - const { rerender } = await render(makeMenu(false)); + const { rerender, toJSON } = await render(makeMenu(false)); // You must update instead of creating directly and using it because // componentDidUpdate isn't called by default in jest. Forcing the update @@ -122,14 +122,13 @@ it('uses the default anchorPosition of top', async () => { }); await waitFor(() => { - const menu = screen.getByTestId(`${testID}-view`); - expect(menu).toHaveStyle({ - position: 'absolute', - left: 100, - top: 100, - }); + const json = JSON.stringify(toJSON()); + expect(json).toContain('"left":100'); + expect(json).toContain('"top":100'); }); + expect(toJSON()).toMatchSnapshot(); + measureSpy.mockRestore(); dimensionsSpy.mockRestore(); }); @@ -167,7 +166,7 @@ it('respects anchorPosition bottom', async () => { ); } - const { rerender } = await render(makeMenu(false)); + const { rerender, toJSON } = await render(makeMenu(false)); await act(async () => { await rerender(makeMenu(true)); @@ -176,14 +175,13 @@ it('respects anchorPosition bottom', async () => { }); await waitFor(() => { - const menu = screen.getByTestId(`${testID}-view`); - expect(menu).toHaveStyle({ - position: 'absolute', - left: 100, - top: 132, - }); + const json = JSON.stringify(toJSON()); + expect(json).toContain('"left":100'); + expect(json).toContain('"top":132'); }); + expect(toJSON()).toMatchSnapshot(); + measureSpy.mockRestore(); dimensionsSpy.mockRestore(); }); @@ -206,11 +204,8 @@ it('renders menu with mode "elevated"', async () => { ); - const menuSurface = screen.getByTestId(`${testID}-surface`); - - // Get flattened styles // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const styles = StyleSheet.flatten(menuSurface.props.style); + const styles = StyleSheet.flatten(screen.getByTestId(testID).props.style); expect(styles).toHaveProperty('shadowColor'); expect(styles).toHaveProperty('shadowOpacity'); @@ -234,11 +229,8 @@ it('renders menu with mode "flat"', async () => { ); - const menuSurface = screen.getByTestId(`${testID}-surface`); - - // Get flattened styles // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const styles = StyleSheet.flatten(menuSurface.props.style); + const styles = StyleSheet.flatten(screen.getByTestId(testID).props.style); expect(styles).not.toHaveProperty('shadowColor'); expect(styles).not.toHaveProperty('shadowOpacity'); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index 4662ce968e..d6cadda51f 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -48,10 +48,10 @@ describe('Menu Item', () => { /> ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('menu-item-title').props.maxFontSizeMultiplier - ).toBe(labelMaxFontSizeMultiplier); + expect(screen.getByText('Cut')).toHaveProp( + 'maxFontSizeMultiplier', + labelMaxFontSizeMultiplier + ); }); it('accepts aria-checked prop', async () => { diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index 83d644e0e1..657eaf0992 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -57,7 +57,7 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ backgroundColor: LightTheme.colors.scrim, }); }); @@ -77,29 +77,26 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ backgroundColor: 'transparent', }); }); it('should receive appropriate top and bottom insets', async () => { - await render( + const { toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-wrapper')).toHaveStyle({ - marginTop: 37, - marginBottom: 44, - }); + expect(toJSON()).toMatchSnapshot(); }); }); describe('when open', () => { describe('if backdrop touched', () => { it('should invoke the onDismiss function immediately', async () => { const onDismiss = jest.fn(); - await render( + const { toJSON } = await render( {null} @@ -107,42 +104,36 @@ describe('Modal', () => { expect(onDismiss).not.toHaveBeenCalled(); - await userEvent.press(screen.getByTestId('modal-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); expect(onDismiss).toHaveBeenCalled(); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); expect(onDismiss).toHaveBeenCalledTimes(1); }); }); it('runs the closing animation if visible toggled', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {}}> {null} ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); - await userEvent.press(screen.getByTestId('modal-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); await rerender( {}}> @@ -150,59 +141,47 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.queryByTestId('modal-surface')).not.toBeOnTheScreen(); - - expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); + expect(toJSON()).toBeNull(); }); describe('if closed via Android back button', () => { it('invokes onDismiss', async () => { const onDismiss = jest.fn(); - await render( + const { toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { BackHandler.mockPressBack(); }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); expect(onDismiss).toHaveBeenCalledTimes(1); }); @@ -212,7 +191,7 @@ describe('Modal', () => { describe('when open as non-dismissible modal', () => { describe('if closed via touching backdrop', () => { it('will run the animation but not fade out', async () => { - await render( + const { toJSON } = await render( { ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); - await userEvent.press(screen.getByTestId('modal-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); it('should not invoke onDismiss', async () => { @@ -261,7 +234,7 @@ describe('Modal', () => { expect(onDismiss).not.toHaveBeenCalled(); - await userEvent.press(screen.getByTestId('modal-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); expect(onDismiss).not.toHaveBeenCalled(); @@ -275,7 +248,7 @@ describe('Modal', () => { describe('if closed via Android back button', () => { it('will run the animation but not fade out', async () => { - await render( + const { toJSON } = await render( { ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { BackHandler.mockPressBack(); }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); it('should not invoke onDismiss', async () => { @@ -345,7 +312,7 @@ describe('Modal', () => { describe('when visible prop changes', () => { describe('from false to true (closed to open)', () => { it('should run fade-in animation on opening', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} @@ -359,40 +326,34 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 0, - }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: 0, }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); }); describe('from true to false (open to closed)', () => { it('should run fade-out animation on closing', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await rerender( @@ -400,12 +361,10 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); @@ -441,18 +400,16 @@ describe('Modal', () => { }); it('should close even if the dialog is not dismissible', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await rerender( @@ -460,12 +417,10 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); @@ -479,18 +434,16 @@ describe('Modal', () => { describe('when visible prop changes again during the open/close animation', () => { describe('while closing, back to true (visible)', () => { it('should keep the modal open', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await rerender( @@ -498,12 +451,10 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { // Not a real seconds, this depends on how frequently @@ -521,24 +472,22 @@ describe('Modal', () => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); }); describe('while opening, back to false (hidden)', () => { it('should keep the modal closed', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} ); - expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); + expect(screen.queryByLabelText('Close modal')).not.toBeOnTheScreen(); await rerender( @@ -546,12 +495,10 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 0, - }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: 0, }); + expect(toJSON()).toMatchSnapshot(); await act(() => { // Not a real seconds, this depends on how frequently @@ -559,7 +506,7 @@ describe('Modal', () => { jest.advanceTimersToNextTimer(1000); }); - expect(screen.getByTestId('modal-backdrop')).toBeOnTheScreen(); + expect(screen.getByLabelText('Close modal')).toBeOnTheScreen(); await rerender( @@ -571,7 +518,7 @@ describe('Modal', () => { jest.runAllTimers(); }); - expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); + expect(screen.queryByLabelText('Close modal')).not.toBeOnTheScreen(); }); }); }); diff --git a/src/components/__tests__/ProgressBar.test.tsx b/src/components/__tests__/ProgressBar.test.tsx index bed20ed08d..afd95f664b 100644 --- a/src/components/__tests__/ProgressBar.test.tsx +++ b/src/components/__tests__/ProgressBar.test.tsx @@ -94,12 +94,10 @@ it('has progressbar role', async () => { }); it('renders progress bar with custom style of filled part', async () => { - await render( + const view = await render( ); await triggerLayout(); - expect(screen.getByTestId('progress-bar-fill')).toHaveStyle({ - borderRadius: 4, - }); + expect(view.toJSON()).toMatchSnapshot(); }); diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index 5fea2800ed..107a912d63 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -44,30 +44,25 @@ it('renders clear icon with custom color', async () => { ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const iconComponent = screen.getByTestId('search-bar-icon-wrapper').props - .children; - - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(iconComponent.props.iconColor).toBe('purple'); -}); - -it('renders clear icon wrapper, which can be the target of touch events, if search has value', async () => { - await render(); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('search-bar-icon-wrapper').props.pointerEvents - ).toBe('auto'); + screen.getByText('close', { includeHiddenElements: true }) + ).toHaveStyle({ color: 'purple' }); }); -it('renders clear icon wrapper, which is never target of touch events, if search has no value', async () => { - await render(); +it('does not respond to touch on the clear icon when search has no value', async () => { + const onClearIconPressMock = jest.fn(); + await render( + + ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('search-bar-icon-wrapper').props.pointerEvents - ).toBe('none'); + await userEvent.press( + screen.getByLabelText('clear', { includeHiddenElements: true }) + ); + expect(onClearIconPressMock).not.toHaveBeenCalled(); }); it('defines onClearIconPress action and checks if it is called when close button is pressed', async () => { @@ -79,20 +74,12 @@ it('defines onClearIconPress action and checks if it is called when close button onClearIconPress={onClearIconPressMock} /> ); - await userEvent.press(screen.getByTestId('search-bar-clear-icon')); + await userEvent.press(screen.getByLabelText('clear')); expect(onClearIconPressMock).toHaveBeenCalledTimes(1); }); -it('renders clear icon wrapper, with appropriate style for v3', async () => { - const { rerender } = await render(); - - expect(screen.getByTestId('search-bar-icon-wrapper')).toHaveStyle({ - position: 'absolute', - right: 0, - marginLeft: 16, - }); - - await rerender( +it('hides the clear icon when a custom right element is rendered', async () => { + await render( { /> ); - expect( - screen.getByTestId('search-bar-icon-wrapper', { - includeHiddenElements: true, - }) - ).toHaveStyle({ display: 'none' }); + expect(screen.queryByLabelText('clear')).not.toBeOnTheScreen(); }); it('renders trailering icon when mode is set to "bar"', async () => { @@ -113,11 +96,12 @@ it('renders trailering icon when mode is set to "bar"', async () => { testID="search-bar" value={''} traileringIcon={'microphone'} + traileringIconAccessibilityLabel="microphone" mode="bar" /> ); - expect(screen.getByTestId('search-bar-trailering-icon')).toBeOnTheScreen(); + expect(screen.getByLabelText('microphone')).toBeOnTheScreen(); }); it('renders trailering icon with press functionality', async () => { @@ -128,12 +112,13 @@ it('renders trailering icon with press functionality', async () => { testID="search-bar" value={''} traileringIcon={'microphone'} + traileringIconAccessibilityLabel="microphone" onTraileringIconPress={onTraileringIconPressMock} mode="bar" /> ); - await userEvent.press(screen.getByTestId('search-bar-trailering-icon')); + await userEvent.press(screen.getByLabelText('microphone')); expect(onTraileringIconPressMock).toHaveBeenCalledTimes(1); }); @@ -143,31 +128,29 @@ it('renders clear icon instead of trailering icon', async () => { testID="search-bar" value={''} traileringIcon={'microphone'} + traileringIconAccessibilityLabel="microphone" mode="bar" /> ); - expect(screen.getByTestId('search-bar-trailering-icon')).toBeOnTheScreen(); + expect(screen.getByLabelText('microphone')).toBeOnTheScreen(); await rerender( ); - expect( - screen.queryByTestId('search-bar-trailering-icon') - ).not.toBeOnTheScreen(); - expect(screen.getByTestId('search-bar-icon-wrapper')).toBeOnTheScreen(); + expect(screen.queryByLabelText('microphone')).not.toBeOnTheScreen(); + expect(screen.getByLabelText('clear')).toBeOnTheScreen(); }); it('renders searchbar in "view" mode', async () => { - await render(); + const tree = (await render()).toJSON(); - expect(screen.getByTestId('search-bar-container')).toHaveStyle({ - borderRadius: 0, - }); + expect(tree).toMatchSnapshot(); }); diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index ce950f671f..4bef0a414d 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -273,134 +273,138 @@ describe('getDisabledSegmentedButtonBorderWidth', () => { describe('should render icon when', () => { it('icon prop is passed', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); - expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); it('icon prop is passed along with label, no matter if button is checked', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); - expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); it('icon prop is passed along with label, button is checked, showSelectedCheck is false', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); - expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); }); describe('should not render icon when', () => { it('icon prop is not passed', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.queryByTestId('walking-button-icon')).not.toBeOnTheScreen(); - expect(screen.queryByTestId('driving-button-icon')).not.toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); it('icon prop is passed along with label, button is checked, showSelectedCheck is true', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.queryByTestId('walking-button-icon')).not.toBeOnTheScreen(); - expect(screen.getByTestId('walking-button-check-icon')).toBeOnTheScreen(); - expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); }); @@ -439,25 +443,26 @@ describe('should have `accessibilityState={ checked: true }` when selected', () it('show selected check icon should be shown', async () => { const onValueChange = jest.fn(); - await render( - - multiSelect - value={['walk', 'transit']} - buttons={[ - { - value: 'walk', - label: 'Walking', - showSelectedCheck: true, - testID: 'walking-check-icon', - }, - { value: 'transit', label: 'Transit' }, - { value: 'drive', label: 'Driving' }, - ]} - onValueChange={onValueChange} - /> - ); + const tree = ( + await render( + + multiSelect + value={['walk', 'transit']} + buttons={[ + { + value: 'walk', + label: 'Walking', + showSelectedCheck: true, + }, + { value: 'transit', label: 'Transit' }, + { value: 'drive', label: 'Driving' }, + ]} + onValueChange={onValueChange} + /> + ) + ).toJSON(); - expect(screen.getByTestId('walking-check-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); }); @@ -484,10 +489,10 @@ describe('labelStyle is handled', () => { /> ); - expect(screen.getByTestId('walking-button-label')).toHaveStyle({ + expect(screen.getByText('Walking')).toHaveStyle({ fontSize: 10, }); - expect(screen.getByTestId('driving-button-label')).toHaveStyle({ + expect(screen.getByText('Driving')).toHaveStyle({ fontSize: 12, }); }); @@ -507,7 +512,7 @@ describe('labelStyle is handled', () => { /> ); - expect(screen.getByTestId('walking-button-label')).toHaveStyle({ + expect(screen.getByText('Walking')).toHaveStyle({ fontSize: 14, }); }); diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index 7ffd6c9752..a1466ef10f 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -651,7 +651,7 @@ exports[`allows customizing Route's type via generics 1`] = ` `; -exports[`hides labels in non-shifting bottom navigation 1`] = ` +exports[`applies maxTabBarWidth styling if compact prop is truthy 1`] = ` + + + + Route: 1 + + + + + + + Route: 3 + + + + + + + Route: 4 + + + @@ -806,9 +948,8 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` pointerEvents="none" style={ { - "alignItems": "center", - "height": 80, - "justifyContent": "center", + "paddingBottom": 16, + "paddingTop": 12, } } > @@ -974,72 +1115,12 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` /> - - - - @@ -1047,131 +1128,112 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignItems": "center", "bottom": 0, "left": 0, - "opacity": 0, + "opacity": 1, "position": "absolute", "right": 0, - "top": 4, + "top": 0, } } > - camera - - - - - camera + Route: 0 + > + Route: 0 + @@ -1223,9 +1285,8 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` pointerEvents="none" style={ { - "alignItems": "center", - "height": 80, - "justifyContent": "center", + "paddingBottom": 16, + "paddingTop": 12, } } > @@ -1284,7 +1345,7 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` ] } > - inbox + camera - inbox + camera + + + + Route: 1 + + + + + Route: 1 + + + - - - - -`; - -exports[`hides labels in shifting bottom navigation 1`] = ` - - - - - - Route: 0 - - - - - - - @@ -1557,23 +1624,6 @@ exports[`hides labels in shifting bottom navigation 1`] = ` } } > - - magnify + inbox - magnify + inbox - - - - @@ -1778,132 +1768,113 @@ exports[`hides labels in shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignItems": "center", "bottom": 0, "left": 0, "opacity": 0, "position": "absolute", "right": 0, - "top": 4, + "top": 0, } } > - camera + Route: 2 - camera + Route: 2 - - - @@ -1954,9 +1925,8 @@ exports[`hides labels in shifting bottom navigation 1`] = ` pointerEvents="none" style={ { - "alignItems": "center", - "height": 80, - "justifyContent": "center", + "paddingBottom": 16, + "paddingTop": 12, } } > @@ -2015,7 +1985,7 @@ exports[`hides labels in shifting bottom navigation 1`] = ` ] } > - inbox + heart - inbox + heart + + + + Route: 3 + + + + + Route: 3 + + + - - - - -`; - -exports[`renders bottom navigation with getLazy 1`] = ` - - - - - - Route: 0 - - - - - - - Route: 1 - - - - - - - Route: 3 - - - - - + - - Route: 4 - - - - - - - + "paddingBottom": 16, + "paddingTop": 12, + } + } + > + + + + shopping-music + + + + + shopping-music + + + + + + + + + + Route: 4 + + + + + Route: 4 + + + + + + + + + +`; + +exports[`does not apply maxTabBarWidth styling if compact prop is falsy 1`] = ` + + + + + + Route: 0 + + + + + + + Route: 1 + + + + + + + Route: 3 + + + + + + + Route: 4 + + + + + + + + + + + + + + magnify + + + + + magnify + + + + + + + + + + Route: 0 + + + + + Route: 0 + + + + + + + + + + + camera + + + + + camera + + + + + + + + + + Route: 1 + + + + + Route: 1 + + + + + + + + + + + inbox + + + + + inbox + + + + + + + + + + Route: 2 + + + + + Route: 2 + + + + + + + + + + + heart + + + + + heart + + + + + + + + + + Route: 3 + + + + + Route: 3 + + + + + + + + + + + shopping-music + + + + + shopping-music + + + + + + + + + + Route: 4 + + + + + Route: 4 + + + + + + + + + +`; + +exports[`hides labels in non-shifting bottom navigation 1`] = ` + + + + + + Route: 0 + + + + + + + + + + + + + + magnify + + + + + magnify + + + + + + + + + + + + + + camera + + + + + camera + + + + + + + + + + + + + + inbox + + + + + inbox + + + + + + + + + + + + +`; + +exports[`hides labels in shifting bottom navigation 1`] = ` + + + + + + Route: 0 + + + + + + + + + + + + + + magnify + + + + + magnify + + + + + + + + + + + + + + camera + + + + + camera + + + + + + + + + + + + + + inbox + + + + + inbox + + + + + + + + + + + + +`; + +exports[`renders bottom navigation with getLazy 1`] = ` + + + + + + Route: 0 + + + + + + + Route: 1 + + + + + + + Route: 3 + + + + + + + Route: 4 + + + + + + + + + + + + + + magnify + + + + + magnify + + + + + + + + + + Route: 0 + + + + + Route: 0 + + + + + + + + + + + camera + + + + + camera + + + + + + + + + + Route: 1 + + + + + Route: 1 + + + + + + + + + + + inbox + + + + + inbox + + + + + + + + + + Route: 2 + + + + + Route: 2 + + + + + - - magnify + heart - magnify + heart - Route: 0 + Route: 3 - Route: 0 + Route: 3 @@ -2803,7 +7507,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` ] } > - camera + shopping-music - camera + shopping-music - Route: 1 + Route: 4 - Route: 1 + Route: 4 + + + + +`; + +exports[`renders bottom navigation with scene animation 1`] = ` + + + + + + Route: 0 + + + + + + + + - inbox + magnify - inbox + magnify - - - Route: 2 - - - Route: 2 + Route: 0 @@ -3398,6 +8182,11 @@ exports[`renders bottom navigation with getLazy 1`] = ` "marginBottom": 4, "marginHorizontal": 12, "marginTop": 0, + "transform": [ + { + "translateY": 7, + }, + ], "width": 32, } } @@ -3443,7 +8232,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` ] } > - heart + camera - heart + camera - Route: 3 - - - - - Route: 3 + Route: 1 @@ -3718,6 +8451,11 @@ exports[`renders bottom navigation with getLazy 1`] = ` "marginBottom": 4, "marginHorizontal": 12, "marginTop": 0, + "transform": [ + { + "translateY": 7, + }, + ], "width": 32, } } @@ -3763,7 +8501,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` ] } > - shopping-music + inbox - shopping-music + inbox - - - - - - Route: 4 - + /> + + - Route: 4 + Route: 2 - - - - -`; - -exports[`renders bottom navigation with scene animation 1`] = ` - - - - - - Route: 0 - - - - - - - - - magnify + heart - magnify + heart - Route: 0 + Route: 3 @@ -4488,7 +9039,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` ] } > - camera + shopping-music - camera + shopping-music - Route: 1 + Route: 4 + + + + +`; + +exports[`renders custom background color passed to barStyle property 1`] = ` + + + + + + Route: 0 + + + + + + + + - inbox + magnify - inbox + magnify + + + Route: 0 + + - Route: 2 + Route: 0 @@ -4976,11 +9712,6 @@ exports[`renders bottom navigation with scene animation 1`] = ` "marginBottom": 4, "marginHorizontal": 12, "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], "width": 32, } } @@ -5026,7 +9757,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` ] } > - heart + camera - heart + camera - Route: 3 + Route: 1 + + + + + Route: 1 @@ -5245,11 +10032,6 @@ exports[`renders bottom navigation with scene animation 1`] = ` "marginBottom": 4, "marginHorizontal": 12, "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], "width": 32, } } @@ -5295,7 +10077,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` ] } > - shopping-music + inbox - shopping-music + inbox - Route: 4 + Route: 2 + + + + + Route: 2 diff --git a/src/components/__tests__/__snapshots__/Button.test.tsx.snap b/src/components/__tests__/__snapshots__/Button.test.tsx.snap index 6f255f105a..628087c74b 100644 --- a/src/components/__tests__/__snapshots__/Button.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Button.test.tsx.snap @@ -1,5 +1,2551 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`button icon styles should return correct icon styles for compact contained button 1`] = ` + + + + + + + camera + + + + Compact + contained + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact contained button 2`] = ` + + + + + + + camera + + + + contained + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact contained-tonal button 1`] = ` + + + + + + + camera + + + + Compact + contained-tonal + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact contained-tonal button 2`] = ` + + + + + + + camera + + + + contained-tonal + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact elevated button 1`] = ` + + + + + + + camera + + + + Compact + elevated + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact elevated button 2`] = ` + + + + + + + camera + + + + elevated + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact outlined button 1`] = ` + + + + + + + camera + + + + Compact + outlined + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact outlined button 2`] = ` + + + + + + + camera + + + + outlined + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact text button 1`] = ` + + + + + + + camera + + + + Compact text button + + + + +`; + +exports[`button icon styles should return correct icon styles for text button 1`] = ` + + + + + + + camera + + + + text button + + + + +`; + exports[`renders button with an accessibility hint 1`] = ` Button with custom testID diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap index c41b1b39de..3f84408bb3 100644 --- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -71,6 +71,310 @@ exports[`DataTable.Cell renders data table cell 1`] = ` `; +exports[`DataTable.Cell renders data table cell children without wrapping text component 1`] = ` + + + + + + + + + + + + + + + +`; + +exports[`DataTable.Cell renders data table cell with text content 1`] = ` + + + Table cell + + +`; + exports[`DataTable.Cell renders right aligned data table cell 1`] = ` Send email @@ -374,7 +373,6 @@ exports[`renders FAB.Menu closed 1`] = ` }, ] } - testID="item-1-text" > Set reminder @@ -976,7 +974,6 @@ exports[`renders FAB.Menu not expanded when trigger is not visible 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -1153,7 +1150,6 @@ exports[`renders FAB.Menu not expanded when trigger is not visible 1`] = ` }, ] } - testID="item-1-text" > Set reminder @@ -1755,7 +1751,6 @@ exports[`renders FAB.Menu open 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -1932,7 +1927,6 @@ exports[`renders FAB.Menu open 1`] = ` }, ] } - testID="item-1-text" > Set reminder @@ -2534,7 +2528,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-0-text" > Item 1 @@ -2711,7 +2704,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-1-text" > Item 2 @@ -2888,7 +2880,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-2-text" > Item 3 @@ -3065,7 +3056,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-3-text" > Item 4 @@ -3242,7 +3232,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-4-text" > Item 5 @@ -3419,7 +3408,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-5-text" > Item 6 @@ -4022,7 +4010,6 @@ exports[`renders FAB.Menu with center alignment 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -4199,7 +4186,6 @@ exports[`renders FAB.Menu with center alignment 1`] = ` }, ] } - testID="item-1-text" > Set reminder @@ -5638,7 +5624,6 @@ exports[`renders FAB.Menu with start alignment 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -5815,7 +5800,6 @@ exports[`renders FAB.Menu with start alignment 1`] = ` }, ] } - testID="item-1-text" > Set reminder diff --git a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap index c1e777d963..7534e6f77e 100644 --- a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap @@ -360,6 +360,129 @@ exports[`renders icon button with color 1`] = ` `; +exports[`renders icon button with custom border radius 1`] = ` + + + + + camera + + + + +`; + exports[`renders icon button with size 1`] = ` `; +exports[`renders icon button with small border radius 1`] = ` + + + + + camera + + + + +`; + exports[`renders icon change animated 1`] = ` + + + + First Item + + + Item description + + + + +`; + exports[`renders list item with custom description 1`] = ` `; + +exports[`respects anchorPosition bottom 1`] = ` +<> + + + + + + + + Open menu + + + + + + + + + + + + + + + + + + Undo + + + + + + + + + Redo + + + + + + + + + + +`; + +exports[`uses the default anchorPosition of top 1`] = ` +<> + + + + + + + + Open menu + + + + + + + + + + + + + + + + + + Undo + + + + + + + + + Redo + + + + + + + + + + +`; diff --git a/src/components/__tests__/__snapshots__/Modal.test.tsx.snap b/src/components/__tests__/__snapshots__/Modal.test.tsx.snap new file mode 100644 index 0000000000..5b36c6a27a --- /dev/null +++ b/src/components/__tests__/__snapshots__/Modal.test.tsx.snap @@ -0,0 +1,4576 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Modal by default should receive appropriate top and bottom insets 1`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 1`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 2`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 3`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 1`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 2`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 3`] = ` + + + + + + + + +`; + +exports[`Modal when open if backdrop touched should invoke the onDismiss function immediately 1`] = ` + + + + + + + + +`; + +exports[`Modal when open if backdrop touched should invoke the onDismiss function immediately 2`] = ` + + + + + + + + +`; + +exports[`Modal when open if closed via Android back button invokes onDismiss 1`] = ` + + + + + + + + +`; + +exports[`Modal when open if closed via Android back button invokes onDismiss 2`] = ` + + + + + + + + +`; + +exports[`Modal when open if closed via Android back button invokes onDismiss 3`] = ` + + + + + + + + +`; + +exports[`Modal when open runs the closing animation if visible toggled 1`] = ` + + + + + + + + +`; + +exports[`Modal when open runs the closing animation if visible toggled 2`] = ` + + + + + + + + +`; + +exports[`Modal when open runs the closing animation if visible toggled 3`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 2`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 3`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes again during the open/close animation while opening, back to false (hidden) should keep the modal closed 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from false to true (closed to open) should run fade-in animation on opening 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from false to true (closed to open) should run fade-in animation on opening 2`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from true to false (open to closed) should close even if the dialog is not dismissible 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from true to false (open to closed) should close even if the dialog is not dismissible 2`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from true to false (open to closed) should run fade-out animation on closing 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from true to false (open to closed) should run fade-out animation on closing 2`] = ` + + + + + + + + +`; diff --git a/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap b/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap index b7fea2ca34..d747e9794b 100644 --- a/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap @@ -129,6 +129,52 @@ exports[`renders indeterminate progress bar 1`] = ` `; +exports[`renders progress bar with custom style of filled part 1`] = ` + + + + + +`; + exports[`renders progress bar with specific progress 1`] = ` `; +exports[`renders searchbar in "view" mode 1`] = ` + + + + + + + magnify + + + + + + + + + + + close + + + + + + + +`; + exports[`renders with placeholder 1`] = ` `; + +exports[`should have \`accessibilityState={ checked: true }\` when selected show selected check icon should be shown 1`] = ` + + + + + + + check + + + + Walking + + + + + + + + + Transit + + + + + + + + + Driving + + + + + +`; + +exports[`should not render icon when icon prop is not passed 1`] = ` + + + + + + + + + + + + + + + + +`; + +exports[`should not render icon when icon prop is passed along with label, button is checked, showSelectedCheck is true 1`] = ` + + + + + + + check + + + + Walking + + + + + + + + + + car + + + + Driving + + + + + +`; + +exports[`should render icon when icon prop is passed 1`] = ` + + + + + + + walk + + + + + + + + + + + + car + + + + + + + +`; + +exports[`should render icon when icon prop is passed along with label, button is checked, showSelectedCheck is false 1`] = ` + + + + + + + walk + + + + Walking + + + + + + + + + + car + + + + Driving + + + + + +`; + +exports[`should render icon when icon prop is passed along with label, no matter if button is checked 1`] = ` + + + + + + + walk + + + + Walking + + + + + + + + + + car + + + + Driving + + + + + +`; From 9daf5b0e5d7ec3448ccf4a976e0ecd7869b8658f Mon Sep 17 00:00:00 2001 From: Konrad Rydygier Date: Tue, 8 Sep 2026 14:59:52 +0200 Subject: [PATCH 2/4] chore: update migration doc --- docs/6.x/docs/guides/migration.md | 41 +++++-------------------------- 1 file changed, 6 insertions(+), 35 deletions(-) diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index a401ad8de8..ef71cfff10 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -71,61 +71,32 @@ You can use the component's color prop where available, or override the correspo ### Test IDs -Hardcoded default test IDs have been removed for the components listed below. Many of these components also derive test IDs for their internal parts by appending a suffix to the `testID` prop (e.g. `${testID}-container`). Since `testID` is no longer defaulted to a hardcoded value, none of these derived test IDs are set either unless you pass a `testID` explicitly — so all queries by the IDs below will stop matching: +Hardcoded default test IDs have been removed for the components listed below: - `Appbar.Content`: `appbar-content` - - `appbar-content-title-text` - `Appbar.Header`: `appbar-header` - - `appbar-header-root-layer` - `BottomNavigation`: `bottom-navigation` - - `bottom-navigation-bar` - `BottomNavigation.Bar`: `bottom-navigation-bar` - - `bottom-navigation-bar-content` - - `bottom-navigation-bar-content-wrapper` - `Button`: `button` - - `button-container` - - `button-icon-container` - - `button-text` - `Card`: `card` - - `card-container` - - `card-outline` - `Chip`: `chip` - - `chip-container` - `Drawer.CollapsedItem`: `drawer-collapsed-item` - - `drawer-collapsed-item-outline` - - `drawer-collapsed-item-container` - `FAB`: `floating-action-button` - - `floating-action-button-container` - - `floating-action-button-text` - `FAB.Extended`: `extended-floating-action-button` - - `extended-floating-action-button-container` - - `extended-floating-action-button-text` - `FAB.Menu`: `floating-action-button-menu` - `IconButton`: `icon-button` - - `icon-button-container` - - `icon-button-icon` (and `icon-button-icon-previous` / `icon-button-icon-current` when `animated`) - `Menu`: `menu` - - `menu-view` - - `menu-surface` - `Menu.Item`: `menu-item` - - `menu-item-title` - `Modal`: `modal` - - `modal-backdrop` - - `modal-wrapper` - - `modal-surface` - `ProgressBar`: `progress-bar` - - `progress-bar-fill` - `Searchbar`: `search-bar` - - `search-bar-container` - - `search-bar-icon` - - `search-bar-icon-wrapper` - - `search-bar-clear-icon` - - `search-bar-trailering-icon` - - `search-bar-divider` - `Surface`: `surface` - - `surface-outer-layer` -You can specify a `testID` explicitly to restore both the component's own test ID and all of its derived test IDs above, using the same suffixes. +You can specify a `testID` explicitly to restore each component's own test ID. + +These components used to also derive test IDs for internal, implementation-only elements by appending a suffix to the `testID` prop (e.g. `${testID}-container`, `${testID}-icon`, `${testID}-outline`). Those internal test IDs have been removed entirely — they are no longer set even when you pass `testID` explicitly. Test IDs should only be used to interact with elements or assert content your users can reach, not to reach into a component's internal structure in tests; if you were relying on one of these internal IDs, query by role, label, text, or the component's own `testID`/`ref` instead. + +The one exception is `BottomNavigation`, which still sets `${testID}-bar` on its internal `BottomNavigation.Bar` when you pass `testID`, since there's currently no other way to reference that node from outside the component. ## Components From 68c506e6821b863c7252382e8431c9b43d1d5c83 Mon Sep 17 00:00:00 2001 From: Konrad Rydygier Date: Wed, 9 Sep 2026 14:15:12 +0200 Subject: [PATCH 3/4] fix: add missing testID props and fix testID placement --- docs/6.x/docs/guides/migration.md | 13 +- .../BottomNavigation/BottomNavigation.tsx | 7 +- src/components/Card/Card.tsx | 4 +- src/components/Card/CardTitle.tsx | 7 +- src/components/Chip/Chip.tsx | 6 + .../DataTable/DataTablePagination.tsx | 7 +- src/components/Dialog/Dialog.tsx | 6 + src/components/Drawer/DrawerItem.tsx | 6 + src/components/List/ListImage.tsx | 1 - src/components/Menu/Menu.tsx | 6 + src/components/Modal.tsx | 6 + src/components/Searchbar.tsx | 23 +++- src/components/Snackbar.tsx | 6 + src/components/Tooltip/Tooltip.tsx | 1 - .../TouchableRipple.native.tsx | 1 - .../__tests__/BottomNavigation.test.tsx | 3 + src/components/__tests__/Card/Card.test.tsx | 4 +- src/components/__tests__/ListImage.test.tsx | 45 +++---- src/components/__tests__/Searchbar.test.tsx | 4 +- src/components/__tests__/Tooltip.test.tsx | 56 ++++---- .../__tests__/TouchableRipple.test.tsx | 10 +- .../BottomNavigation.test.tsx.snap | 3 - .../__snapshots__/DataTable.test.tsx.snap | 2 - .../__snapshots__/ListImage.test.tsx.snap | 43 +++++- .../__snapshots__/Searchbar.test.tsx.snap | 1 - .../TouchableRipple.test.tsx.snap | 123 ++++++++++++++++++ 26 files changed, 308 insertions(+), 86 deletions(-) create mode 100644 src/components/__tests__/__snapshots__/TouchableRipple.test.tsx.snap diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index ef71cfff10..a22135f600 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -94,9 +94,18 @@ Hardcoded default test IDs have been removed for the components listed below: You can specify a `testID` explicitly to restore each component's own test ID. -These components used to also derive test IDs for internal, implementation-only elements by appending a suffix to the `testID` prop (e.g. `${testID}-container`, `${testID}-icon`, `${testID}-outline`). Those internal test IDs have been removed entirely — they are no longer set even when you pass `testID` explicitly. Test IDs should only be used to interact with elements or assert content your users can reach, not to reach into a component's internal structure in tests; if you were relying on one of these internal IDs, query by role, label, text, or the component's own `testID`/`ref` instead. +These components used to also derive test IDs for internal, implementation-only elements by appending a suffix to the `testID` prop (e.g. `${testID}-container`, `${testID}-icon`, `${testID}-outline`). They have been removed entirely. -The one exception is `BottomNavigation`, which still sets `${testID}-bar` on its internal `BottomNavigation.Bar` when you pass `testID`, since there's currently no other way to reference that node from outside the component. +If you were relying on internal test IDs, update your tests not to rely on internal implementation details and only interact with elements or assert content your users can reach, e.g.: query by role, label, text etc., or `testID` props accepted by the component. + +Some components now accept explicit `testID` props for their interactable, internal elements: + +- `BottomNavigation`: `barTestID` for the internal `BottomNavigation.Bar`, replacing the previous `${testID}-bar` derivation. +- `Chip`: `closeIconTestID` for the close icon button. +- `Dialog` and `Modal`: `overlayTestID` for the overlay displayed behind the content. +- `Menu`: `overlayTestID` for the overlay displayed behind the menu. +- `Searchbar`: `searchTestID`, `clearTestID`, and `trailingTestID` for the search, clear, and trailing icon buttons. +- `Snackbar`: `iconTestID` for the icon button. ## Components diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index 4556b2bd1b..adecee59a3 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -252,6 +252,10 @@ export type Props = { * TestID used for testing purposes */ testID?: string; + /** + * testID for the underlying `BottomNavigation.Bar`. + */ + barTestID?: string; }; const FAR_FAR_AWAY = Platform.OS === 'web' ? 0 : 9999; @@ -336,6 +340,7 @@ const BottomNavigation = ({ labelMaxFontSizeMultiplier = 1, compact: compactProp, testID, + barTestID, theme: themeOverrides, getLazy = ({ route }: { route: Route }) => route.lazy, }: Props) => { @@ -579,7 +584,7 @@ const BottomNavigation = ({ safeAreaInsets={safeAreaInsets} labelMaxFontSizeMultiplier={labelMaxFontSizeMultiplier} compact={compact} - testID={testID ? `${testID}-bar` : undefined} + testID={barTestID} theme={theme} /> diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 6558807355..712110883b 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -204,7 +204,7 @@ const Card = ({ const borderRadius = theme.shapes.corner.medium; const content = ( - + {React.Children.map(children, (child, index) => React.isValidElement(child) ? React.cloneElement(child as React.ReactElement, { @@ -225,6 +225,7 @@ const Card = ({ style={[{ borderColor }, style]} theme={theme} elevation={elevation} + testID={hasPassedTouchHandler ? undefined : testID} {...rest} > {isMode('outlined') && ( @@ -250,6 +251,7 @@ const Card = ({ onPress={onPress} onPressIn={handlePressIn} onPressOut={handlePressOut} + testID={testID} > {content} diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index 7332c23014..15477d3033 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -102,6 +102,10 @@ export type Props = ViewProps & { * @optional */ theme?: ThemeProp; + /** + * testID to be used on tests. + */ + testID?: string; }; const LEFT_SIZE = 40; @@ -143,6 +147,7 @@ const CardTitle = ({ rightStyle, style, theme: themeOverrides, + testID, }: Props) => { useInternalTheme(themeOverrides); @@ -150,7 +155,7 @@ const CardTitle = ({ const marginBottom = subtitle ? 0 : 2; return ( - + {left ? ( {left({ diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index bb1bd30ed1..5a015fcf8d 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -87,6 +87,10 @@ export type Props = Omit & { * Accessibility label for the close icon. This is read by the screen reader when the user taps the close icon. */ closeIconAccessibilityLabel?: string; + /** + * testID for the close icon button. + */ + closeIconTestID?: string; /** * Function to execute on press. */ @@ -186,6 +190,7 @@ const Chip = ({ 'aria-label': ariaLabel, role = 'button', closeIconAccessibilityLabel = 'Close', + closeIconTestID, onPress, onLongPress, onPressOut, @@ -385,6 +390,7 @@ const Chip = ({ disabled={disabled} role="button" aria-label={closeIconAccessibilityLabel} + testID={closeIconTestID} > {closeIcon ? ( diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index 0e07e5e1c0..0fd2cb7a04 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -292,15 +292,10 @@ const DataTablePagination = ({ {numberOfItemsPerPageList && numberOfItemsPerPage && onItemsPerPageChange && ( - + { const { right, left } = useSafeAreaInsets(); @@ -124,6 +129,7 @@ const Dialog = ({ ]} theme={theme} testID={testID} + overlayTestID={overlayTestID} > {React.Children.toArray(children) .filter((child) => child != null && typeof child !== 'boolean') diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 5924f44e3b..dda753186b 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -64,6 +64,10 @@ export type Props = ViewProps & { * @optional */ theme?: ThemeProp; + /** + * testID to be used on tests. + */ + testID?: string; }; /** @@ -98,6 +102,7 @@ const DrawerItem = ({ right, labelMaxFontSizeMultiplier, hitSlop, + testID, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); @@ -129,6 +134,7 @@ const DrawerItem = ({ aria-label={ariaLabel} theme={theme} hitSlop={hitSlop} + testID={testID} > diff --git a/src/components/List/ListImage.tsx b/src/components/List/ListImage.tsx index 55e2a527db..213d4254c7 100644 --- a/src/components/List/ListImage.tsx +++ b/src/components/List/ListImage.tsx @@ -50,7 +50,6 @@ const ListImage = ({ style={getStyles()} source={source} accessibilityIgnoresInvertColors - testID="list-image" /> ); }; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 0c7d68eae8..96dd4096b7 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -68,6 +68,10 @@ export type Props = { * Accessibility label for the overlay. This is read by the screen reader when the user taps outside the menu. */ overlayAccessibilityLabel?: string; + /** + * testID for the overlay that is displayed behind the menu. + */ + overlayTestID?: string; /** * Content of the `Menu`. */ @@ -186,6 +190,7 @@ const Menu = ({ visible, statusBarHeight, overlayAccessibilityLabel = 'Close menu', + overlayTestID, testID, anchor, onDismiss, @@ -681,6 +686,7 @@ const Menu = ({ onPress={onDismiss} pointerEvents={visible ? 'auto' : 'none'} style={styles.pressableOverlay} + testID={overlayTestID} /> { diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 5cc802df95..fbc1197630 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -36,6 +36,10 @@ export type Props = { * Accessibility label for the overlay. This is read by the screen reader when the user taps outside the modal. */ overlayAccessibilityLabel?: string; + /** + * testID for the overlay that is displayed behind the modal content. + */ + overlayTestID?: string; /** * Determines Whether the modal is visible. */ @@ -127,6 +131,7 @@ function Modal({ dismissableBackButton = dismissable, visible = false, overlayAccessibilityLabel = 'Close modal', + overlayTestID, onDismiss = () => {}, children, contentContainerStyle, @@ -235,6 +240,7 @@ function Modal({ onPress={dismissable ? onDismissCallback : undefined} importantForAccessibility="no" style={[styles.backdrop, backdropStyle, backdropTransitionStyle]} + testID={overlayTestID} /> & { * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ searchAccessibilityLabel?: string; + /** + * testID for the left icon button (see `onIconPress`). + */ + searchTestID?: string; /** * Custom icon for clear button, default will be icon close. It's visible when `loading` is set to `false`. * In v5.x with theme version 3, `clearIcon` is visible only if `right` prop is not defined. @@ -72,6 +76,10 @@ export type Props = Omit & { * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ clearAccessibilityLabel?: string; + /** + * testID for the clear button. + */ + clearTestID?: string; /** * @supported Available in v5.x with theme version 3 * Icon name for the right trailering icon button. @@ -91,6 +99,10 @@ export type Props = Omit & { * Accessibility label for the right trailering icon button. This is read by the screen reader when the user taps the button. */ traileringIconAccessibilityLabel?: string; + /** + * testID for the right trailering icon button. + */ + trailingTestID?: string; /** * @supported Available in v5.x with theme version 3 * Callback which returns a React element to display on the right side. @@ -166,12 +178,15 @@ const Searchbar = ({ iconColor: customIconColor, onIconPress, searchAccessibilityLabel = 'search', + searchTestID, clearIcon, clearAccessibilityLabel = 'clear', + clearTestID, onClearIconPress, traileringIcon, traileringIconColor, traileringIconAccessibilityLabel, + trailingTestID, onTraileringIconPress, right, mode = 'bar', @@ -256,6 +271,7 @@ const Searchbar = ({ } theme={theme} aria-label={searchAccessibilityLabel} + testID={searchTestID} /> {loading ? ( - + ) : ( // Clear icon should be always rendered within Searchbar – it's transparent, // without touch events, when there is no value. It's done to avoid issues @@ -316,6 +329,7 @@ const Searchbar = ({ } role="button" theme={theme} + testID={clearTestID} /> )} @@ -327,6 +341,7 @@ const Searchbar = ({ iconColor={traileringIconColor || colors.onSurfaceVariant} icon={traileringIcon} aria-label={traileringIconAccessibilityLabel} + testID={trailingTestID} /> ) : null} {isBarMode && diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index aa23b4b2a5..fac1f7a049 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -53,6 +53,10 @@ export type Props = Omit & { * Accessibility label for the icon button. This is read by the screen reader when the user taps the button. */ iconAccessibilityLabel?: string; + /** + * testID for the icon button. + */ + iconTestID?: string; /** * The duration for which the Snackbar is shown. */ @@ -150,6 +154,7 @@ const Snackbar = ({ icon, onIconPress, iconAccessibilityLabel = 'Close icon', + iconTestID, duration = DURATION_MEDIUM, onDismiss, children, @@ -339,6 +344,7 @@ const Snackbar = ({ } aria-label={iconAccessibilityLabel} style={styles.icon} + testID={iconTestID} /> ) : null} diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index 144acc3d32..d0dc180115 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -216,7 +216,6 @@ const Tooltip = ({ ...(measurement.measured ? styles.visible : styles.hidden), }, ]} - testID="tooltip-container" > {pressed && rippleEffectEnabled && ( { await render( { await render( { await render( { it('renders with a content style', async () => { await render( - + Content ); - expect(screen.getByTestId('card')).toHaveStyle(styles.contentStyle); + expect(screen.getByText('Content').parent).toHaveStyle(styles.contentStyle); }); it('does not render a disabled accessibility state', async () => { diff --git a/src/components/__tests__/ListImage.test.tsx b/src/components/__tests__/ListImage.test.tsx index 8aa7e9b54d..04796fd73a 100644 --- a/src/components/__tests__/ListImage.test.tsx +++ b/src/components/__tests__/ListImage.test.tsx @@ -2,27 +2,16 @@ import { StyleSheet } from 'react-native'; import { expect, it } from '@jest/globals'; -import { render, screen } from '../../test-utils'; +import { render } from '../../test-utils'; import ListImage from '../List/ListImage'; const styles = StyleSheet.create({ - image: { - width: 56, - height: 56, - }, - video: { - width: 114, - height: 64, - marginLeft: 0, - }, container: { width: 30, height: 56, }, }); -const testID = 'list-image'; - it('renders ListImage with default variant', async () => { const tree = ( await render( @@ -49,23 +38,27 @@ it('renders ListImage with default variant & styles', async () => { }); it('renders ListImage with `image` variant', async () => { - await render( - - ); + const tree = ( + await render( + + ) + ).toJSON(); - expect(screen.getByTestId(testID)).toHaveStyle(styles.image); + expect(tree).toMatchSnapshot(); }); it('renders ListImage with `video` variant', async () => { - await render( - - ); + const tree = ( + await render( + + ) + ).toJSON(); - expect(screen.getByTestId(testID)).toHaveStyle(styles.video); + expect(tree).toMatchSnapshot(); }); diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index 107a912d63..4f4f7ed4c3 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -30,13 +30,13 @@ it('activity indicator snapshot test', async () => { it('renders with ActivityIndicator', async () => { await render(); - expect(screen.getByTestId('activity-indicator')).toBeOnTheScreen(); + expect(screen.getByRole('progressbar')).toBeOnTheScreen(); }); it('renders without ActivityIndicator', async () => { await render(); - expect(screen.queryByTestId('activity-indicator')).not.toBeOnTheScreen(); + expect(screen.queryByRole('progressbar')).not.toBeOnTheScreen(); }); it('renders clear icon with custom color', async () => { diff --git a/src/components/__tests__/Tooltip.test.tsx b/src/components/__tests__/Tooltip.test.tsx index 75b4a18cf7..d6b037e10c 100644 --- a/src/components/__tests__/Tooltip.test.tsx +++ b/src/components/__tests__/Tooltip.test.tsx @@ -193,18 +193,19 @@ describe('Tooltip', () => { describe('When it does not overflow', () => { it('centers the tooltip in the middle of the children component', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup(); await userEvent.longPress(getTrigger(getByText)); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 210, // pageX (220) + (width (80) - TOOLTIP_WIDTH (100)) / 2 = 210 top: 250, // pageY (200) + height (50) }); @@ -214,18 +215,19 @@ describe('Tooltip', () => { describe('When it overflows to left', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageX: 0 }); // Component starting at the starting 0 X coord await userEvent.longPress(getTrigger(getByText)); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 0, // Tooltip renders starting from children's x coord top: 250, }); @@ -235,18 +237,19 @@ describe('Tooltip', () => { describe('When it overflows to right', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageX: 900, width: 150 }); // Component close to the screen limit await userEvent.longPress(getTrigger(getByText)); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 950, // pageX (900) + width (150) - 100 (TOOLTIP_WIDTH) // Tooltip is placed from right to left without going offscreen top: 250, }); @@ -256,18 +259,19 @@ describe('Tooltip', () => { describe('When it overflows to bottom', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageY: 600, height: 50 }); await userEvent.longPress(getTrigger(getByText)); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 210, top: 500, // pageY (600) - TOOLTIP_HEIGHT (100) // Tooltip is placed at the top of the component, }); @@ -388,19 +392,20 @@ describe('Tooltip', () => { describe('When it does not overflow', () => { it('centers the tooltip in the middle of the children component', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup(); await fireEvent(getTrigger(getByText), 'hoverIn'); await runTimers(500); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 210, // pageX (220) + (width (80) - TOOLTIP_WIDTH (100)) / 2 = 210 top: 250, // pageY (200) + height (50) }); @@ -410,19 +415,20 @@ describe('Tooltip', () => { describe('When it overflows to left', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageX: 0 }); // Component starting at the starting 0 X coord await fireEvent(getTrigger(getByText), 'hoverIn'); await runTimers(500); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 0, // Tooltip renders starting from children's x coord top: 250, }); @@ -432,19 +438,20 @@ describe('Tooltip', () => { describe('When it overflows to right', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageX: 900, width: 150 }); // Component close to the screen limit await fireEvent(getTrigger(getByText), 'hoverIn'); await runTimers(500); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 950, // pageX (900) + width (150) - 100 (TOOLTIP_WIDTH) // Tooltip is placed from right to left without going offscreen top: 250, }); @@ -454,19 +461,20 @@ describe('Tooltip', () => { describe('When it overflows to bottom', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageY: 600, height: 50 }); await fireEvent(getTrigger(getByText), 'hoverIn'); await runTimers(500); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 210, top: 500, // pageY (600) - TOOLTIP_HEIGHT (100) // Tooltip is placed at the top of the component, }); diff --git a/src/components/__tests__/TouchableRipple.test.tsx b/src/components/__tests__/TouchableRipple.test.tsx index f3c4cb1168..663d540bec 100644 --- a/src/components/__tests__/TouchableRipple.test.tsx +++ b/src/components/__tests__/TouchableRipple.test.tsx @@ -48,25 +48,23 @@ describe('TouchableRipple', () => { Platform.OS = 'ios'; it('displays the underlay when pressed', async () => { - await render( + const { toJSON } = await render( Press me! ); - const underlay = screen.getByTestId('touchable-ripple-underlay'); - expect(underlay).toBeOnTheScreen(); + expect(toJSON()).toMatchSnapshot(); }); it('renders custom underlay color', async () => { - await render( + const { toJSON } = await render( Press me! ); - const underlay = screen.getByTestId('touchable-ripple-underlay'); - expect(underlay).toHaveStyle({ backgroundColor: 'purple' }); + expect(toJSON()).toMatchSnapshot(); }); }); }); diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index a1466ef10f..22b83abaaa 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -872,7 +872,6 @@ exports[`applies maxTabBarWidth styling if compact prop is truthy 1`] = ` "right": 0, } } - testID="bottom-navigation-bar" > Rows per page diff --git a/src/components/__tests__/__snapshots__/ListImage.test.tsx.snap b/src/components/__tests__/__snapshots__/ListImage.test.tsx.snap index 7aff9530b8..927779644c 100644 --- a/src/components/__tests__/__snapshots__/ListImage.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListImage.test.tsx.snap @@ -1,5 +1,46 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`renders ListImage with \`image\` variant 1`] = ` + +`; + +exports[`renders ListImage with \`video\` variant 1`] = ` + +`; + exports[`renders ListImage with default variant & styles 1`] = ` `; @@ -41,6 +81,5 @@ exports[`renders ListImage with default variant 1`] = ` }, ] } - testID="list-image" /> `; diff --git a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap index b67e4ad9e9..c200e71706 100644 --- a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap @@ -258,7 +258,6 @@ exports[`activity indicator snapshot test 1`] = ` }, ] } - testID="activity-indicator" > + + + Press me! + + +`; + +exports[`TouchableRipple on iOS renders custom underlay color 1`] = ` + + + + Press me! + + +`; From bac9535d597716c825b4ad85bc98abd5e2c9aefc Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 9 Sep 2026 16:13:16 +0200 Subject: [PATCH 4/4] Apply suggestion from @satya164 --- docs/6.x/docs/guides/migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index a22135f600..35a8f74830 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -98,7 +98,7 @@ These components used to also derive test IDs for internal, implementation-only If you were relying on internal test IDs, update your tests not to rely on internal implementation details and only interact with elements or assert content your users can reach, e.g.: query by role, label, text etc., or `testID` props accepted by the component. -Some components now accept explicit `testID` props for their interactable, internal elements: +Some components now accept explicit `testID` props for their interactable elements: - `BottomNavigation`: `barTestID` for the internal `BottomNavigation.Bar`, replacing the previous `${testID}-bar` derivation. - `Chip`: `closeIconTestID` for the close icon button.