UNPKG

@kadconsulting/dry

Version:
176 lines 9.5 kB
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import { mockedMatchMediaValue, actualMatchMediaValue, } from '../../../jest.setup'; import userEvent from '@testing-library/user-event'; import { ThemeProvider, useTheme, ThemeTypes } from './'; import darkTheme from '../../themes/default/palette/dark'; import lightTheme from '../../themes/default/palette/light'; import { render, screen, waitFor } from '@testing-library/react'; import { LocalStorageKeys } from '../../config/LocalStorageKeys'; import { FEATURE_DETECTION_TEST_VALUE } from '../../hooks/useLocalStorageForKey'; const actualStorageDotPrototypeDotGetItem = Storage.prototype.getItem; describe('ThemeProvider', () => { beforeAll(() => Object.defineProperty(window, 'matchMedia', mockedMatchMediaValue({ matches: false }))); afterAll(() => { window.matchMedia = actualMatchMediaValue; }); beforeEach(() => { jest.spyOn(window.localStorage, 'setItem'); jest.spyOn(window.localStorage, 'getItem'); Storage.prototype.getItem = jest .fn() /** Mock the localStorage wrapper hook's feature detection */ .mockImplementationOnce(() => FEATURE_DETECTION_TEST_VALUE); }); afterEach(() => { Storage.prototype.getItem = actualStorageDotPrototypeDotGetItem; jest.clearAllMocks(); }); it('renders its children', () => { // ARRANGE const children = 'Hello world'; // ACT render(_jsx(ThemeProvider, { themes: { light: lightTheme }, children: children })); // ASSERT expect(screen.getByText(children)).toBeInTheDocument(); }); it('may accept only one theme and themeType', () => { const defaultThemeType = ThemeTypes.LIGHT; const TestConsumer = () => { const { themeType } = useTheme(); return _jsx(_Fragment, { children: themeType }); }; // ARRANGE render(_jsx(ThemeProvider, { themes: { light: lightTheme }, children: _jsx(TestConsumer, {}) })); // ASSERT expect(screen.getByText(defaultThemeType)).toBeInTheDocument(); }); it('renders the correct default themeType', () => { const defaultThemeType = ThemeTypes.LIGHT; const TestConsumer = () => { const { themeType } = useTheme(); return _jsx(_Fragment, { children: themeType }); }; // ARRANGE render(_jsx(ThemeProvider, { themes: { dark: darkTheme, light: lightTheme }, children: _jsx(TestConsumer, {}) })); // ASSERT expect(screen.getByText(defaultThemeType)).toBeInTheDocument(); }); it('renders the correct themeType when a user preference was set', async () => { // ARRANGE const userPreference = ThemeTypes.DARK; const actualStorageDotPrototypeDotGetItem = Storage.prototype.getItem; Storage.prototype.getItem = jest .fn() /** Mock the localStorage wrapper hook's feature detection */ .mockImplementationOnce(() => FEATURE_DETECTION_TEST_VALUE) /** Mock the test case */ .mockImplementation(() => userPreference); const TestConsumer = () => { const { themeType } = useTheme(); return _jsx("div", { "data-testid": 'theme', children: themeType }); }; // ACT render(_jsx(ThemeProvider, { themes: { dark: darkTheme, light: lightTheme }, children: _jsx(TestConsumer, {}) })); // ASSERT await waitFor(() => { expect(screen.getByTestId('theme')).toContainHTML(userPreference); expect(window.localStorage.getItem).toHaveBeenCalledWith(LocalStorageKeys.THEME); }); // CLEANUP jest.clearAllMocks(); Storage.prototype.getItem = actualStorageDotPrototypeDotGetItem; }); it('allows consumers to update the themeType and persist their selection', async () => { // ARRANGE const user = userEvent.setup(); const updatedThemeValue = ThemeTypes.DARK; jest.spyOn(Object.getPrototypeOf(window.localStorage), 'setItem'); Object.setPrototypeOf(window.localStorage.setItem, jest.fn()); const TestConsumer = () => { const { themeType, setTheme } = useTheme(); return (_jsxs(_Fragment, { children: [_jsx("div", { "data-testid": 'theme', children: themeType }), _jsxs("button", { type: 'button', onClick: () => setTheme(updatedThemeValue), children: ["Set theme type to ", updatedThemeValue] })] })); }; // ACT render(_jsx(ThemeProvider, { themes: { dark: darkTheme, light: lightTheme }, children: _jsx(TestConsumer, {}) })); user.click(screen.getByText(`Set theme type to ${updatedThemeValue}`)); // ASSERT await waitFor(() => { expect(screen.getByTestId('theme')).toContainHTML(updatedThemeValue); expect(window.localStorage.setItem).toHaveBeenCalledWith(LocalStorageKeys.THEME, updatedThemeValue); }); // CLEANUP jest.clearAllMocks(); }); it('toggles the selector class when the themeType is updated', async () => { // ARRANGE const user = userEvent.setup(); const updatedThemeValue = ThemeTypes.DARK; const TestConsumer = () => { const { themeType, setTheme } = useTheme(); return (_jsxs(_Fragment, { children: [_jsx("div", { "data-testid": 'theme', children: themeType }), _jsxs("button", { type: 'button', onClick: () => setTheme(updatedThemeValue), children: ["Set theme type to ", updatedThemeValue] })] })); }; // ACT render(_jsx(ThemeProvider, { themes: { dark: darkTheme, light: lightTheme }, children: _jsx(TestConsumer, {}) })); user.click(screen.getByText(`Set theme type to ${updatedThemeValue}`)); // ASSERT await waitFor(() => { expect(screen.getByTestId('theme')).toContainHTML(updatedThemeValue); expect(document.querySelector(':root')).toHaveClass(updatedThemeValue); }); }); it('allows consumers to update the themeType to a "System" preference - light', async () => { // ARRANGE const user = userEvent.setup(); jest.spyOn(Object.getPrototypeOf(window.localStorage), 'removeItem'); Object.setPrototypeOf(window.localStorage.removeItem, jest.fn()); // Set the test environment's system preference to light Object.defineProperty(window, 'matchMedia', mockedMatchMediaValue({ matches: false }) // 👈 The media query matches to a dark preference, so setting this value to false sets the preference to a light theme ); const TestConsumer = () => { const { themeType, useSystemPreference } = useTheme(); return (_jsxs(_Fragment, { children: [_jsx("div", { "data-testid": 'theme', children: themeType }), _jsx("button", { type: 'button', onClick: useSystemPreference, children: "Match system preference" })] })); }; // ACT render(_jsx(ThemeProvider, { themes: { dark: darkTheme, light: lightTheme }, children: _jsx(TestConsumer, {}) })); user.click(screen.getByText('Match system preference')); // ASSERT await waitFor(() => { expect(screen.getByTestId('theme')).toContainHTML('light'); expect(window.localStorage.removeItem).toHaveBeenCalledWith(LocalStorageKeys.THEME); }); }); it('allows consumers to update the themeType to a "System" preference - dark', async () => { // ARRANGE const user = userEvent.setup(); jest.spyOn(Object.getPrototypeOf(window.localStorage), 'removeItem'); Object.setPrototypeOf(window.localStorage.removeItem, jest.fn()); // Set the test environment's system preference to dark Object.defineProperty(window, 'matchMedia', mockedMatchMediaValue({ matches: true }) // 👈 The media query matches to a dark preference, so setting this value to true sets the preference to a dark theme ); const TestConsumer = () => { const { themeType, useSystemPreference } = useTheme(); return (_jsxs(_Fragment, { children: [_jsx("div", { "data-testid": 'theme', children: themeType }), _jsx("button", { type: 'button', onClick: useSystemPreference, children: "Match system preference" })] })); }; // ACT render(_jsx(ThemeProvider, { themes: { dark: darkTheme, light: lightTheme }, children: _jsx(TestConsumer, {}) })); user.click(screen.getByText('Match system preference')); // ASSERT await waitFor(() => { expect(screen.getByTestId('theme')).toContainHTML('dark'); expect(window.localStorage.removeItem).toHaveBeenCalledWith(LocalStorageKeys.THEME); }); }); it('allows library consumers to pass the provider an override for the current theme in their own downstream tests', () => { // ARRANGE const themeType = ThemeTypes.DARK; const TestConsumer = () => { const { themeType } = useTheme(); return _jsx(_Fragment, { children: themeType }); }; // ACT render(_jsx(ThemeProvider, { overrides: { themeType }, themes: { dark: darkTheme, light: lightTheme }, children: _jsx(TestConsumer, {}) })); // ASSERT expect(screen.getByText(themeType)).toBeInTheDocument(); }); }); //# sourceMappingURL=ThemeProvider.test.js.map