Fix testing for MUI breakpoints

Signed-off-by: Philipp Hugenroth <philipph@spotify.com>
This commit is contained in:
Philipp Hugenroth
2021-07-15 18:56:16 +02:00
parent 417aeb2e71
commit 73b7f246e4
3 changed files with 36 additions and 96 deletions
@@ -14,96 +14,30 @@
* limitations under the License.
*/
import { act } from '@testing-library/react';
type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
const defaultBreakpoint: Breakpoint = 'xl';
const queryToBreakpoint = {
'(min-width:1920px)': 'xl',
'(min-width:1280px)': 'lg',
'(min-width:960px)': 'md',
'(min-width:600px)': 'sm',
'(min-width:0px)': 'xs',
} as Record<string, Breakpoint>;
/**
* Converts media query string to Breakpoint.
* Chooses the default breakpoint if no breakpoint is set in the media query.
* This is a mocking method suggested in the Jest Doc's, as it is not implemented in JSDOM yet.
* It can be used to mock values when the MUI `useMediaQuery` hook if it is used in a tested component.
*
* @param query media query string
* @returns Breakpoint
* For issues checkout the documentation:
* https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
*
* If there are any updates from MUI React on testing `useMediaQuery` this mock should be replaced
* https://material-ui.com/components/use-media-query/#testing
*
* @param matchMediaOptions
*/
function toBreakpoint(query: string): Breakpoint {
return queryToBreakpoint[query]
? queryToBreakpoint[query]
: defaultBreakpoint;
}
type Listener = (event: { matches: boolean }) => void;
interface QueryList {
addListener(listener: Listener): void;
removeListener(listener: Listener): void;
addEventListener(listener: Listener): void;
removeEventListener(listener: Listener): void;
matches: boolean;
}
interface Query {
query: string;
queryList: QueryList;
listeners: Set<Listener>;
}
export default function mockBreakpoint(
initialBreakpoint: Breakpoint = defaultBreakpoint,
) {
let currentBreakpoint = initialBreakpoint;
const queries = Array<Query>();
const previousMatchMedia: any = (window as any).matchMedia;
// https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
(window as any).matchMedia = (query: string): QueryList => {
const listeners = new Set<Listener>();
const queryList: QueryList = {
addListener(listener) {
listeners.add(listener);
},
removeListener(listener) {
listeners.delete(listener);
},
addEventListener(listener) {
listeners.add(listener);
},
removeEventListener(listener) {
listeners.delete(listener);
},
matches: toBreakpoint(query) === currentBreakpoint,
};
queries.push({ query, queryList, listeners });
return queryList;
};
return {
set(breakpoint: Breakpoint) {
currentBreakpoint = breakpoint;
act(() => {
queries.forEach(({ query, queryList, listeners }) => {
const matches = toBreakpoint(query) === breakpoint;
queryList.matches = matches;
listeners.forEach(listener => listener({ matches }));
});
});
},
remove() {
(window as any).matchMedia = previousMatchMedia;
},
};
export default function mockBreakpoint({ matches = false }) {
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: matches,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
}
@@ -112,8 +112,6 @@ describe('CatalogPage', () => {
getProfile: () => testProfile,
};
const { set: setBreakpoint } = mockBreakpoint();
const renderWrapped = (children: React.ReactNode) =>
renderWithEffects(
wrapInTestApp(
@@ -248,9 +246,16 @@ describe('CatalogPage', () => {
).resolves.toBeInTheDocument();
});
it('should wrap filter in accordion on smaller screens', async () => {
setBreakpoint('sm');
const { findByText } = await renderWrapped(<CatalogPage />);
await expect(findByText(/Filters/)).resolves.toBeInTheDocument();
it('should wrap filter in drawer on smaller screens', async () => {
mockBreakpoint({ matches: true });
const { findByText, getByTestId } = await renderWrapped(<CatalogPage />);
expect(getByTestId('entity-filters-drawer')).toBeInTheDocument();
await expect(findByText('Filters')).resolves.toBeInTheDocument();
});
it('should wrap filter in grid on larger screens', async () => {
mockBreakpoint({ matches: false });
const { getByTestId } = await renderWrapped(<CatalogPage />);
expect(getByTestId('entity-filters-grid')).toBeInTheDocument();
});
});
@@ -27,6 +27,7 @@ export const FilterContainer = ({ children }: React.PropsWithChildren<{}>) => {
return isMidSizeScreen ? (
<Drawer
data-testid="entity-filters-drawer"
open={showFiltersDrawer}
onClose={() => {
toggleFiltersDrawer(false);
@@ -43,7 +44,7 @@ export const FilterContainer = ({ children }: React.PropsWithChildren<{}>) => {
<Box m={2}>{children}</Box>
</Drawer>
) : (
<Grid item lg={2}>
<Grid data-testid="entity-filters-grid" item lg={2}>
{children}
</Grid>
);