Skip to content

Commit 552a2d4

Browse files
committed
test: add test for basic scenarios
1 parent 0ebb890 commit 552a2d4

File tree

7 files changed

+222
-0
lines changed

7 files changed

+222
-0
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { setTimeout } from 'node:timers/promises'
2+
import { expect, test } from 'vitest'
3+
import { editFile, isBuild, page } from '~utils'
4+
5+
if (isBuild) {
6+
test('should render', async () => {
7+
expect(await page.textContent('h1')).toContain('HMR Full Bundle Mode')
8+
await expect.poll(() => page.textContent('.app')).toBe('hello')
9+
await expect.poll(() => page.textContent('.hmr')).toBe('hello')
10+
})
11+
} else {
12+
// INITIAL -> BUNDLING -> BUNDLED
13+
test('show bundling in progress', async () => {
14+
const reloadPromise = page.waitForEvent('load')
15+
await expect
16+
.poll(() => page.textContent('body'))
17+
.toContain('Bundling in progress')
18+
await reloadPromise // page shown after reload
19+
await expect.poll(() => page.textContent('h1')).toBe('HMR Full Bundle Mode')
20+
await expect.poll(() => page.textContent('.app')).toBe('hello')
21+
})
22+
23+
// BUNDLED -> GENERATE_HMR_PATCH -> BUNDLING -> BUNDLE_ERROR -> BUNDLING -> BUNDLED
24+
test('handle bundle error', async () => {
25+
editFile('main.js', (code) =>
26+
code.replace("text('.app', 'hello')", "text('.app', 'hello'); text("),
27+
)
28+
await expect.poll(() => page.isVisible('vite-error-overlay')).toBe(true)
29+
editFile('main.js', (code) =>
30+
code.replace("text('.app', 'hello'); text(", "text('.app', 'hello')"),
31+
)
32+
await expect.poll(() => page.isVisible('vite-error-overlay')).toBe(false)
33+
await expect.poll(() => page.textContent('.app')).toBe('hello')
34+
})
35+
36+
// BUNDLED -> GENERATE_HMR_PATCH -> BUNDLING -> BUNDLED
37+
test('update bundle', async () => {
38+
editFile('main.js', (code) =>
39+
code.replace("text('.app', 'hello')", "text('.app', 'hello1')"),
40+
)
41+
await expect.poll(() => page.textContent('.app')).toBe('hello1')
42+
43+
editFile('main.js', (code) =>
44+
code.replace("text('.app', 'hello1')", "text('.app', 'hello')"),
45+
)
46+
await expect.poll(() => page.textContent('.app')).toBe('hello')
47+
})
48+
49+
// BUNDLED -> GENERATE_HMR_PATCH -> BUNDLING -> BUNDLING -> BUNDLED
50+
test('debounce bundle', async () => {
51+
editFile('main.js', (code) =>
52+
code.replace(
53+
"text('.app', 'hello')",
54+
"text('.app', 'hello1')\n" + '// @delay-transform',
55+
),
56+
)
57+
await setTimeout(100)
58+
editFile('main.js', (code) =>
59+
code.replace("text('.app', 'hello1')", "text('.app', 'hello2')"),
60+
)
61+
await expect.poll(() => page.textContent('.app')).toBe('hello2')
62+
63+
editFile('main.js', (code) =>
64+
code.replace(
65+
"text('.app', 'hello2')\n" + '// @delay-transform',
66+
"text('.app', 'hello')",
67+
),
68+
)
69+
await expect.poll(() => page.textContent('.app')).toBe('hello')
70+
})
71+
72+
// BUNDLED -> GENERATING_HMR_PATCH -> BUNDLED
73+
test('handle generate hmr patch error', async () => {
74+
await expect.poll(() => page.textContent('.hmr')).toBe('hello')
75+
editFile('hmr.js', (code) =>
76+
code.replace("const foo = 'hello'", "const foo = 'hello"),
77+
)
78+
await expect.poll(() => page.isVisible('vite-error-overlay')).toBe(true)
79+
80+
editFile('hmr.js', (code) =>
81+
code.replace("const foo = 'hello", "const foo = 'hello'"),
82+
)
83+
await expect.poll(() => page.isVisible('vite-error-overlay')).toBe(false)
84+
await expect.poll(() => page.textContent('.hmr')).toContain('hello')
85+
})
86+
87+
// BUNDLED -> GENERATING_HMR_PATCH -> BUNDLED
88+
test('generate hmr patch', async () => {
89+
await expect.poll(() => page.textContent('.hmr')).toBe('hello')
90+
editFile('hmr.js', (code) =>
91+
code.replace("const foo = 'hello'", "const foo = 'hello1'"),
92+
)
93+
await expect.poll(() => page.textContent('.hmr')).toBe('hello1')
94+
95+
editFile('hmr.js', (code) =>
96+
code.replace("const foo = 'hello1'", "const foo = 'hello'"),
97+
)
98+
await expect.poll(() => page.textContent('.hmr')).toContain('hello')
99+
})
100+
101+
// BUNDLED -> GENERATING_HMR_PATCH -> GENERATING_HMR_PATCH -> BUNDLED
102+
test('continuous generate hmr patch', async () => {
103+
editFile('hmr.js', (code) =>
104+
code.replace(
105+
"const foo = 'hello'",
106+
"const foo = 'hello1'\n" + '// @delay-transform',
107+
),
108+
)
109+
await setTimeout(100)
110+
editFile('hmr.js', (code) =>
111+
code.replace("const foo = 'hello1'", "const foo = 'hello2'"),
112+
)
113+
await expect.poll(() => page.textContent('.hmr')).toBe('hello2')
114+
115+
editFile('hmr.js', (code) =>
116+
code.replace(
117+
"const foo = 'hello2'\n" + '// @delay-transform',
118+
"const foo = 'hello'",
119+
),
120+
)
121+
await expect.poll(() => page.textContent('.hmr')).toBe('hello')
122+
})
123+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const foo = 'hello'
2+
3+
text('.hmr', foo)
4+
5+
function text(el, text) {
6+
document.querySelector(el).textContent = text
7+
}
8+
9+
import.meta.hot?.accept((mod) => {
10+
if (mod) {
11+
text('.hmr', mod.foo)
12+
}
13+
})
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<h1>HMR Full Bundle Mode</h1>
2+
3+
<div class="app"></div>
4+
<div class="hmr"></div>
5+
6+
<script type="module" src="./main.js"></script>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import './hmr.js'
2+
3+
text('.app', 'hello')
4+
5+
function text(el, text) {
6+
document.querySelector(el).textContent = text
7+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "@vitejs/test-hmr-full-bundle-mode",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "vite build",
9+
"debug": "node --inspect-brk ../../packages/vite/bin/vite",
10+
"preview": "vite preview"
11+
}
12+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { type Plugin, defineConfig } from 'vite'
2+
3+
export default defineConfig({
4+
experimental: {
5+
fullBundleMode: true,
6+
},
7+
plugins: [waitBundleCompleteUntilAccess(), delayTransformComment()],
8+
})
9+
10+
function waitBundleCompleteUntilAccess(): Plugin {
11+
let resolvers: PromiseWithResolvers<void>
12+
13+
return {
14+
name: 'wait-bundle-complete-until-access',
15+
apply: 'serve',
16+
configureServer(server) {
17+
let accessCount = 0
18+
resolvers = promiseWithResolvers()
19+
20+
server.middlewares.use((_req, _res, next) => {
21+
accessCount++
22+
if (accessCount === 1) {
23+
resolvers.resolve()
24+
}
25+
next()
26+
})
27+
},
28+
async generateBundle() {
29+
await resolvers.promise
30+
await new Promise<void>((resolve) => setTimeout(resolve, 300))
31+
},
32+
}
33+
}
34+
35+
function delayTransformComment(): Plugin {
36+
return {
37+
name: 'delay-transform-comment',
38+
async transform(code) {
39+
if (code.includes('// @delay-transform')) {
40+
await new Promise<void>((resolve) => setTimeout(resolve, 300))
41+
}
42+
},
43+
}
44+
}
45+
46+
interface PromiseWithResolvers<T> {
47+
promise: Promise<T>
48+
resolve: (value: T | PromiseLike<T>) => void
49+
reject: (reason?: any) => void
50+
}
51+
function promiseWithResolvers<T>(): PromiseWithResolvers<T> {
52+
let resolve: any
53+
let reject: any
54+
const promise = new Promise<T>((_resolve, _reject) => {
55+
resolve = _resolve
56+
reject = _reject
57+
})
58+
return { promise, resolve, reject }
59+
}

pnpm-lock.yaml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)