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}
/>];
Solution: I was able to us Drews answer to mock my router like this
const TestHomeComponent = () => <UserContext.Provider value={loggedInUser}><TestableAxpSubmissionsWebSearchSubmitterProfile {...props1} /></UserContext.Provider>;
const TestBasicSearchComponent = () => <UserContext.Provider value={loggedInUser}><TestableAxpSubmissionsWebSearchSubmitterProfile {...props3} /></UserContext.Provider>;
const TestCreateComponent = () => <UserContext.Provider value={loggedInUser}><TestableAxpSubmissionsWebSearchSubmitterProfile {...props2} /></UserContext.Provider>;
const TestAdvSearchComponent = () => <UserContext.Provider value={loggedInUser}><TestableAxpSubmissionsWebSearchSubmitterProfile {...props4} /></UserContext.Provider>;
const { container, debug } = render(
<Router initialEntries={['/submitter-profile']} history={createMemoryHistory()}>
<UserContext.Provider value={loggedInUser}>
<Route path="/submitter-profile" component={TestHomeComponent} />
<Route path="/submitter-profile/basic-search" component={TestBasicSearchComponent} />
<Route path="/submitter-profile/create" component={TestCreateComponent} />
<Route path="/submitter-profile/advanced-search" component={TestAdvSearchComponent} />
<Route path="/" component={TestHomeComponent} />
</UserContext.Provider>
</Router>
);
The React-Router v3 Route component doesn't have an element
prop, it has a component
prop. Update the test code to use the component
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:
describe('Navigation tests', () => {
it('invalid submitterId on save for edit profile', async () => {
const memoryHistory = createMemoryHistory();
const { container } = render(
<Router
initialEntries={['/submitter-profile']}
history={memoryHistory}
>
<UserContext.Provider value={loggedInUser}>
<Route
path="/submitter-profile/basic-search"
component={TestableAxpSubmissionsWebSearchSubmitterProfile}
/>
<Route
path="/submitter-profile/create"
component={TestableAxpSubmissionsWebSearchSubmitterProfile}
/>
<Route
path="/submitter-profile/advanced-search"
component={TestableAxpSubmissionsWebSearchSubmitterProfile}
/>
<Route
path="/submitter-profile"
component={TestableAxpSubmissionsWebSearchSubmitterProfile}
/>
<Route
path="/"
component={TestableAxpSubmissionsWebSearchSubmitterProfile}
/>
</UserContext.Provider>
</Router>
);
...
});
});