Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Components: add tests for Navigator* #35163

Merged
merged 8 commits into from
Sep 28, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 218 additions & 0 deletions packages/components/src/navigator/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/**
* External dependencies
*/
import { render, screen, fireEvent } from '@testing-library/react';

/**
* Internal dependencies
*/
import { Navigator, NavigatorScreen, useNavigator } from '../';

jest.mock( 'framer-motion', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary to "ignore" the transitions in the tests

const actual = jest.requireActual( 'framer-motion' );
return {
__esModule: true,
...actual,
AnimatePresence: ( { children } ) => <div>{ children }</div>,
motion: {
...actual.motion,
div: require( 'react' ).forwardRef( ( { children }, ref ) => (
<div ref={ ref }>{ children }</div>
) ),
},
};
} );

const PATHS = {
HOME: '/',
CHILD: '/child',
NOT_FOUND: '/not-found',
};

function NavigatorButton( { path, isBack = false, onClick, ...props } ) {
const navigator = useNavigator();
return (
<button
onClick={ () => {
navigator.push( path, { isBack } );
// Used to spy on the values passed to `navigator.push`
onClick?.( { path, isBack } );
} }
{ ...props }
/>
);
}

const MyNavigation = ( {
initialPath = PATHS.HOME,
onNavigatorButtonClick,
} ) => (
<Navigator initialPath={ initialPath }>
<NavigatorScreen path={ PATHS.HOME }>
<p>This is the home screen.</p>
<NavigatorButton
path={ PATHS.CHILD }
onClick={ onNavigatorButtonClick }
>
Navigate to child screen.
</NavigatorButton>
<NavigatorButton
path={ PATHS.NOT_FOUND }
onClick={ onNavigatorButtonClick }
>
Navigate to non-existing screen.
</NavigatorButton>
</NavigatorScreen>

<NavigatorScreen path={ PATHS.CHILD }>
<p>This is the child screen.</p>
<NavigatorButton
path={ PATHS.HOME }
isBack
onClick={ onNavigatorButtonClick }
>
Go back
</NavigatorButton>
</NavigatorScreen>

{ /* A `NavigatorScreen` with `path={ PATHS.NOT_FOUND }` is purposefully not included */ }
</Navigator>
);

const getNavigationScreenByText = ( text, { throwIfNotFound = true } = {} ) => {
const fnName = throwIfNotFound ? 'getByText' : 'queryByText';
return screen[ fnName ]( text );
};
const getHomeScreen = ( { throwIfNotFound } = {} ) =>
getNavigationScreenByText( 'This is the home screen.', {
throwIfNotFound,
} );
const getChildScreen = ( { throwIfNotFound } = {} ) =>
getNavigationScreenByText( 'This is the child screen.', {
throwIfNotFound,
} );

const getNavigationButtonByText = ( text, { throwIfNotFound = true } = {} ) => {
const fnName = throwIfNotFound ? 'getByRole' : 'queryByRole';
return screen[ fnName ]( 'button', { name: text } );
};
const getToNonExistingScreenButton = ( { throwIfNotFound } = {} ) =>
getNavigationButtonByText( 'Navigate to non-existing screen.', {
throwIfNotFound,
} );
const getToChildScreenButton = ( { throwIfNotFound } = {} ) =>
getNavigationButtonByText( 'Navigate to child screen.', {
throwIfNotFound,
} );
const getToHomeScreenButton = ( { throwIfNotFound } = {} ) =>
getNavigationButtonByText( 'Go back', {
throwIfNotFound,
} );

describe( 'Navigator', () => {
it( 'should render', () => {
render( <MyNavigation /> );

expect( getHomeScreen() ).toBeInTheDocument();
expect(
getChildScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();
} );

it( 'should show a different screen on the first render depending on the value of `initialPath`', () => {
render( <MyNavigation initialPath={ PATHS.CHILD } /> );

expect(
getHomeScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();
expect( getChildScreen() ).toBeInTheDocument();
} );

it( 'should ignore changes to `initialPath` after the first render', () => {
const { rerender } = render( <MyNavigation /> );

expect( getHomeScreen() ).toBeInTheDocument();
expect(
getChildScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();

rerender( <MyNavigation initialPath={ PATHS.CHILD } /> );

expect( getHomeScreen() ).toBeInTheDocument();
expect(
getChildScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();
} );

it( 'should not rended anything if the `initialPath` does not match any available screen', () => {
render( <MyNavigation initialPath={ PATHS.NOT_FOUND } /> );

expect(
getHomeScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();
expect(
getChildScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();
} );

it( 'should navigate across screens', () => {
const spy = jest.fn();

render( <MyNavigation onNavigatorButtonClick={ spy } /> );

expect( getToChildScreenButton() ).toBeInTheDocument();

// Navigate to child screen
fireEvent.click( getToChildScreenButton() );

expect(
getHomeScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();
expect( getChildScreen() ).toBeInTheDocument();
expect( getToHomeScreenButton() ).toBeInTheDocument();

// Navigate back to home screen
fireEvent.click( getToHomeScreenButton() );

expect(
getChildScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();
expect( getHomeScreen() ).toBeInTheDocument();

// Check the values passed to `navigator.push()`
expect( spy ).toHaveBeenCalledTimes( 2 );
expect( spy ).toHaveBeenNthCalledWith( 1, {
path: PATHS.CHILD,
isBack: false,
} );
expect( spy ).toHaveBeenNthCalledWith( 2, {
path: PATHS.HOME,
isBack: true,
} );
} );

it( 'should not rended anything if the path does not match any available screen', () => {
const spy = jest.fn();

render( <MyNavigation onNavigatorButtonClick={ spy } /> );

expect( getToChildScreenButton() ).toBeInTheDocument();

// Attempt to navigate to non-existing screen. No screens get rendered.
fireEvent.click( getToNonExistingScreenButton() );

expect(
getHomeScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();
expect(
getChildScreen( { throwIfNotFound: false } )
).not.toBeInTheDocument();

// Check the values passed to `navigator.push()`
expect( spy ).toHaveBeenCalledTimes( 1 );
expect( spy ).toHaveBeenNthCalledWith( 1, {
path: PATHS.NOT_FOUND,
isBack: false,
} );
} );
} );