* Prep work to sync mobile search in #10424 * Added a comment about the custom search event. * Refactor to fix already defined error caused by const and InstantClick. * Moved <SearchFormSync /> out of the pack file. * Fixed broken tests. * Renamed getInitialSearchTerm utility function to getSearchTermFromUrl * Added some API docs to getSearchTermFromUrl * Fixed a typo. * Added some more API docs. * Switched to a custom event so a custom payload can be passed. * Added some API docs. * Fixed bad destructuring statement. * wip for some more tests. * Refactored getSearchTermFromUrl to use URLSearchParams * lazy loaded state for search term initial value. * test broken still. Need to think on it. * filterXSS is now set up in testSetup.js * Fixed tests. * Added the @utilities alias to Storybooks webpack config. * Almost 100% coverage with two useful tests. * Added some comments explaining document.body usage in the tests. * Fixed issue where search attemps to get results on first load. * Small refactor for setting the window.location in tests. * Clarified an explanation in a comment.
126 lines
4.8 KiB
JavaScript
126 lines
4.8 KiB
JavaScript
import { h } from 'preact';
|
|
import { fireEvent, render } from '@testing-library/preact';
|
|
import { SearchFormSync } from '../SearchFormSync';
|
|
|
|
function setWindowLocation(url = '') {
|
|
delete window.location; // Inspired from https://www.benmvp.com/blog/mocking-window-location-methods-jest-jsdom/
|
|
window.location = new URL(url);
|
|
}
|
|
|
|
// a11y tests are not required for this component as it's job is to provide data to other components.
|
|
// There is nothing UI related about this component.
|
|
describe('<SearchFormSync />', () => {
|
|
// For some reason when document.body is used for renders, we need to clear out the rendered markup in it.
|
|
// My guess is that Preact testing library handles this internally when using the default container to render in.
|
|
|
|
beforeEach(() => {
|
|
setWindowLocation(`https://locahost:3000/`);
|
|
|
|
// The body is being cleared out because we are using it as the root element for the tests.
|
|
// Typically using the document.body as the root for rendering of components in tests is not necessary,
|
|
// but in the case of this component, it renders a portal, and this seemed to be the only way to get these
|
|
// tests to render portals.
|
|
document.body.innerHTML = '';
|
|
|
|
global.InstantClick = jest.fn(() => ({
|
|
on: jest.fn(),
|
|
off: jest.fn(),
|
|
preload: jest.fn(),
|
|
display: jest.fn(),
|
|
}))();
|
|
});
|
|
|
|
it('should synchronize search forms', async () => {
|
|
const { findByLabelText, findAllByLabelText } = render(<SearchFormSync />, {
|
|
container: document.body,
|
|
});
|
|
|
|
// Only one input is rendered at this point because the synchSearchForms custom event is what
|
|
// tells us that there is a new search form to sync with the existing one.
|
|
const searchInput = await findByLabelText('search');
|
|
|
|
// Because window.location has no search term in it's URL
|
|
expect(searchInput.value).toEqual('');
|
|
|
|
// https://www.theatlantic.com/technology/archive/2012/09/here-it-is-the-best-word-ever/262348/
|
|
const searchTerm = 'diphthong';
|
|
|
|
// simulates a search result returned which contains the server side rendered search form for mobile only.
|
|
setWindowLocation(`https://locahost:3000/search?q=${searchTerm}`);
|
|
|
|
// This part of the DOM would be rendered in the search results from the server side.
|
|
// See search.html.erb.
|
|
document.body.innerHTML =
|
|
'<div id="mobile-search-container"><form></form></div>';
|
|
|
|
fireEvent(
|
|
window,
|
|
new CustomEvent('syncSearchForms', {
|
|
detail: { querystring: window.location.search },
|
|
}),
|
|
);
|
|
|
|
const [desktopSearch, mobileSearch] = await findAllByLabelText('search');
|
|
|
|
expect(desktopSearch.value).toEqual(searchTerm);
|
|
expect(mobileSearch.value).toEqual(searchTerm);
|
|
});
|
|
|
|
it('should synchronize search forms on a subsequent search', async () => {
|
|
const { findByLabelText, findAllByLabelText } = render(<SearchFormSync />, {
|
|
container: document.body,
|
|
});
|
|
|
|
// Only one input is rendered at this point because the synchSearchForms custom event is what
|
|
// tells us that there is a new search form to sync with the existing one.
|
|
const searchInput = await findByLabelText('search');
|
|
|
|
// Because window.location has no search term in it's URL
|
|
expect(searchInput.value).toEqual('');
|
|
|
|
// https://www.theatlantic.com/technology/archive/2012/09/here-it-is-the-best-word-ever/262348/
|
|
const searchTerm = 'diphthong';
|
|
|
|
// simulates a search result returned which contains the server side rendered search form for mobile only.
|
|
setWindowLocation(`https://locahost:3000/search?q=${searchTerm}`);
|
|
|
|
// This part of the DOM would be rendered in the search results from the server side.
|
|
// See search.html.erb.
|
|
document.body.innerHTML =
|
|
'<div id="mobile-search-container"><form></form></div>';
|
|
|
|
fireEvent(
|
|
window,
|
|
new CustomEvent('syncSearchForms', {
|
|
detail: { querystring: window.location.search },
|
|
}),
|
|
);
|
|
|
|
let [desktopSearch, mobileSearch] = await findAllByLabelText('search');
|
|
|
|
expect(desktopSearch.value).toEqual(searchTerm);
|
|
expect(mobileSearch.value).toEqual(searchTerm);
|
|
|
|
const searchTerm2 = 'diphthong2';
|
|
|
|
// simulates a search result returned which contains the server side rendered search form for mobile only.
|
|
setWindowLocation(`https://locahost:3000/search?q=${searchTerm2}`);
|
|
|
|
// This part of the DOM would be rendered in the search results from the server side.
|
|
// See search.html.erb.
|
|
document.body.innerHTML =
|
|
'<div id="mobile-search-container"><form></form></div>';
|
|
|
|
fireEvent(
|
|
window,
|
|
new CustomEvent('syncSearchForms', {
|
|
detail: { querystring: window.location.search },
|
|
}),
|
|
);
|
|
|
|
[desktopSearch, mobileSearch] = await findAllByLabelText('search');
|
|
|
|
expect(desktopSearch.value).toEqual(searchTerm2);
|
|
expect(mobileSearch.value).toEqual(searchTerm2);
|
|
});
|
|
});
|