@kadconsulting/dry
Version:
KAD Reusable Component Library
176 lines • 9.5 kB
JavaScript
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