feat: support matches per breakpoints
Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Adds the ability to mock a media query per break point and to change the active break point during a test. Usage example:
|
||||
|
||||
```ts
|
||||
const { set } = mockBreakpoint({
|
||||
initialBreakpoint: 'md',
|
||||
queryBreakpointMap: {
|
||||
'(min-width:1500px)': 'xl',
|
||||
'(min-width:1000px)': 'lg',
|
||||
'(min-width:700px)': 'md',
|
||||
'(min-width:400px)': 'sm',
|
||||
'(min-width:0px)': 'xs',
|
||||
},
|
||||
});
|
||||
// assertions for when the active break point is "md"
|
||||
set('lg');
|
||||
// assertions for when the active break point is "lg"
|
||||
```
|
||||
@@ -65,6 +65,7 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.61",
|
||||
"@react-hookz/web": "^24.0.0",
|
||||
"@testing-library/react": "^16.0.0",
|
||||
"@types/react-sparklines": "^1.7.0",
|
||||
"ansi-regex": "^6.0.1",
|
||||
"classnames": "^2.2.6",
|
||||
@@ -98,7 +99,6 @@
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/dom": "^10.0.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^16.0.0",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/ansi-regex": "^5.0.0",
|
||||
"@types/classnames": "^2.2.9",
|
||||
|
||||
@@ -3,8 +3,19 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
||||
|
||||
// @public
|
||||
export function mockBreakpoint(options: { matches: boolean }): void;
|
||||
|
||||
// @public
|
||||
export function mockBreakpoint(options: {
|
||||
initialBreakpoint?: Breakpoint;
|
||||
queryBreakpointMap?: Record<string, Breakpoint>;
|
||||
}): {
|
||||
set(value: string): void;
|
||||
remove(): void;
|
||||
};
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
||||
import { mockBreakpoint } from './testUtils';
|
||||
|
||||
describe('mockBreakpoint', () => {
|
||||
const originalMatchMedia = window.matchMedia;
|
||||
|
||||
afterAll(() => {
|
||||
window.matchMedia = originalMatchMedia;
|
||||
});
|
||||
|
||||
it('should remove the mock', () => {
|
||||
const { remove } = mockBreakpoint({ initialBreakpoint: 'md' });
|
||||
expect(originalMatchMedia).not.toBe(window.matchMedia);
|
||||
remove();
|
||||
expect(window.matchMedia).toBe(originalMatchMedia);
|
||||
});
|
||||
|
||||
it('should mock matchMedia with initialBreakpoint', () => {
|
||||
const { set } = mockBreakpoint({ initialBreakpoint: 'md' });
|
||||
|
||||
expect(window.matchMedia('(min-width:960px)').matches).toBe(true);
|
||||
expect(window.matchMedia('(min-width:1280px)').matches).toBe(false);
|
||||
|
||||
set('lg');
|
||||
expect(window.matchMedia('(min-width:1280px)').matches).toBe(true);
|
||||
});
|
||||
|
||||
it('should mock matchMedia with matches option', () => {
|
||||
mockBreakpoint({ matches: true });
|
||||
|
||||
expect(window.matchMedia('(min-width:1920px)').matches).toBe(true);
|
||||
expect(window.matchMedia('(min-width:1280px)').matches).toBe(true);
|
||||
expect(window.matchMedia('(min-width:960px)').matches).toBe(true);
|
||||
expect(window.matchMedia('(min-width:600px)').matches).toBe(true);
|
||||
expect(window.matchMedia('(min-width:0px)').matches).toBe(true);
|
||||
});
|
||||
|
||||
it('should mock matchMedia with custom queryBreakpointMap', () => {
|
||||
const customMap: Record<string, Breakpoint> = {
|
||||
'(min-width:1500px)': 'xl',
|
||||
'(min-width:1000px)': 'lg',
|
||||
'(min-width:700px)': 'md',
|
||||
'(min-width:400px)': 'sm',
|
||||
'(min-width:0px)': 'xs',
|
||||
};
|
||||
const { set } = mockBreakpoint({
|
||||
initialBreakpoint: 'sm',
|
||||
queryBreakpointMap: customMap,
|
||||
});
|
||||
|
||||
expect(window.matchMedia('(min-width:400px)').matches).toBe(true);
|
||||
expect(window.matchMedia('(min-width:700px)').matches).toBe(false);
|
||||
|
||||
set('md');
|
||||
expect(window.matchMedia('(min-width:700px)').matches).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -14,6 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
||||
import { act } from '@testing-library/react';
|
||||
|
||||
const originalMatchMedia = window.matchMedia;
|
||||
|
||||
/**
|
||||
* This is a mocking method suggested in the Jest docs, as it is not implemented in JSDOM yet.
|
||||
* It can be used to mock values for the Material UI `useMediaQuery` hook if it is used in a tested component.
|
||||
@@ -25,19 +30,137 @@
|
||||
* https://mui.com/material-ui/react-use-media-query/#testing
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* Match with any media query:
|
||||
* ```ts
|
||||
* mockBreakpoint({ matches: true });
|
||||
* ```
|
||||
*/
|
||||
export function mockBreakpoint(options: { matches: boolean }) {
|
||||
export function mockBreakpoint(options: { matches: boolean }): void;
|
||||
/**
|
||||
* This is a mocking method suggested in the Jest docs, as it is not implemented in JSDOM yet.
|
||||
* It can be used to mock values for the Material UI `useMediaQuery` hook if it is used in a tested component.
|
||||
*
|
||||
* 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 Material UI React on testing `useMediaQuery` this mock should be replaced
|
||||
* https://mui.com/material-ui/react-use-media-query/#testing
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* Set the initial breakpoint:
|
||||
* ```ts
|
||||
* mockBreakpoint({ initialBreakpoint: 'md' });
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* Map media queries to breakpoints:
|
||||
* ```ts
|
||||
* mockBreakpoint({ queryBreakpointMap: { '(min-width:1500px)': 'xl', '(min-width:1000px)': 'lg', '(min-width:700px)': 'md', '(min-width:400px)': 'sm', '(min-width:0px)': 'xs', } });
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* Change the breakpoint during the test:
|
||||
* ```ts
|
||||
* const { set } = mockBreakpoint({ initialBreakpoint: 'md' });
|
||||
* set('lg');
|
||||
* ```
|
||||
**/
|
||||
export function mockBreakpoint(options: {
|
||||
/** Defaults to 'lg' */
|
||||
initialBreakpoint?: Breakpoint;
|
||||
/** Defaults to \{ '(min-width:1920px)': 'xl', '(min-width:1280px)': 'lg', '(min-width:960px)': 'md', '(min-width:600px)': 'sm', '(min-width:0px)': 'xs' \} */
|
||||
queryBreakpointMap?: Record<string, Breakpoint>;
|
||||
}): {
|
||||
set(value: string): void;
|
||||
remove(): void;
|
||||
};
|
||||
export function mockBreakpoint(
|
||||
options:
|
||||
| {
|
||||
matches: boolean;
|
||||
}
|
||||
| {
|
||||
initialBreakpoint?: Breakpoint;
|
||||
queryBreakpointMap?: Record<string, Breakpoint>;
|
||||
},
|
||||
): {
|
||||
set(value: string): void;
|
||||
remove(): void;
|
||||
} {
|
||||
const mediaQueries: {
|
||||
mediaQueryString: string;
|
||||
mediaQueryList: { matches: boolean };
|
||||
mediaQueryListeners: Set<(event: { matches: boolean }) => void>;
|
||||
}[] = [];
|
||||
let breakpoint: Breakpoint = 'lg';
|
||||
if ('initialBreakpoint' in options && options.initialBreakpoint) {
|
||||
breakpoint = options.initialBreakpoint;
|
||||
}
|
||||
const breakpoints: Record<string, Breakpoint> =
|
||||
'queryBreakpointMap' in options &&
|
||||
typeof options.queryBreakpointMap === 'object'
|
||||
? options.queryBreakpointMap
|
||||
: {
|
||||
'(min-width:1920px)': 'xl',
|
||||
'(min-width:1280px)': 'lg',
|
||||
'(min-width:960px)': 'md',
|
||||
'(min-width:600px)': 'sm',
|
||||
'(min-width:0px)': 'xs',
|
||||
};
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation(query => ({
|
||||
matches: options.matches ?? false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // deprecated
|
||||
removeListener: jest.fn(), // deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
value: jest.fn().mockImplementation(mediaQueryString => {
|
||||
const mediaQueryListeners = new Set<
|
||||
(event: { matches: boolean }) => void
|
||||
>();
|
||||
const mediaQueryList = {
|
||||
matches:
|
||||
'matches' in options
|
||||
? options.matches
|
||||
: breakpoints[mediaQueryString] === breakpoint,
|
||||
media: mediaQueryString,
|
||||
onchange: null,
|
||||
addListener: jest.fn(listener => {
|
||||
mediaQueryListeners.add(listener);
|
||||
}),
|
||||
removeListener: jest.fn(listener => {
|
||||
mediaQueryListeners.delete(listener);
|
||||
}),
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
};
|
||||
mediaQueries.push({
|
||||
mediaQueryString,
|
||||
mediaQueryList,
|
||||
mediaQueryListeners,
|
||||
});
|
||||
return mediaQueryList;
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
set(newBreakpoint: Breakpoint) {
|
||||
breakpoint = newBreakpoint;
|
||||
mediaQueries.forEach(
|
||||
({ mediaQueryString, mediaQueryList, mediaQueryListeners }) => {
|
||||
act(() => {
|
||||
const matches =
|
||||
'matches' in options
|
||||
? options.matches
|
||||
: breakpoints[mediaQueryString] === breakpoint;
|
||||
mediaQueryList.matches = matches;
|
||||
mediaQueryListeners.forEach(listener => listener({ matches }));
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
remove() {
|
||||
window.matchMedia = originalMatchMedia;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user