I am trying to test the below scenario with React Router V3. Based on the step prop, I set some state variables and then render the component. The correct screen is then displayed based on the props passed to
const TestComponent = ({
languageData,
locale,
route: { step },
}) => {
const [showTiles, setShowTiles] = useState(true);
const [showAdsSearch, setShowAdsSearch] = useState(false);
const [showBasicSearch, setShowBasicSearch] = useState(false);
const [showSubmitterFields, setShowSubmitterFields] = useState(false);
const viewHome = () => {
setShowBasicSearch(false);
setShowSubmitterFields(false);
setShowAdsSearch(false);
setShowTiles(true);
};
const viewBasicSearch = () => {
setShowBasicSearch(true);
setShowSubmitterFields(false);
setShowAdsSearch(false);
setShowTiles(false);
};
const viewAdSearch = () => {
setShowBasicSearch(false);
setShowSubmitterFields(false);
setShowAdsSearch(true);
setShowTiles(false);
};
const viewCreateSubmitterProfileFields = () => {
setShowBasicSearch(false);
setShowSubmitterFields(true);
setShowAdsSearch(false);
setShowTiles(false);
};
const stepMap = new Map([
[1, viewHome],
[2, viewCreateSubmitterProfileFields],
[3, viewBasicSearch],
[4, viewAdSearch],
]);
useEffect(() => {
const currentState = stepMap.get(step) || viewHome;
currentState();
}, [step]);
if (Object.entries(languageData).length === 0) {
return <ProgressCircle />;
}
return (
<IntlProvider locale={locale} messages={languageData}>
<UserContext.Consumer>
{(user) => (Object.keys(user).length > 0 ? (
<HomeView
className={styles.grayBg}
showAdsSearch={showAdsSearch}
setShowAdsSearch={setShowAdsSearch}
showSubmitterFields={showSubmitterFields}
setShowSubmitterFields={setShowSubmitterFields}
showBasicSearch={showBasicSearch}
setShowBasicSearch={setShowBasicSearch}
showTiles={showTiles}
setShowTiles={setShowTiles}
/>
) : <div>NO USER CONTEXT SET IN ROOT MODULE</div>)}
</UserContext.Consumer>
</IntlProvider>
);
};
The home component:
export function Home({
showTiles,
setShowTiles,
showBasicSearch,
setShowBasicSearch,
showAdsSearch,
setShowAdsSearch,
showSubmitterFields,
setShowSubmitterFields,
}) {
return (
<React.Fragment>
<HeaderSection
setShowBasicSearch={setShowBasicSearch}
showBasicSearch={showBasicSearch}
setShowTiles={setShowTiles}
setShowAdsSearch={setShowAdsSearch}
setSubmitterIdToView={setSubmitterIdToView}
setSubmitterName={setSubmitterName}
setShowSubmitterFields={setShowSubmitterFields}
/>
<div id="home" className={styles.grayBgForHome}>
{showTiles && (
<TilesView
setShowBasicSearch={setShowBasicSearch}
setShowTiles={setShowTiles}
setShowAdsSearch={setShowAdsSearch}
showAdsSearch={showAdsSearch}
showSubmitterFields={showSubmitterFields}
setShowSubmitterFields={setShowSubmitterFields}
/>
)}
{showBasicSearch && (
<React.Fragment>
<h1 className={styles.headingText}>
Search Submitter Profile
</h1>
<ConnectedBasicSearch/>
</React.Fragment>
)}
{showAdsSearch && (
<React.Fragment>
<h1 className={styles.headingText}>
Advanced Search Submitter Profile
</h1>
<ConnectedAdvancedSearch/>
</React.Fragment>
)}
{showSubmitterFields && (
<React.Fragment>
<h1 className={styles.headingText}>Create Submitter Profile</h1>
<ConnectedSubmitterProfile/>
</React.Fragment>
)}
</div>
<FooterSection />
</React.Fragment>
);
}
In my test, I have the below. What I am trying to do is render Home with step = 1 which should display the Tiles component. Then I click on the #basicSearchTile which is a
Link to /submitter-profile/basic-search and want to check for certain elements being displayed:
async function clickElement(container, selector) {
const user = userEvent.setup();
const element = container.querySelector(selector);
await user.click(element);
}
async function checkDisplayed(container, selector, shouldBeDisplayed) {
const element = container.querySelector(selector);
if (shouldBeDisplayed) {
expect(element).toBeInTheDocument();
} else {
expect(element).not.toBeInTheDocument();
}
}
const props1 = {
languageData: { intlKeyMock: 'intlValueMock' },
locale: 'localeMock',
route: { step: 1 },
};
const props2 = {
languageData: { intlKeyMock: 'intlValueMock' },
locale: 'localeMock',
route: { step: 2 },
};
const props3 = {
languageData: { intlKeyMock: 'intlValueMock' },
locale: 'localeMock',
route: { step: 3 },
};
const props4 = {
languageData: { intlKeyMock: 'intlValueMock' },
locale: 'localeMock',
route: { step: 4 },
};
describe('Navigation tests', () => {
it('invalid submitterId on save for edit profile', async () => {
const { container } = render(
<Router initialEntries={['/submitter-profile']} history={createMemoryHistory()}>
<UserContext.Provider value={loggedInUser}>
<Route path="/" element={<TestableAxpSubmissionsWebSearchSubmitterProfile {...props1} />} />
<Route path="/submitter-profile" element={<TestableAxpSubmissionsWebSearchSubmitterProfile {...props1} />} />
<Route path="/submitter-profile/basic-search" element={<TestableAxpSubmissionsWebSearchSubmitterProfile {...props3} />} />
<Route path="/submitter-profile/create" element={<TestableAxpSubmissionsWebSearchSubmitterProfile {...props2} />} />
<Route path="/submitter-profile/advanced-search" element={<TestableAxpSubmissionsWebSearchSubmitterProfile {...props4} />} />
</UserContext.Provider>
</Router>
);
screen.debug();
await clickElement(container, '#basicSearchTile');
await clickElement(container, '#search-button');
await clickElement(container, '#resultTableRowsubmitterId123');
await clickElement(container, '#editToggle');
await clickElement(container, '#saveButton');
screen.debug();
await checkDisplayed(container, '#responseModalMsg', true);
await checkTextValue(container, '#responseModal', 'Must enter a valid value for Submitter ID.');
await clickElement(container, '#responseModal');
await checkDisplayed(container, '#responseModalMsg', false);
});
});
However, whenever i use render, the screen.debug() calls are returning an empty body .
<body>
<div />
</body>
Also I have added the route definitions in case they are helpful.
Routing in the app is working great. it is just the tests that I cannot get to work
const Routes = [
<Route
key="submitter-profile-route"
path="/submitter-profile"
moduleName="axp-submissions-web-search-submitter-profile"
step={1}
/>,
<Route
key="create-submitter-profile-route"
path="/submitter-profile/create"
moduleName="axp-submissions-web-search-submitter-profile"
step={2}
/>,
<Route
key="basic-search-submitter-profile-route"
path="/submitter-profile/basic-search"
moduleName="axp-submissions-web-search-submitter-profile"
step={3}
/>,
<Route
key="advanced-search-submitter-profile-route"
path="/submitter-profile/advanced-search"
moduleName="axp-submissions-web-search-submitter-profile"
step={4}
/>];
2
Answers
In first, it seems correctly set up. But in your test, you use
render
from@testing-library/react
to render component but this doesn’t support react-router v3.I’m just thinking here that you need a Router component to navigate in your tests.
I use
createMemoryHistory
fromhistory
here, because we need to create a history object first then pass it to theRouter
.If can’t find
createMemoryHistory
or nohistory
yet, install it by doingnpm install --save history
.Then, create a router like the below sample to allow you simulate navigation in your tests.
The React-Router v3 Route component doesn’t have an
element
prop, it has acomponent
prop. Update the test code to use thecomponent
prop and pass a reference to the component instead of JSX.Be sure to also specify the routes in the inverse order of path specificity, e.g. render more specific paths before less specific paths.
Example: