11// @vitest -environment jsdom
22
33import * as React from 'react'
4- import { act , render , screen } from '@testing-library/react'
4+ import { act , render , renderHook , screen } from '@testing-library/react'
55import { vi , beforeAll , beforeEach , afterEach , afterAll , describe , test , it , expect } from 'vitest'
66import { cleanup } from '@testing-library/react'
77
88import { ThemeProvider , useTheme } from '../src/index'
9+ import { ThemeProviderProps } from '../src/types'
910
1011let originalLocalStorage : Storage
1112const localStorageMock : Storage = ( ( ) => {
@@ -90,86 +91,81 @@ afterAll(() => {
9091 window . localStorage = originalLocalStorage
9192} )
9293
94+ function makeWrapper ( props : ThemeProviderProps ) {
95+ return ( { children } : { children : React . ReactNode } ) => (
96+ < ThemeProvider { ...props } > { children } </ ThemeProvider >
97+ )
98+ }
99+
93100describe ( 'defaultTheme' , ( ) => {
94- test ( 'should return system when no default-theme is set' , ( ) => {
95- render (
96- < ThemeProvider >
97- < HelperComponent />
98- </ ThemeProvider >
99- )
100-
101- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'system' )
101+ test ( 'should return system-theme when no default-theme is set' , ( ) => {
102+ setDeviceTheme ( 'light' )
103+
104+ const { result } = renderHook ( ( ) => useTheme ( ) , {
105+ wrapper : makeWrapper ( { } )
106+ } )
107+ expect ( result . current . theme ) . toBe ( 'system' )
108+ expect ( result . current . systemTheme ) . toBe ( 'light' )
109+ expect ( result . current . resolvedTheme ) . toBe ( 'light' )
102110 } )
103111
104112 test ( 'should return light when no default-theme is set and enableSystem=false' , ( ) => {
105- render (
106- < ThemeProvider enableSystem = { false } >
107- < HelperComponent />
108- </ ThemeProvider >
109- )
113+ const { result } = renderHook ( ( ) => useTheme ( ) , {
114+ wrapper : makeWrapper ( { enableSystem : false } )
115+ } )
110116
111- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'light' )
117+ expect ( result . current . theme ) . toBe ( 'light' )
118+ expect ( result . current . resolvedTheme ) . toBe ( 'light' )
112119 } )
113120
114121 test ( 'should return light when light is set as default-theme' , ( ) => {
115- render (
116- < ThemeProvider defaultTheme = "light" >
117- < HelperComponent />
118- </ ThemeProvider >
119- )
122+ const { result } = renderHook ( ( ) => useTheme ( ) , {
123+ wrapper : makeWrapper ( { defaultTheme : 'light' } )
124+ } )
120125
121- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'light' )
126+ expect ( result . current . theme ) . toBe ( 'light' )
127+ expect ( result . current . resolvedTheme ) . toBe ( 'light' )
122128 } )
123129
124130 test ( 'should return dark when dark is set as default-theme' , ( ) => {
125- render (
126- < ThemeProvider defaultTheme = "dark" >
127- < HelperComponent />
128- </ ThemeProvider >
129- )
130-
131- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'dark' )
131+ const { result } = renderHook ( ( ) => useTheme ( ) , {
132+ wrapper : makeWrapper ( { defaultTheme : 'dark' } )
133+ } )
134+ expect ( result . current . theme ) . toBe ( 'dark' )
135+ expect ( result . current . resolvedTheme ) . toBe ( 'dark' )
132136 } )
133137} )
134138
135139describe ( 'provider' , ( ) => {
136140 it ( 'ignores nested ThemeProviders' , ( ) => {
137- act ( ( ) => {
138- render (
141+ const { result } = renderHook ( ( ) => useTheme ( ) , {
142+ wrapper : ( { children } ) => (
139143 < ThemeProvider defaultTheme = "dark" >
140- < ThemeProvider defaultTheme = "light" >
141- < HelperComponent />
142- </ ThemeProvider >
144+ < ThemeProvider defaultTheme = "light" > { children } </ ThemeProvider >
143145 </ ThemeProvider >
144146 )
145147 } )
146148
147- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'dark' )
149+ expect ( result . current . theme ) . toBe ( 'dark' )
150+ expect ( result . current . resolvedTheme ) . toBe ( 'dark' )
148151 } )
149152} )
150153
151154describe ( 'storage' , ( ) => {
152155 test ( 'should not set localStorage with default value' , ( ) => {
153- act ( ( ) => {
154- render (
155- < ThemeProvider defaultTheme = "dark" >
156- < HelperComponent />
157- </ ThemeProvider >
158- )
156+ renderHook ( ( ) => useTheme ( ) , {
157+ wrapper : makeWrapper ( { defaultTheme : 'dark' } )
159158 } )
160159
161160 expect ( window . localStorage . setItem ) . toBeCalledTimes ( 0 )
162161 expect ( window . localStorage . getItem ( 'theme' ) ) . toBeNull ( )
163162 } )
164163
165164 test ( 'should set localStorage when switching themes' , ( ) => {
166- act ( ( ) => {
167- render (
168- < ThemeProvider >
169- < HelperComponent forceSetTheme = "dark" />
170- </ ThemeProvider >
171- )
165+ const { result } = renderHook ( ( ) => useTheme ( ) , {
166+ wrapper : makeWrapper ( { } )
172167 } )
168+ result . current . setTheme ( 'dark' )
173169
174170 expect ( window . localStorage . setItem ) . toBeCalledTimes ( 1 )
175171 expect ( window . localStorage . getItem ( 'theme' ) ) . toBe ( 'dark' )
@@ -290,47 +286,40 @@ describe('forcedTheme', () => {
290286 test ( 'should render saved theme when no forcedTheme is set' , ( ) => {
291287 localStorageMock . setItem ( 'theme' , 'dark' )
292288
293- render (
294- < ThemeProvider >
295- < HelperComponent />
296- </ ThemeProvider >
297- )
289+ const { result } = renderHook ( ( ) => useTheme ( ) , {
290+ wrapper : makeWrapper ( { } )
291+ } )
298292
299- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'dark' )
300- expect ( screen . getByTestId ( ' forcedTheme' ) . textContent ) . toBe ( '' )
293+ expect ( result . current . theme ) . toBe ( 'dark' )
294+ expect ( result . current . forcedTheme ) . toBeUndefined ( )
301295 } )
302296
303297 test ( 'should render light theme when forcedTheme is set to light' , ( ) => {
304298 localStorageMock . setItem ( 'theme' , 'dark' )
305299
306- act ( ( ) => {
307- render (
308- < ThemeProvider forcedTheme = "light" >
309- < HelperComponent />
310- </ ThemeProvider >
311- )
300+ const { result } = renderHook ( ( ) => useTheme ( ) , {
301+ wrapper : makeWrapper ( {
302+ forcedTheme : 'light'
303+ } )
312304 } )
313305
314- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'dark' )
315- expect ( screen . getByTestId ( 'forcedTheme' ) . textContent ) . toBe ( 'light' )
306+ expect ( result . current . theme ) . toBe ( 'dark' )
307+ expect ( result . current . forcedTheme ) . toBe ( 'light' )
316308 } )
317309} )
318310
319- describe ( 'system' , ( ) => {
311+ describe ( 'system theme ' , ( ) => {
320312 test ( 'resolved theme should be set' , ( ) => {
321313 setDeviceTheme ( 'dark' )
322314
323- act ( ( ) => {
324- render (
325- < ThemeProvider >
326- < HelperComponent />
327- </ ThemeProvider >
328- )
315+ const { result } = renderHook ( ( ) => useTheme ( ) , {
316+ wrapper : makeWrapper ( { } )
329317 } )
330318
331- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'system' )
332- expect ( screen . getByTestId ( 'forcedTheme' ) . textContent ) . toBe ( '' )
333- expect ( screen . getByTestId ( 'resolvedTheme' ) . textContent ) . toBe ( 'dark' )
319+ expect ( result . current . theme ) . toBe ( 'system' )
320+ expect ( result . current . systemTheme ) . toBe ( 'dark' )
321+ expect ( result . current . resolvedTheme ) . toBe ( 'dark' )
322+ expect ( result . current . forcedTheme ) . toBeUndefined ( )
334323 } )
335324
336325 test ( 'system theme should be set, even if theme is not system' , ( ) => {
@@ -353,18 +342,14 @@ describe('system', () => {
353342 test ( 'system theme should not be set if enableSystem is false' , ( ) => {
354343 setDeviceTheme ( 'dark' )
355344
356- act ( ( ) => {
357- render (
358- < ThemeProvider defaultTheme = "light" enableSystem = { false } >
359- < HelperComponent />
360- </ ThemeProvider >
361- )
345+ const { result } = renderHook ( ( ) => useTheme ( ) , {
346+ wrapper : makeWrapper ( { enableSystem : false , defaultTheme : 'light' } )
362347 } )
363348
364- expect ( screen . getByTestId ( 'theme' ) . textContent ) . toBe ( 'light' )
365- expect ( screen . getByTestId ( 'forcedTheme' ) . textContent ) . toBe ( '' )
366- expect ( screen . getByTestId ( 'resolvedTheme' ) . textContent ) . toBe ( 'light' )
367- expect ( screen . getByTestId ( 'systemTheme' ) . textContent ) . toBe ( '' )
349+ expect ( result . current . theme ) . toBe ( 'light' )
350+ expect ( result . current . systemTheme ) . toBeUndefined ( )
351+ expect ( result . current . resolvedTheme ) . toBe ( 'light' )
352+ expect ( result . current . forcedTheme ) . toBeUndefined ( )
368353 } )
369354} )
370355
@@ -407,3 +392,16 @@ describe('color-scheme', () => {
407392 expect ( document . documentElement . style . colorScheme ) . toBe ( 'dark' )
408393 } )
409394} )
395+
396+ describe ( 'setTheme' , ( ) => {
397+ test ( 'setTheme(<literal>)' , ( ) => {
398+ const { result, rerender } = renderHook ( ( ) => useTheme ( ) , {
399+ wrapper : ( { children } ) => < ThemeProvider defaultTheme = "light" > { children } </ ThemeProvider >
400+ } )
401+ expect ( result . current ?. setTheme ) . toBeDefined ( )
402+ expect ( result . current . resolvedTheme ) . toBe ( 'light' )
403+ result . current . setTheme ( 'dark' )
404+ rerender ( )
405+ expect ( result . current . resolvedTheme ) . toBe ( 'dark' )
406+ } )
407+ } )
0 commit comments