Skip to content

Commit 8238100

Browse files
committed
Fix test
1 parent 59b9a54 commit 8238100

2 files changed

Lines changed: 154 additions & 165 deletions

File tree

packages/react/src/stores/__tests__/theme-store.browser.test.ts

Lines changed: 154 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
mock,
1313
} from 'bun:test'
1414

15+
import { createThemeStore } from '../theme-store'
16+
1517
beforeAll(() => {
1618
document.documentElement.removeAttribute('data-theme')
1719
})
@@ -20,136 +22,163 @@ afterAll(() => {
2022
document.documentElement.removeAttribute('data-theme')
2123
})
2224

23-
describe('themeStore', () => {
25+
describe.each([
26+
[true, 'browser'],
27+
[false, 'server'],
28+
])('themeStore %s', (_isBrowser, _title) => {
29+
const originalWindow = globalThis.window
2430
beforeEach(() => {
31+
// Remove window to simulate SSR
32+
if (!_isBrowser) {
33+
// @ts-expect-error - Temporarily remove window for SSR test
34+
delete globalThis.window
35+
}
2536
document.documentElement.removeAttribute('data-theme')
2637
})
2738

2839
afterEach(() => {
2940
document.documentElement.removeAttribute('data-theme')
41+
globalThis.window = originalWindow
3042
})
31-
32-
it('should return themeStore object for browser', async () => {
33-
// const modulePath = require.resolve('../theme-store')
34-
// delete require.cache[modulePath]
35-
const { createThemeStore } = await import('../theme-store')
36-
const themeStore = createThemeStore()
37-
expect(themeStore).toBeDefined()
38-
expect(themeStore.get).toEqual(expect.any(Function))
39-
expect(themeStore.set).toEqual(expect.any(Function))
40-
expect(themeStore.subscribe).toEqual(expect.any(Function))
41-
expect(themeStore.get()).toBeNull()
42-
expect(themeStore.set('dark' as any)).toBeUndefined()
43-
// subscribe returns an unsubscribe function, which returns boolean when called
44-
const unsubscribe = themeStore.subscribe(() => {})
45-
expect(typeof unsubscribe).toBe('function')
46-
themeStore.subscribe(() => {})
47-
expect(themeStore.set('dark' as any)).toBeUndefined()
48-
})
49-
50-
it('should call subscriber when theme changes via set', async () => {
51-
// const modulePath = require.resolve('../theme-store')
52-
// delete require.cache[modulePath]
53-
const { createThemeStore } = await import('../theme-store')
54-
const themeStore = createThemeStore()
55-
const callback = mock()
56-
57-
themeStore.subscribe(callback)
58-
59-
// First call is from subscribe itself (reads current data-theme)
60-
expect(callback).toHaveBeenCalledTimes(1)
61-
62-
themeStore.set('light' as any)
63-
expect(callback).toHaveBeenCalledTimes(2)
64-
expect(callback).toHaveBeenLastCalledWith('light')
65-
expect(themeStore.get()).toBe('light' as any)
66-
})
67-
68-
it('should unsubscribe correctly', async () => {
69-
// const modulePath = require.resolve('../theme-store')
70-
// delete require.cache[modulePath]
71-
const { createThemeStore } = await import('../theme-store')
72-
const themeStore = createThemeStore()
73-
const callback = mock()
74-
75-
const unsubscribe = themeStore.subscribe(callback)
76-
expect(callback).toHaveBeenCalledTimes(1)
77-
78-
// Unsubscribe
79-
const result = unsubscribe()
80-
expect(result).toBe(true as any)
81-
82-
// Should not be called after unsubscribe
83-
themeStore.set('dark' as any)
84-
expect(callback).toHaveBeenCalledTimes(1)
85-
})
86-
87-
it('should read initial theme from data-theme attribute', async () => {
88-
document.documentElement.setAttribute('data-theme', 'dark')
89-
90-
// const modulePath = require.resolve('../theme-store')
91-
// delete require.cache[modulePath]
92-
const { createThemeStore } = await import('../theme-store')
93-
const themeStore = createThemeStore()
94-
const callback = mock()
95-
96-
themeStore.subscribe(callback)
97-
98-
// Should be called with 'dark' from the attribute
99-
expect(callback).toHaveBeenCalledWith('dark')
100-
})
101-
102-
it('should update theme when data-theme attribute changes via MutationObserver', async () => {
103-
// const modulePath = require.resolve('../theme-store')
104-
// delete require.cache[modulePath]
105-
const { createThemeStore } = await import('../theme-store')
106-
const themeStore = createThemeStore()
107-
const callback = mock()
108-
109-
themeStore.subscribe(callback)
110-
expect(callback).toHaveBeenCalledTimes(1)
111-
112-
// Change the attribute - MutationObserver should trigger
113-
document.documentElement.setAttribute('data-theme', 'dark')
114-
115-
// Wait for MutationObserver to fire (it's async)
116-
await new Promise((resolve) => setTimeout(resolve, 10))
117-
118-
expect(callback).toHaveBeenCalledWith('dark')
119-
expect(themeStore.get()).toBe('dark' as any)
120-
})
121-
122-
it('should handle multiple subscribers', async () => {
123-
// const modulePath = require.resolve('../theme-store')
124-
// delete require.cache[modulePath]
125-
const { createThemeStore } = await import('../theme-store')
126-
const themeStore = createThemeStore()
127-
const callback1 = mock()
128-
const callback2 = mock()
129-
130-
themeStore.subscribe(callback1)
131-
themeStore.subscribe(callback2)
132-
133-
themeStore.set('light' as any)
134-
135-
expect(callback1).toHaveBeenLastCalledWith('light')
136-
expect(callback2).toHaveBeenLastCalledWith('light')
137-
})
138-
139-
it('should filter mutations by type and target', async () => {
140-
// const modulePath = require.resolve('../theme-store')
141-
// delete require.cache[modulePath]
142-
const { createThemeStore } = await import('../theme-store')
143-
const themeStore = createThemeStore()
144-
const callback = mock()
145-
146-
themeStore.subscribe(callback)
147-
148-
// Change data-theme attribute (should trigger)
149-
document.documentElement.setAttribute('data-theme', 'system')
150-
151-
await new Promise((resolve) => setTimeout(resolve, 10))
152-
153-
expect(themeStore.get()).toBe('system' as any)
154-
})
43+
if (_isBrowser) {
44+
it('should return themeStore object for browser', async () => {
45+
// const modulePath = require.resolve('../theme-store')
46+
// delete require.cache[modulePath]
47+
const { createThemeStore } = await import('../theme-store')
48+
const themeStore = createThemeStore()
49+
expect(themeStore).toBeDefined()
50+
expect(themeStore.get).toEqual(expect.any(Function))
51+
expect(themeStore.set).toEqual(expect.any(Function))
52+
expect(themeStore.subscribe).toEqual(expect.any(Function))
53+
expect(themeStore.get()).toBeNull()
54+
expect(themeStore.set('dark' as any)).toBeUndefined()
55+
// subscribe returns an unsubscribe function, which returns boolean when called
56+
const unsubscribe = themeStore.subscribe(() => {})
57+
expect(typeof unsubscribe).toBe('function')
58+
themeStore.subscribe(() => {})
59+
expect(themeStore.set('dark' as any)).toBeUndefined()
60+
})
61+
62+
it('should call subscriber when theme changes via set', async () => {
63+
// const modulePath = require.resolve('../theme-store')
64+
// delete require.cache[modulePath]
65+
const { createThemeStore } = await import('../theme-store')
66+
const themeStore = createThemeStore()
67+
const callback = mock()
68+
69+
themeStore.subscribe(callback)
70+
71+
// First call is from subscribe itself (reads current data-theme)
72+
expect(callback).toHaveBeenCalledTimes(1)
73+
74+
themeStore.set('light' as any)
75+
expect(callback).toHaveBeenCalledTimes(2)
76+
expect(callback).toHaveBeenLastCalledWith('light')
77+
expect(themeStore.get()).toBe('light' as any)
78+
})
79+
80+
it('should unsubscribe correctly', async () => {
81+
// const modulePath = require.resolve('../theme-store')
82+
// delete require.cache[modulePath]
83+
const { createThemeStore } = await import('../theme-store')
84+
const themeStore = createThemeStore()
85+
const callback = mock()
86+
87+
const unsubscribe = themeStore.subscribe(callback)
88+
expect(callback).toHaveBeenCalledTimes(1)
89+
90+
// Unsubscribe
91+
const result = unsubscribe()
92+
expect(result).toBe(true as any)
93+
94+
// Should not be called after unsubscribe
95+
themeStore.set('dark' as any)
96+
expect(callback).toHaveBeenCalledTimes(1)
97+
})
98+
99+
it('should read initial theme from data-theme attribute', async () => {
100+
document.documentElement.setAttribute('data-theme', 'dark')
101+
102+
// const modulePath = require.resolve('../theme-store')
103+
// delete require.cache[modulePath]
104+
const { createThemeStore } = await import('../theme-store')
105+
const themeStore = createThemeStore()
106+
const callback = mock()
107+
108+
themeStore.subscribe(callback)
109+
110+
// Should be called with 'dark' from the attribute
111+
expect(callback).toHaveBeenCalledWith('dark')
112+
})
113+
114+
it('should update theme when data-theme attribute changes via MutationObserver', async () => {
115+
// const modulePath = require.resolve('../theme-store')
116+
// delete require.cache[modulePath]
117+
const { createThemeStore } = await import('../theme-store')
118+
const themeStore = createThemeStore()
119+
const callback = mock()
120+
121+
themeStore.subscribe(callback)
122+
expect(callback).toHaveBeenCalledTimes(1)
123+
124+
// Change the attribute - MutationObserver should trigger
125+
document.documentElement.setAttribute('data-theme', 'dark')
126+
127+
// Wait for MutationObserver to fire (it's async)
128+
await new Promise((resolve) => setTimeout(resolve, 10))
129+
130+
expect(callback).toHaveBeenCalledWith('dark')
131+
expect(themeStore.get()).toBe('dark' as any)
132+
})
133+
134+
it('should handle multiple subscribers', async () => {
135+
// const modulePath = require.resolve('../theme-store')
136+
// delete require.cache[modulePath]
137+
const { createThemeStore } = await import('../theme-store')
138+
const themeStore = createThemeStore()
139+
const callback1 = mock()
140+
const callback2 = mock()
141+
142+
themeStore.subscribe(callback1)
143+
themeStore.subscribe(callback2)
144+
145+
themeStore.set('light' as any)
146+
147+
expect(callback1).toHaveBeenLastCalledWith('light')
148+
expect(callback2).toHaveBeenLastCalledWith('light')
149+
})
150+
151+
it('should filter mutations by type and target', async () => {
152+
// const modulePath = require.resolve('../theme-store')
153+
// delete require.cache[modulePath]
154+
const { createThemeStore } = await import('../theme-store')
155+
const themeStore = createThemeStore()
156+
const callback = mock()
157+
158+
themeStore.subscribe(callback)
159+
160+
// Change data-theme attribute (should trigger)
161+
document.documentElement.setAttribute('data-theme', 'system')
162+
163+
await new Promise((resolve) => setTimeout(resolve, 10))
164+
165+
expect(themeStore.get()).toBe('system' as any)
166+
})
167+
} else {
168+
it('should return themeStore object for server', async () => {
169+
const themeStore = createThemeStore()
170+
171+
// Test SSR store behavior
172+
expect(themeStore).toBeDefined()
173+
expect(themeStore.get()).toBeNull()
174+
expect(themeStore.set('dark' as any)).toBeUndefined()
175+
176+
const unsubscribe = themeStore.subscribe(() => {})
177+
expect(typeof unsubscribe).toBe('function')
178+
179+
// The unsubscribe should return a no-op function
180+
const innerUnsubscribe = unsubscribe()
181+
expect(innerUnsubscribe).toBeUndefined()
182+
})
183+
}
155184
})

packages/react/src/stores/__tests__/theme-store.test.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)