Skip to content

Commit 43f45f8

Browse files
committed
test: add test for basic scenarios
1 parent 8db9ad7 commit 43f45f8

File tree

7 files changed

+228
-0
lines changed

7 files changed

+228
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { setTimeout } from 'node:timers/promises'
2+
import { expect, test } from 'vitest'
3+
import { editFile, expectWithRetry, 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 expectWithRetry(() => page.textContent('.app')).toBe('hello')
9+
await expectWithRetry(() => 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 expectWithRetry(() => page.textContent('body')).toContain(
16+
'Bundling in progress',
17+
)
18+
await reloadPromise // page shown after reload
19+
await expectWithRetry(() => page.textContent('h1')).toBe(
20+
'HMR Full Bundle Mode',
21+
)
22+
await expectWithRetry(() => page.textContent('.app')).toBe('hello')
23+
})
24+
25+
// BUNDLED -> GENERATE_HMR_PATCH -> BUNDLING -> BUNDLE_ERROR -> BUNDLING -> BUNDLED
26+
test('handle bundle error', async () => {
27+
editFile('main.js', (code) =>
28+
code.replace("text('.app', 'hello')", "text('.app', 'hello'); text("),
29+
)
30+
await expectWithRetry(() => page.isVisible('vite-error-overlay')).toBe(true)
31+
editFile('main.js', (code) =>
32+
code.replace("text('.app', 'hello'); text(", "text('.app', 'hello')"),
33+
)
34+
await expectWithRetry(() => page.isVisible('vite-error-overlay')).toBe(
35+
false,
36+
)
37+
await expectWithRetry(() => page.textContent('.app')).toBe('hello')
38+
})
39+
40+
// BUNDLED -> GENERATE_HMR_PATCH -> BUNDLING -> BUNDLED
41+
test('update bundle', async () => {
42+
editFile('main.js', (code) =>
43+
code.replace("text('.app', 'hello')", "text('.app', 'hello1')"),
44+
)
45+
await expectWithRetry(() => page.textContent('.app')).toBe('hello1')
46+
47+
editFile('main.js', (code) =>
48+
code.replace("text('.app', 'hello1')", "text('.app', 'hello')"),
49+
)
50+
await expectWithRetry(() => page.textContent('.app')).toBe('hello')
51+
})
52+
53+
// BUNDLED -> GENERATE_HMR_PATCH -> BUNDLING -> BUNDLING -> BUNDLED
54+
test('debounce bundle', async () => {
55+
editFile('main.js', (code) =>
56+
code.replace(
57+
"text('.app', 'hello')",
58+
"text('.app', 'hello1')\n" + '// @delay-transform',
59+
),
60+
)
61+
await setTimeout(100)
62+
editFile('main.js', (code) =>
63+
code.replace("text('.app', 'hello1')", "text('.app', 'hello2')"),
64+
)
65+
await expectWithRetry(() => page.textContent('.app')).toBe('hello2')
66+
67+
editFile('main.js', (code) =>
68+
code.replace(
69+
"text('.app', 'hello2')\n" + '// @delay-transform',
70+
"text('.app', 'hello')",
71+
),
72+
)
73+
await expectWithRetry(() => page.textContent('.app')).toBe('hello')
74+
})
75+
76+
// BUNDLED -> GENERATING_HMR_PATCH -> BUNDLED
77+
test('handle generate hmr patch error', async () => {
78+
await expectWithRetry(() => page.textContent('.hmr')).toBe('hello')
79+
editFile('hmr.js', (code) =>
80+
code.replace("const foo = 'hello'", "const foo = 'hello"),
81+
)
82+
await expectWithRetry(() => page.isVisible('vite-error-overlay')).toBe(true)
83+
84+
editFile('hmr.js', (code) =>
85+
code.replace("const foo = 'hello", "const foo = 'hello'"),
86+
)
87+
await expectWithRetry(() => page.isVisible('vite-error-overlay')).toBe(
88+
false,
89+
)
90+
await expectWithRetry(() => page.textContent('.hmr')).toContain('hello')
91+
})
92+
93+
// BUNDLED -> GENERATING_HMR_PATCH -> BUNDLED
94+
test('generate hmr patch', async () => {
95+
await expectWithRetry(() => page.textContent('.hmr')).toBe('hello')
96+
editFile('hmr.js', (code) =>
97+
code.replace("const foo = 'hello'", "const foo = 'hello1'"),
98+
)
99+
await expectWithRetry(() => page.textContent('.hmr')).toBe('hello1')
100+
101+
editFile('hmr.js', (code) =>
102+
code.replace("const foo = 'hello1'", "const foo = 'hello'"),
103+
)
104+
await expectWithRetry(() => page.textContent('.hmr')).toContain('hello')
105+
})
106+
107+
// BUNDLED -> GENERATING_HMR_PATCH -> GENERATING_HMR_PATCH -> BUNDLED
108+
test('continuous generate hmr patch', async () => {
109+
editFile('hmr.js', (code) =>
110+
code.replace(
111+
"const foo = 'hello'",
112+
"const foo = 'hello1'\n" + '// @delay-transform',
113+
),
114+
)
115+
await setTimeout(100)
116+
editFile('hmr.js', (code) =>
117+
code.replace("const foo = 'hello1'", "const foo = 'hello2'"),
118+
)
119+
await expectWithRetry(() => page.textContent('.hmr')).toBe('hello2')
120+
121+
editFile('hmr.js', (code) =>
122+
code.replace(
123+
"const foo = 'hello2'\n" + '// @delay-transform',
124+
"const foo = 'hello'",
125+
),
126+
)
127+
await expectWithRetry(() => page.textContent('.hmr')).toBe('hello')
128+
})
129+
}
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)