diff --git a/src/components/Text/Text.tsx b/src/components/Text/Text.tsx index 0d2ca25a..0a279098 100644 --- a/src/components/Text/Text.tsx +++ b/src/components/Text/Text.tsx @@ -173,7 +173,7 @@ export const Monospace = forwardRef( ({ inline = false, ...props }, forwardedRef) => { return ( = ({ } window - .matchMedia('(prefers-color-scheme: dark)') + ?.matchMedia?.('(prefers-color-scheme: dark)') .addEventListener('change', (e) => { setThemeValues(e.matches ? 'dark' : 'light') }) window - .matchMedia('(prefers-color-scheme: light)') + ?.matchMedia?.('(prefers-color-scheme: light)') .addEventListener('change', (e) => { setThemeValues(e.matches ? 'light' : 'dark') }) diff --git a/src/components/ThemeProvider/ThemeProvider.test.tsx b/src/components/ThemeProvider/ThemeProvider.test.tsx index c557bc42..cd098d9e 100644 --- a/src/components/ThemeProvider/ThemeProvider.test.tsx +++ b/src/components/ThemeProvider/ThemeProvider.test.tsx @@ -1,29 +1,94 @@ import React from 'react' +import { act, renderDark, renderLight, renderPlain, screen } from 'test-utils' import { ThemeProvider } from '.' import { - UtilityUseThemeResolve, UtilityUseThemeController, + UtilityUseThemeResolve, } from './ThemeProvider.stories' -import { renderPlain, renderLight, renderDark } from 'test-utils' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let addEventListener: jest.Mock + +beforeEach(() => { + addEventListener = jest.fn() + + // Official way to supply missing window method https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation((query: string) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener, + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), + }) +}) it('renders light without error', () => { const { asFragment } = renderPlain() expect(asFragment()).toBeDefined() + expect(addEventListener).toHaveBeenCalledTimes(4) }) it('renders dark without error', () => { const { asFragment } = renderPlain() expect(asFragment()).toBeDefined() + expect(addEventListener).toHaveBeenCalledTimes(4) }) -it('renders resolutions within light theme', () => { - const { asFragment } = renderLight() - expect(asFragment()).toBeDefined() +it('changing system/window theme changes to dark sets the theme', async () => { + renderPlain( + + + + ) + + act(() => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access + const listener = addEventListener.mock.calls[0][1] as (e: { + matches: string + }) => void + listener({ matches: 'dark' }) + }) + + expect(await screen.findByText('#000000')).toBeInTheDocument() }) -it('renders resolutions within dark theme', () => { - const { asFragment } = renderDark() - expect(asFragment()).toBeDefined() +it('changing system/window theme to light sets the theme', async () => { + renderPlain( + + + + ) + act(() => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access + const listener = addEventListener.mock.calls[1][1] as (e: { + matches: string + }) => void + listener({ matches: 'light' }) + }) + + expect(await screen.findByText('#f7f7f7')).toBeInTheDocument() +}) + +it('changing system/window when theme selected does not change the theme', () => { + renderPlain( + + + + ) + act(() => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access + const listener = addEventListener.mock.calls[0][1] as (e: { + matches: string + }) => void + listener({ matches: 'dark' }) + }) + expect(screen.queryByText('#000000')).not.toBeInTheDocument() }) it('renders switch within light theme', () => { diff --git a/src/utils/test-utils.tsx b/src/utils/test-utils.tsx index 209be52e..127671f3 100644 --- a/src/utils/test-utils.tsx +++ b/src/utils/test-utils.tsx @@ -19,21 +19,6 @@ import ResizeObserver from 'resize-observer-polyfill' // This is used in some components. global.ResizeObserver = ResizeObserver -// Official way to supply missing window method https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom -Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation((query: string) => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), // Deprecated - removeListener: jest.fn(), // Deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), -}) - const LightTheme: React.FC = ({ children }) => ( {children} )