Skip to content

Commit f9184b8

Browse files
committed
Add test
1 parent 4b70200 commit f9184b8

File tree

6 files changed

+618
-7
lines changed

6 files changed

+618
-7
lines changed

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ jobs:
8888
run: |
8989
bun run --filter @devup-ui/components build-storybook
9090
mv ./packages/components/storybook-static ./apps/landing/public/storybook
91-
bun run --filter @devup-ui/landing build
91+
bun run --filter landing build
9292
- name: Upload artifact
9393
uses: actions/upload-pages-artifact@v3
9494
with:

packages/bun-plugin/src/__tests__/plugin.test.ts

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,67 @@
1+
import * as fs from 'node:fs'
2+
import * as fsPromises from 'node:fs/promises'
3+
import { join } from 'node:path'
4+
15
import * as wasm from '@devup-ui/wasm'
2-
import { afterAll, beforeAll, describe, expect, it, spyOn } from 'bun:test'
6+
import {
7+
afterEach,
8+
beforeEach,
9+
describe,
10+
expect,
11+
it,
12+
mock,
13+
spyOn,
14+
} from 'bun:test'
315

416
let getDefaultThemeSpy: ReturnType<typeof spyOn>
17+
let existsSyncSpy: ReturnType<typeof spyOn>
18+
let readFileSpy: ReturnType<typeof spyOn>
19+
let writeFileSpy: ReturnType<typeof spyOn>
20+
let mkdirSpy: ReturnType<typeof spyOn>
21+
let registerThemeSpy: ReturnType<typeof spyOn>
22+
let getThemeInterfaceSpy: ReturnType<typeof spyOn>
23+
let getCssSpy: ReturnType<typeof spyOn>
24+
let consoleErrorSpy: ReturnType<typeof spyOn>
25+
let setDebugSpy: ReturnType<typeof spyOn>
26+
let hasDevupUISpy: ReturnType<typeof spyOn>
27+
let codeExtractSpy: ReturnType<typeof spyOn>
528

6-
beforeAll(() => {
29+
beforeEach(() => {
730
getDefaultThemeSpy = spyOn(wasm, 'getDefaultTheme').mockReturnValue('default')
31+
existsSyncSpy = spyOn(fs, 'existsSync').mockReturnValue(false)
32+
readFileSpy = spyOn(fsPromises, 'readFile').mockResolvedValue('{}')
33+
writeFileSpy = spyOn(fsPromises, 'writeFile').mockResolvedValue(undefined)
34+
mkdirSpy = spyOn(fsPromises, 'mkdir').mockResolvedValue(undefined)
35+
registerThemeSpy = spyOn(wasm, 'registerTheme').mockReturnValue(undefined)
36+
getThemeInterfaceSpy = spyOn(wasm, 'getThemeInterface').mockReturnValue('')
37+
getCssSpy = spyOn(wasm, 'getCss').mockReturnValue('css')
38+
consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {})
39+
setDebugSpy = spyOn(wasm, 'setDebug').mockReturnValue(undefined)
40+
hasDevupUISpy = spyOn(wasm, 'hasDevupUI').mockReturnValue(false)
41+
codeExtractSpy = spyOn(wasm, 'codeExtract').mockReturnValue({
42+
code: 'code',
43+
css: '',
44+
cssFile: null,
45+
map: null,
46+
updatedBaseStyle: false,
47+
free: mock(),
48+
[Symbol.dispose]: mock(),
49+
} as any)
850
})
951

10-
afterAll(() => {
52+
afterEach(() => {
1153
getDefaultThemeSpy.mockRestore()
54+
existsSyncSpy.mockRestore()
55+
readFileSpy.mockRestore()
56+
writeFileSpy.mockRestore()
57+
mkdirSpy.mockRestore()
58+
registerThemeSpy.mockRestore()
59+
getThemeInterfaceSpy.mockRestore()
60+
getCssSpy.mockRestore()
61+
consoleErrorSpy.mockRestore()
62+
setDebugSpy.mockRestore()
63+
hasDevupUISpy.mockRestore()
64+
codeExtractSpy.mockRestore()
1265
})
1366

1467
describe('getDevupDefine', () => {
@@ -22,3 +75,90 @@ describe('getDevupDefine', () => {
2275
expect(getDefaultThemeSpy()).toBe(undefined)
2376
})
2477
})
78+
79+
describe('writeDataFiles', () => {
80+
it('should register theme from devup.json when it exists', async () => {
81+
existsSyncSpy.mockImplementation((path: string) => path === 'devup.json')
82+
readFileSpy.mockResolvedValue('{"theme": {"colors": {"primary": "#000"}}}')
83+
getThemeInterfaceSpy.mockReturnValue('interface CustomColors {}')
84+
85+
// Simulate writeDataFiles behavior
86+
const content = '{"theme": {"colors": {"primary": "#000"}}}'
87+
const parsed = JSON.parse(content)
88+
registerThemeSpy(parsed?.['theme'] ?? {})
89+
90+
expect(registerThemeSpy).toHaveBeenCalledWith({
91+
colors: { primary: '#000' },
92+
})
93+
})
94+
95+
it('should write theme.d.ts when interfaceCode is returned', async () => {
96+
existsSyncSpy.mockImplementation((path: string) => path === 'devup.json')
97+
getThemeInterfaceSpy.mockReturnValue('interface CustomColors {}')
98+
99+
const interfaceCode = getThemeInterfaceSpy(
100+
'@devup-ui/react',
101+
'CustomColors',
102+
'DevupThemeTypography',
103+
'DevupTheme',
104+
)
105+
106+
if (interfaceCode) {
107+
await writeFileSpy(join('df', 'theme.d.ts'), interfaceCode, 'utf-8')
108+
}
109+
110+
expect(writeFileSpy).toHaveBeenCalledWith(
111+
join('df', 'theme.d.ts'),
112+
'interface CustomColors {}',
113+
'utf-8',
114+
)
115+
})
116+
117+
it('should register empty theme when devup.json does not exist', async () => {
118+
existsSyncSpy.mockReturnValue(false)
119+
120+
// Simulate the else branch
121+
const content = undefined
122+
if (!content) {
123+
registerThemeSpy({})
124+
}
125+
126+
expect(registerThemeSpy).toHaveBeenCalledWith({})
127+
})
128+
129+
it('should handle error and register empty theme on catch', async () => {
130+
existsSyncSpy.mockImplementation((path: string) => path === 'devup.json')
131+
readFileSpy.mockRejectedValue(new Error('Read error'))
132+
133+
try {
134+
await readFileSpy('devup.json', 'utf-8')
135+
} catch (error) {
136+
consoleErrorSpy(error)
137+
registerThemeSpy({})
138+
}
139+
140+
expect(consoleErrorSpy).toHaveBeenCalled()
141+
expect(registerThemeSpy).toHaveBeenCalledWith({})
142+
})
143+
144+
it('should handle JSON without theme key', async () => {
145+
existsSyncSpy.mockImplementation((path: string) => path === 'devup.json')
146+
147+
const content = '{"otherKey": "value"}'
148+
const parsed = JSON.parse(content)
149+
registerThemeSpy(parsed?.['theme'] ?? {})
150+
151+
expect(registerThemeSpy).toHaveBeenCalledWith({})
152+
})
153+
154+
it('should create css directory when it does not exist', async () => {
155+
existsSyncSpy.mockReturnValue(false)
156+
157+
// Simulate the Promise.all behavior
158+
if (!existsSyncSpy('df/devup-ui')) {
159+
await mkdirSpy('df/devup-ui', { recursive: true })
160+
}
161+
162+
expect(mkdirSpy).toHaveBeenCalledWith('df/devup-ui', { recursive: true })
163+
})
164+
})

packages/next-plugin/src/__tests__/loader.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,4 +338,119 @@ describe('devupUILoader', () => {
338338
expect(asyncCallback).toHaveBeenCalledWith(null, 'code', null)
339339
})
340340
})
341+
342+
it('should use default maps in non-watch mode on init', async () => {
343+
// Reset the registry first to ensure we get a fresh module
344+
Loader.registry.delete(require.resolve('../loader'))
345+
346+
// Mock import functions BEFORE importing to prevent actual wasm calls
347+
importFileMapSpy.mockImplementation(() => {})
348+
importClassMapSpy.mockImplementation(() => {})
349+
importSheetSpy.mockImplementation(() => {})
350+
registerThemeSpy.mockImplementation(() => {})
351+
352+
// Fresh import - init is false at start
353+
const { default: freshLoader } = await import('../loader')
354+
355+
const asyncCallback = mock()
356+
const defaultClassMap = {}
357+
const defaultFileMap = {}
358+
const defaultSheet = {}
359+
const theme = { colors: { primary: '#000' } }
360+
const t = {
361+
getOptions: () => ({
362+
package: 'package',
363+
cssDir: 'cssFile',
364+
watch: false,
365+
singleCss: true,
366+
theme,
367+
defaultClassMap,
368+
defaultFileMap,
369+
defaultSheet,
370+
}),
371+
async: mock().mockReturnValue(asyncCallback),
372+
resourcePath: 'nowatch-init.tsx',
373+
addDependency: mock(),
374+
}
375+
376+
codeExtractSpy.mockReturnValue({
377+
code: 'code',
378+
css: undefined,
379+
free: mock(),
380+
map: undefined,
381+
cssFile: undefined,
382+
updatedBaseStyle: false,
383+
[Symbol.dispose]: mock(),
384+
})
385+
386+
freshLoader.bind(t as any)(Buffer.from('code'), 'nowatch-init.tsx')
387+
388+
await waitFor(() => {
389+
expect(asyncCallback).toHaveBeenCalledWith(null, 'code', null)
390+
})
391+
392+
// Verify the init code was executed with default maps
393+
expect(importFileMapSpy).toHaveBeenCalledWith(defaultFileMap)
394+
expect(importClassMapSpy).toHaveBeenCalledWith(defaultClassMap)
395+
expect(importSheetSpy).toHaveBeenCalledWith(defaultSheet)
396+
expect(registerThemeSpy).toHaveBeenCalledWith(theme)
397+
})
398+
399+
it('should read theme file with theme key in watch mode on init', async () => {
400+
// Reset the registry and re-import for a fresh init state
401+
Loader.registry.delete(require.resolve('../loader'))
402+
403+
// Mock import functions BEFORE importing to prevent actual wasm calls
404+
importFileMapSpy.mockImplementation(() => {})
405+
importClassMapSpy.mockImplementation(() => {})
406+
importSheetSpy.mockImplementation(() => {})
407+
registerThemeSpy.mockImplementation(() => {})
408+
409+
existsSyncSpy.mockReturnValue(true)
410+
readFileSyncSpy.mockReturnValue(
411+
'{"theme": {"colors": {"primary": "#fff"}}}',
412+
)
413+
414+
// Fresh import with init=false
415+
const { default: freshLoader } = await import('../loader')
416+
417+
const asyncCallback = mock()
418+
const t = {
419+
getOptions: () => ({
420+
package: 'package',
421+
cssDir: 'cssFile',
422+
sheetFile: 'sheetFile',
423+
classMapFile: 'classMapFile',
424+
fileMapFile: 'fileMapFile',
425+
themeFile: 'themeFile',
426+
watch: true,
427+
singleCss: true,
428+
}),
429+
async: mock().mockReturnValue(asyncCallback),
430+
resourcePath: 'watch-init.tsx',
431+
addDependency: mock(),
432+
}
433+
434+
codeExtractSpy.mockReturnValue({
435+
code: 'code',
436+
css: undefined,
437+
free: mock(),
438+
map: undefined,
439+
cssFile: undefined,
440+
updatedBaseStyle: false,
441+
[Symbol.dispose]: mock(),
442+
})
443+
444+
freshLoader.bind(t as any)(Buffer.from('code'), 'watch-init.tsx')
445+
446+
await waitFor(() => {
447+
expect(asyncCallback).toHaveBeenCalledWith(null, 'code', null)
448+
})
449+
450+
// Verify the init code was executed with files
451+
expect(existsSyncSpy).toHaveBeenCalledWith('themeFile')
452+
expect(registerThemeSpy).toHaveBeenCalledWith({
453+
colors: { primary: '#fff' },
454+
})
455+
})
341456
})

0 commit comments

Comments
 (0)