Skip to content

Commit 9139207

Browse files
authored
Kit playground - basic tests to ensure changes in vite-plugin-svelte don't break kit (#8)
* wip: add a kit project to playground for testing * wip: first working version for kit tests * wip: more hmr tests for kit, ensure kit process is taken down after test * wip: add testto ensure edited content is served on page reload * wip: update deps * rename kit playground
1 parent 18647aa commit 9139207

24 files changed

+584
-37
lines changed

.prettierrc.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ module.exports = {
1818
options: {
1919
requirePragma: true
2020
}
21+
},
22+
{
23+
files: '**/package.json',
24+
options: {
25+
useTabs: false,
26+
tabWidth: 2
27+
}
2128
}
2229
]
2330
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"test:build": "cross-env VITE_TEST_BUILD=1 jest",
1313
"test:build:debug": "cross-env VITE_TEST_BUILD=1 VITE_PRESERVE_BUILD_ARTIFACTS=1 node --inspect-brk ./node_modules/.bin/jest",
1414
"test:ci": "run-s test:ci:serve test:ci:build",
15-
"test:ci:serve": "cross-env VITE_PRESERVE_BUILD_ARTIFACTS=1 jest --verbose --no-cache --runInBand --force-exit --ci --json --outputFile=\"temp/serve/jest-results.json\" ",
15+
"test:ci:serve": "cross-env VITE_PRESERVE_BUILD_ARTIFACTS=1 jest --verbose --no-cache --runInBand --force-exit --ci --json --outputFile=\"temp/serve/jest-results.json\"",
1616
"test:ci:build": "cross-env VITE_TEST_BUILD=1 VITE_PRESERVE_BUILD_ARTIFACTS=1 jest --verbose --no-cache --runInBand --force-exit --ci --json --outputFile=\"temp/build/jest-results.json\"",
1717
"lint": "run-p lint:script lint:style",
1818
"lint:script": "eslint --ignore-path .gitignore '**/*.{js,ts,svelte,html,svx,md}'",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.DS_Store
2+
node_modules
3+
/.svelte
4+
/build
5+
/functions
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# create-svelte
2+
3+
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte);
4+
5+
## Creating a project
6+
7+
If you're seeing this, you've probably already done this step. Congrats!
8+
9+
```bash
10+
# create a new project in the current directory
11+
npm init svelte@next
12+
13+
# create a new project in my-app
14+
npm init svelte@next my-app
15+
```
16+
17+
> Note: the `@next` is temporary
18+
19+
## Developing
20+
21+
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
22+
23+
```bash
24+
npm run dev
25+
26+
# or start the server and open the app in a new browser tab
27+
npm run dev -- --open
28+
```
29+
30+
## Building
31+
32+
Svelte apps are built with _adapters_, which optimise your project for deployment to different environments.
33+
34+
By default, `npm run build` will generate a Node app that you can run with `node build`. To use a different adapter, add it to the `devDependencies` in `package.json` making sure to specify the version as `next` and update your `svelte.config.cjs` to [specify your chosen adapter](https://kit.svelte.dev/docs#configuration-adapter). The following official adapters are available:
35+
36+
- [@sveltejs/adapter-node](https://github.com/sveltejs/kit/tree/master/packages/adapter-node)
37+
- [@sveltejs/adapter-static](https://github.com/sveltejs/kit/tree/master/packages/adapter-static)
38+
- [@sveltejs/adapter-netlify](https://github.com/sveltejs/kit/tree/master/packages/adapter-netlify)
39+
- [@sveltejs/adapter-vercel](https://github.com/sveltejs/kit/tree/master/packages/adapter-vercel)
40+
- ...more soon
41+
42+
[See the adapter documentation for more detail](https://kit.svelte.dev/docs#adapters)
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import {
2+
editFileAndWaitForHmrComplete,
3+
getColor,
4+
getEl,
5+
getText,
6+
isBuild,
7+
untilUpdated
8+
} from '../../testUtils';
9+
10+
import fetch from 'node-fetch';
11+
12+
describe('kit-node', () => {
13+
describe('index route', () => {
14+
it('should contain greeting', async () => {
15+
// TODO is hydration testing needed here?
16+
expect(await page.textContent('h1')).toMatch('Hello world!'); // after hydration
17+
18+
const html = await (await fetch(page.url())).text();
19+
expect(html).toMatch('Hello world!'); // before hydration
20+
if (isBuild) {
21+
// TODO additional testing needed here once vite-plugin-svelte implements indexHtmlTransform hook
22+
}
23+
});
24+
25+
it('should have correct styles applied', async () => {
26+
if (isBuild) {
27+
expect(await getColor('h1')).toBe('rgb(255, 62, 0)');
28+
} else {
29+
// During dev, the CSS is loaded from async chunk and we may have to wait
30+
// when the test runs concurrently.
31+
await untilUpdated(() => getColor('h1'), 'rgb(255, 62, 0)');
32+
}
33+
});
34+
35+
it('should increase count on click', async () => {
36+
const button = await getEl('button');
37+
expect(await getText(button)).toBe('Clicks: 0');
38+
await button.click();
39+
expect(await getText(button)).toBe('Clicks: 1');
40+
});
41+
42+
it('should not have failed requests', async () => {
43+
// should have no 404s
44+
browserLogs.forEach((msg) => {
45+
expect(msg).not.toMatch('404');
46+
});
47+
});
48+
49+
if (!isBuild) {
50+
describe('hmr', () => {
51+
const updateIndexSvelte = editFileAndWaitForHmrComplete.bind(
52+
null,
53+
'src/routes/index.svelte'
54+
);
55+
56+
it('should render additional html', async () => {
57+
// add div 1
58+
expect(await getEl('#hmr-test')).toBe(null);
59+
await updateIndexSvelte((content) =>
60+
content.replace(
61+
'<!-- HMR-TEMPLATE-INJECT -->',
62+
'<div id="hmr-test">foo</div>\n<!-- HMR-TEMPLATE-INJECT -->'
63+
)
64+
);
65+
await expect(await getText(`#hmr-test`)).toBe('foo');
66+
67+
// add div 2
68+
expect(await getEl('#hmr-test2')).toBe(null);
69+
await updateIndexSvelte((content) =>
70+
content.replace(
71+
'<!-- HMR-TEMPLATE-INJECT -->',
72+
'<div id="hmr-test2">bar</div>\n<!-- HMR-TEMPLATE-INJECT -->'
73+
)
74+
);
75+
await expect(await getText(`#hmr-test`)).toBe('foo');
76+
await expect(await getText(`#hmr-test2`)).toBe('bar');
77+
// remove div 1
78+
await updateIndexSvelte((content) =>
79+
content.replace('<div id="hmr-test">foo</div>\n', '')
80+
);
81+
await expect(await getText(`#hmr-test`)).toBe(null);
82+
await expect(await getText(`#hmr-test2`)).toBe('bar');
83+
});
84+
85+
it('should render additional child components', async () => {
86+
let buttons = await page.$$('button');
87+
expect(buttons.length).toBe(1);
88+
expect(await getText(buttons[0])).toBe('Clicks: 0');
89+
await updateIndexSvelte((content) =>
90+
content.replace(
91+
'<!-- HMR-TEMPLATE-INJECT -->',
92+
'<Counter id="hmr-test-counter"/>\n<!-- HMR-TEMPLATE-INJECT -->'
93+
)
94+
);
95+
buttons = await page.$$('button');
96+
expect(buttons.length).toBe(2);
97+
expect(await getText(buttons[0])).toBe('Clicks: 0');
98+
expect(await getText(buttons[1])).toBe('Clicks: 0');
99+
await buttons[1].click();
100+
expect(await getText(buttons[0])).toBe('Clicks: 0');
101+
expect(await getText(buttons[1])).toBe('Clicks: 1');
102+
await updateIndexSvelte((content) =>
103+
content.replace('<Counter id="hmr-test-counter"/>\n', '')
104+
);
105+
buttons = await page.$$('button');
106+
expect(buttons.length).toBe(1);
107+
expect(await getText(buttons[0])).toBe('Clicks: 0');
108+
});
109+
110+
it('should apply changed styles', async () => {
111+
expect(await getColor(`h1`)).toBe('rgb(255, 62, 0)');
112+
await updateIndexSvelte((content) => content.replace('color: #ff3e00', 'color: blue'));
113+
expect(await getColor(`h1`)).toBe('blue');
114+
await updateIndexSvelte((content) => content.replace('color: blue', 'color: green'));
115+
expect(await getColor(`h1`)).toBe('green');
116+
});
117+
118+
it('should serve changes even after page reload', async () => {
119+
expect(await getColor(`h1`)).toBe('green');
120+
expect(await getText(`#hmr-test2`)).toBe('bar');
121+
await page.reload({ waitUntil: 'networkidle' });
122+
expect(await getColor(`h1`)).toBe('green');
123+
await expect(await getText(`#hmr-test2`)).toBe('bar');
124+
});
125+
126+
describe('Counter.svelte', () => {
127+
const updateCounter = editFileAndWaitForHmrComplete.bind(null, 'src/lib/Counter.svelte');
128+
it('should render additional html', async () => {
129+
// add div 1
130+
expect(await getEl('#hmr-test3')).toBe(null);
131+
await updateCounter((content) =>
132+
content.replace(
133+
'<!-- HMR-TEMPLATE-INJECT -->',
134+
'<div id="hmr-test3">foo</div>\n<!-- HMR-TEMPLATE-INJECT -->'
135+
)
136+
);
137+
await expect(await getText(`#hmr-test3`)).toBe('foo');
138+
139+
// add div 2
140+
expect(await getEl('#hmr-test4')).toBe(null);
141+
await updateCounter((content) =>
142+
content.replace(
143+
'<!-- HMR-TEMPLATE-INJECT -->',
144+
'<div id="hmr-test4">bar</div>\n<!-- HMR-TEMPLATE-INJECT -->'
145+
)
146+
);
147+
await expect(await getText(`#hmr-test3`)).toBe('foo');
148+
await expect(await getText(`#hmr-test4`)).toBe('bar');
149+
// remove div 1
150+
await updateCounter((content) =>
151+
content.replace('<div id="hmr-test3">foo</div>\n', '')
152+
);
153+
await expect(await getText(`#hmr-test3`)).toBe(null);
154+
await expect(await getText(`#hmr-test4`)).toBe('bar');
155+
});
156+
157+
it('should apply changed styles', async () => {
158+
expect(await getColor(`button`)).toBe('rgb(255, 62, 0)');
159+
await updateCounter((content) => content.replace('color: #ff3e00', 'color: blue'));
160+
expect(await getColor(`button`)).toBe('blue');
161+
await updateCounter((content) => content.replace('color: blue', 'color: green'));
162+
expect(await getColor(`button`)).toBe('green');
163+
});
164+
165+
it('should apply changed initial state', async () => {
166+
expect(await getText('button')).toBe('Clicks: 0');
167+
await updateCounter((content) => content.replace('let count = 0', 'let count = 2'));
168+
expect(await getText('button')).toBe('Clicks: 2');
169+
await updateCounter((content) => content.replace('let count = 2', 'let count = 0'));
170+
expect(await getText('button')).toBe('Clicks: 0');
171+
});
172+
});
173+
});
174+
}
175+
});
176+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// @ts-check
2+
// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
3+
// the default e2e test serve behavior
4+
5+
const execa = require('execa');
6+
const treeKill = require('tree-kill');
7+
/**
8+
* @param {string} root
9+
* @param {boolean} isProd
10+
*/
11+
exports.serve = async function serve(root, isProd) {
12+
if (isProd) {
13+
await execa('svelte-kit', ['build'], { stdio: 'inherit', preferLocal: true, cwd: root });
14+
}
15+
16+
return new Promise((resolve, reject) => {
17+
try {
18+
const serverProcess = execa(
19+
'svelte-kit',
20+
[isProd ? 'start' : 'dev', '--port', isProd ? '3200' : '3201'],
21+
{
22+
preferLocal: true,
23+
cwd: root,
24+
cleanup: true
25+
}
26+
);
27+
serverProcess.stdout.pipe(process.stdout);
28+
serverProcess.stderr.pipe(process.stderr);
29+
30+
const resolveWhenStarted = (data) => {
31+
const str = data.toString();
32+
// hack, console output may contain color code gibberish
33+
// skip gibberish between localhost: and port number starting with 3
34+
const match = str.match(/(http:\/\/localhost:)(?:[^3]*)(\d+)/);
35+
if (match) {
36+
serverProcess.stdout.off('data', resolveWhenStarted);
37+
const customServer = {
38+
port: parseInt(match[2], 10),
39+
async close() {
40+
if (serverProcess) {
41+
// ensure started svelte-kit process is gone including all subprocesses
42+
return new Promise((resolve, reject) =>
43+
treeKill(serverProcess.pid, (err) => {
44+
err ? reject(err) : resolve();
45+
})
46+
);
47+
}
48+
}
49+
};
50+
resolve(customServer);
51+
}
52+
};
53+
54+
serverProcess.stdout.on('data', resolveWhenStarted);
55+
} catch (e) {
56+
reject(e);
57+
}
58+
});
59+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"baseUrl": ".",
4+
"paths": {
5+
"$app/*": [".svelte/dev/runtime/app/*", ".svelte/build/runtime/app/*"],
6+
"$lib/*": ["src/lib/*"]
7+
}
8+
},
9+
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
10+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "playground-kit-node",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "svelte-kit dev",
7+
"build": "svelte-kit build",
8+
"start": "svelte-kit start"
9+
},
10+
"devDependencies": {
11+
"@sveltejs/adapter-node": "^1.0.0-next.12",
12+
"@sveltejs/kit": "^1.0.0-next.71",
13+
"svelte": "^3.29.0",
14+
"vite": "^2.1.0"
15+
},
16+
"type": "module",
17+
"engines": {
18+
"node": ">= 12.17.0"
19+
}
20+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="/favicon.ico" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
%svelte.head%
8+
</head>
9+
<body>
10+
<div id="svelte">%svelte.body%</div>
11+
</body>
12+
</html>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/// <reference types="@sveltejs/kit" />
2+
/// <reference types="svelte" />
3+
/// <reference types="vite/client" />
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script>
2+
let count = 0;
3+
4+
const increment = () => {
5+
count += 1;
6+
};
7+
</script>
8+
9+
<button on:click={increment}>
10+
Clicks: {count}
11+
</button>
12+
13+
<!-- HMR-TEMPLATE-INJECT -->
14+
<style>
15+
button {
16+
font-family: inherit;
17+
font-size: inherit;
18+
padding: 1em 2em;
19+
color: #ff3e00;
20+
background-color: rgba(255, 62, 0, 0.1);
21+
border-radius: 2em;
22+
border: 2px solid rgba(255, 62, 0, 0);
23+
outline: none;
24+
width: 200px;
25+
font-variant-numeric: tabular-nums;
26+
}
27+
28+
button:focus {
29+
border: 2px solid #ff3e00;
30+
}
31+
32+
button:active {
33+
background-color: rgba(255, 62, 0, 0.2);
34+
}
35+
</style>

0 commit comments

Comments
 (0)