/
hmr.spec.ts
233 lines (212 loc) · 9.26 KB
/
hmr.spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
import {
isBuild,
isWin,
getEl,
getText,
editFileAndWaitForHmrComplete,
hmrCount,
untilMatches,
sleep,
getColor,
editFile,
addFile,
removeFile,
editViteConfig,
page,
browserLogs,
viteTestUrl
} from '~utils';
test('should render App', async () => {
expect(await getText('#app-header')).toBe('Test-App');
});
test('should render static import', async () => {
expect(await getText('#static-import .label')).toBe('static-import');
});
test('should render dependency import', async () => {
expect(await getText('#dependency-import .label')).toBe('dependency-import');
});
test('should render dynamic import', async () => {
expect(await getEl('#dynamic-import')).toBe(null);
const dynamicImportButton = await getEl('#button-import-dynamic');
expect(dynamicImportButton).toBeDefined();
await dynamicImportButton.click();
await untilMatches(
() => getText('#dynamic-import .label'),
'dynamic-import',
'dynamic import loaded after click'
);
});
test('should not have failed requests', async () => {
browserLogs.forEach((msg) => {
expect(msg).not.toMatch('404');
});
});
test('should respect transforms', async () => {
expect(await getText('#js-transform')).toBe('Hello world');
expect(await getColor('#css-transform')).toBe('red');
});
if (!isBuild) {
describe('hmr', () => {
const updateHmrTest = editFileAndWaitForHmrComplete.bind(null, 'src/components/HmrTest.svelte');
const updateModuleContext = editFileAndWaitForHmrComplete.bind(
null,
'src/components/partial-hmr/ModuleContext.svelte'
);
const updateApp = editFileAndWaitForHmrComplete.bind(null, 'src/App.svelte');
const updateStore = editFileAndWaitForHmrComplete.bind(null, 'src/stores/hmr-stores.js');
test('should have expected initial state', async () => {
// initial state, both counters 0, both labels red
expect(await getText(`#hmr-test-1 .counter`)).toBe('0');
expect(await getText(`#hmr-test-2 .counter`)).toBe('0');
expect(await getText(`#hmr-test-1 .label`)).toBe('hmr-test');
expect(await getText(`#hmr-test-2 .label`)).toBe('hmr-test');
expect(await getColor(`#hmr-test-1 .label`)).toBe('red');
expect(await getColor(`#hmr-test-2 .label`)).toBe('red');
});
test('should have working increment button', async () => {
// increment counter of one instance to have local state to verify after hmr updates
await (await getEl(`#hmr-test-1 .increment`)).click();
await sleep(50);
// counter1 = 1, counter2 = 0
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
expect(await getText(`#hmr-test-2 .counter`)).toBe('0');
});
test('should apply css changes in HmrTest.svelte', async () => {
// update style, change label color from red to green
await updateHmrTest((content) => content.replace('color: red', 'color: green'));
// counter state should remain
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
expect(await getText(`#hmr-test-2 .counter`)).toBe('0');
// color should have changed
expect(await getColor(`#hmr-test-1 .label`)).toBe('green');
expect(await getColor(`#hmr-test-2 .label`)).toBe('green');
});
test('should apply js change in HmrTest.svelte ', async () => {
// update script, change label value
await updateHmrTest((content) =>
content.replace("const label = 'hmr-test'", "const label = 'hmr-test-updated'")
);
expect(await getText(`#hmr-test-1 .label`)).toBe('hmr-test-updated');
expect(await getText(`#hmr-test-2 .label`)).toBe('hmr-test-updated');
});
test('should keep state of external store intact on change of HmrTest.svelte', async () => {
// counter state should remain
await updateHmrTest((content) =>
content.replace('<!-- HMR-TEMPLATE-INJECT -->', '<span/>\n<!-- HMR-TEMPLATE-INJECT -->')
);
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
expect(await getText(`#hmr-test-2 .counter`)).toBe('0');
});
test('should preserve state of external store used by HmrTest.svelte when editing App.svelte', async () => {
// update App, add a new instance of HmrTest
await updateApp((content) =>
content.replace(
'<!-- HMR-TEMPLATE-INJECT -->',
'<HmrTest id="hmr-test-3"/>\n<!-- HMR-TEMPLATE-INJECT -->'
)
);
// counter state is preserved
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
expect(await getText(`#hmr-test-2 .counter`)).toBe('0');
// a third instance has been added
expect(await getText(`#hmr-test-3 .counter`)).toBe('0');
});
test('should preserve state of store when editing hmr-stores.js', async () => {
// change state
await (await getEl(`#hmr-test-2 .increment`)).click();
await sleep(50);
// update store
await updateStore((content) => `${content}\n/*trigger change*/\n`);
// counter state is preserved
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
expect(await getText(`#hmr-test-2 .counter`)).toBe('1');
// a third instance has been added
expect(await getText(`#hmr-test-3 .counter`)).toBe('0');
});
test('should work when editing script context="module"', async () => {
expect(await getText(`#hmr-with-context`)).toContain('x=0 y=1 slot=1');
expect(await getText(`#hmr-without-context`)).toContain('x=0 y=1 slot=');
expect(hmrCount('UsingNamed.svelte'), 'updates for UsingNamed.svelte').toBe(0);
expect(hmrCount('UsingDefault.svelte'), 'updates for UsingDefault.svelte').toBe(0);
await updateModuleContext((content) => content.replace('y = 1', 'y = 2'));
expect(await getText(`#hmr-with-context`)).toContain('x=0 y=2 slot=2');
expect(await getText(`#hmr-without-context`)).toContain('x=0 y=2 slot=');
expect(hmrCount('UsingNamed.svelte'), 'updates for UsingNamed.svelte').toBe(1);
expect(hmrCount('UsingDefault.svelte'), 'updates for UsingDefault.svelte').toBe(0);
});
test('should work with emitCss: false in vite config', async () => {
await editViteConfig((c) => c.replace('svelte()', 'svelte({emitCss:false})'));
expect(await getText(`#hmr-test-1 .counter`)).toBe('0');
expect(await getColor(`#hmr-test-1 .label`)).toBe('green');
await (await getEl(`#hmr-test-1 .increment`)).click();
await sleep(50);
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
await updateHmrTest((content) => content.replace('color: green', 'color: red'));
expect(await getColor(`#hmr-test-1 .label`)).toBe('red');
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
});
test('should work with emitCss: false in svelte config', async () => {
addFile('svelte.config.cjs', `module.exports={vitePlugin:{emitCss:false}}`);
await sleep(isWin ? 1000 : 500); // adding config restarts server, give it some time
await page.goto(viteTestUrl, { waitUntil: 'networkidle' });
await sleep(50);
expect(await getColor(`#hmr-test-1 .label`)).toBe('red');
removeFile('svelte.config.cjs');
});
test('should detect changes in svelte config and restart', async () => {
const injectPreprocessor = ({ content, filename }) => {
if (filename && filename.includes('App.svelte')) {
return {
code: content.replace(
'<!-- HMR-TEMPLATE-INJECT -->',
'<div id="preprocess-inject">Injected</div>\n<!-- HMR-TEMPLATE-INJECT -->'
)
};
}
};
await addFile(
'svelte.config.cjs',
`module.exports = {
preprocess:[{markup:${injectPreprocessor.toString()}}]};`
);
await sleep(isWin ? 1000 : 500); // adding config restarts server, give it some time
await page.goto(viteTestUrl, { waitUntil: 'networkidle' });
await sleep(50);
expect(await getText('#preprocess-inject')).toBe('Injected');
expect(await getText(`#hmr-test-1 .counter`)).toBe('0');
expect(await getColor(`#hmr-test-1 .label`)).toBe('red');
await (await getEl(`#hmr-test-1 .increment`)).click();
await sleep(50);
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
await updateHmrTest((content) => content.replace('color: red', 'color: green'));
expect(await getColor(`#hmr-test-1 .label`)).toBe('green');
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
await editFile('svelte.config.cjs', (content) =>
content
.replace('preprocess-inject', 'preprocess-inject-2')
.replace('Injected', 'Injected 2')
);
await sleep(isWin ? 1000 : 500); // editing config restarts server, give it some time
await page.goto(viteTestUrl, { waitUntil: 'networkidle' });
await sleep(50);
expect(await getText('#preprocess-inject-2')).toBe('Injected 2');
expect(await getEl('#preprocess-inject')).toBe(null);
expect(await getColor(`#hmr-test-1 .label`)).toBe('green');
expect(await getText(`#hmr-test-1 .counter`)).toBe('0');
await (await getEl(`#hmr-test-1 .increment`)).click();
await sleep(50);
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
await updateHmrTest((content) => content.replace('color: green', 'color: red'));
expect(await getColor(`#hmr-test-1 .label`)).toBe('red');
expect(await getText(`#hmr-test-1 .counter`)).toBe('1');
await removeFile('svelte.config.cjs');
await sleep(isWin ? 1000 : 500); // editing config restarts server, give it some time
await page.goto(viteTestUrl, { waitUntil: 'networkidle' });
await sleep(50);
expect(await getEl('#preprocess-inject-2')).toBe(null);
expect(await getEl('#preprocess-inject')).toBe(null);
expect(await getColor(`#hmr-test-1 .label`)).toBe('red');
expect(await getText(`#hmr-test-1 .counter`)).toBe('0');
});
});
}