Skip to content

Commit e32ea88

Browse files
committed
test: cover dev initialize wiring, close ordering and profiling
1 parent 1e3e803 commit e32ea88

1 file changed

Lines changed: 145 additions & 0 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import{EventEmitter}from'node:events'
2+
importprocessfrom'node:process'
3+
4+
import{afterEach,beforeEach,describe,expect,it,vi}from'vitest'
5+
6+
const{ closeListener, closeNuxt, closeWatchers, devServers, releaseLock, startCpuProfile, stopCpuProfile }=vi.hoisted(()=>({
7+
closeListener: vi.fn(()=>Promise.resolve()),
8+
closeNuxt: vi.fn(()=>Promise.resolve()),
9+
closeWatchers: vi.fn(),
10+
devServers: []asany[],
11+
releaseLock: vi.fn(),
12+
startCpuProfile: vi.fn(()=>Promise.resolve()),
13+
stopCpuProfile: vi.fn(),
14+
}))
15+
16+
vi.mock('../../../src/utils/profile.ts',()=>({ startCpuProfile, stopCpuProfile }))
17+
vi.mock('../../../src/utils/env.ts',()=>({overrideEnv: vi.fn()}))
18+
19+
vi.mock('../../../src/dev/utils',()=>({
20+
NuxtDevServer: classextendsEventEmitter{
21+
listener={url: 'http://127.0.0.1:3000',close: closeListener}
22+
closeWatchers=closeWatchers
23+
close=closeNuxt
24+
releaseLock=releaseLock
25+
load=vi.fn(()=>Promise.resolve())
26+
27+
constructor(readonlyoptions: Record<string,any>){
28+
super()
29+
devServers.push(this)
30+
}
31+
32+
init=vi.fn(async()=>{
33+
this.emit('ready','http://127.0.0.1:3000')
34+
})
35+
},
36+
}))
37+
38+
const{ initialize }=awaitimport('../../../src/dev/index')
39+
40+
functioncontext(args: Record<string,unknown>={}){
41+
return{cwd: process.cwd(),args: { ...args}}asnever
42+
}
43+
44+
beforeEach(()=>{
45+
devServers.length=0
46+
vi.clearAllMocks()
47+
})
48+
49+
afterEach(()=>{
50+
vi.restoreAllMocks()
51+
})
52+
53+
describe('initialize',()=>{
54+
it('should report the address to a callback registered after ready',async()=>{
55+
const{ onReady }=awaitinitialize(context())
56+
constseen: string[]=[]
57+
58+
onReady(address=>seen.push(address))
59+
60+
expect(seen).toEqual(['http://127.0.0.1:3000'])
61+
})
62+
63+
it('should close the watchers, the listener and nuxt before releasing the lock',async()=>{
64+
constorder: string[]=[]
65+
closeWatchers.mockImplementation(()=>order.push('watchers'))
66+
closeListener.mockImplementation(async()=>voidorder.push('listener'))
67+
closeNuxt.mockImplementation(async()=>voidorder.push('nuxt'))
68+
releaseLock.mockImplementation(()=>order.push('lock'))
69+
70+
const{ close }=awaitinitialize(context())
71+
awaitclose()
72+
73+
expect(order[0]).toBe('watchers')
74+
expect(order.at(-1)).toBe('lock')
75+
expect(order).toContain('listener')
76+
expect(order).toContain('nuxt')
77+
})
78+
79+
it('should close only once however often it is asked',async()=>{
80+
const{ close }=awaitinitialize(context())
81+
82+
awaitPromise.all([close(),close()])
83+
awaitclose()
84+
85+
expect(releaseLock).toHaveBeenCalledTimes(1)
86+
expect(closeListener).toHaveBeenCalledTimes(1)
87+
})
88+
89+
it('should release the lock even when closing fails',async()=>{
90+
closeNuxt.mockRejectedValue(newError('nitro would not close'))
91+
92+
const{ close }=awaitinitialize(context())
93+
94+
awaitexpect(close()).rejects.toThrow('nitro would not close')
95+
expect(releaseLock).toHaveBeenCalledTimes(1)
96+
})
97+
98+
it('should reload in place when asked',async()=>{
99+
const{ reload }=awaitinitialize(context())
100+
101+
awaitreload({type: 'shortcut'})
102+
103+
expect(devServers[0].load).toHaveBeenCalledWith(true,{type: 'shortcut'})
104+
})
105+
106+
it('should pass `extends` through to the dev server overrides',async()=>{
107+
awaitinitialize(context({extends: ['../layer']}))
108+
109+
expect(devServers[0].options.overrides).toMatchObject({extends: ['../layer']})
110+
})
111+
112+
it('should not profile unless it was asked to',async()=>{
113+
awaitinitialize(context())
114+
115+
expect(startCpuProfile).not.toHaveBeenCalled()
116+
expect(devServers[0].options.overrides.debug).toBeUndefined()
117+
})
118+
119+
it('should profile quietly for a bare `--profile`',async()=>{
120+
awaitinitialize(context({profile: ''}))
121+
122+
expect(startCpuProfile).toHaveBeenCalledTimes(1)
123+
expect(devServers[0].options.overrides).toMatchObject({debug: {perf: 'quiet'}})
124+
})
125+
126+
it('should profile verbosely for `--profile=verbose`',async()=>{
127+
awaitinitialize(context({profile: 'verbose'}))
128+
129+
expect(devServers[0].options.overrides).toMatchObject({debug: {perf: true}})
130+
})
131+
132+
it('should hand the file change and restart hooks to the caller',async()=>{
133+
const{ onFileChange, onRestart }=awaitinitialize(context())
134+
constchanged=vi.fn()
135+
constrestarted=vi.fn()
136+
137+
onFileChange(changed)
138+
onRestart(restarted)
139+
devServers[0].emit('change')
140+
devServers[0].emit('restart',{type: 'hook'})
141+
142+
expect(changed).toHaveBeenCalledTimes(1)
143+
expect(restarted).toHaveBeenCalledWith({type: 'hook'})
144+
})
145+
})

0 commit comments

Comments
 (0)