Skip to content

Commit 945214a

Browse files
authored
ci: add tests (#48)
* ci: add tests * chore: remove vitest imports docs: update contributing guide * chore: update node adapter test message * chore: update contributing guide * chore: use `existsSync` and `readFileSync` in test * chore: rollback changes in package.json files + fix lint * chore: add `preview-node` script to example * chore: update pnpm to 8.4.0 * ci: add client tests (#50) * ci: add client tests * chore: use `Promise.race` when awaiting sw registration * docs: add installing Playwright Chromium browser hint before running tests * chore: update to pnpm 8.5.0 * chore: update browser test description * chore: remove Vitest globals (IntelliJ complains) * chrore: update vitest and lock file
1 parent 921856e commit 945214a

File tree

13 files changed

+561
-45
lines changed

13 files changed

+561
-45
lines changed

.eslintrc.json

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
11
{
2-
"extends": ["@antfu"],
3-
"overrides": [
4-
{
5-
"files": [
6-
"**/*.md/*.*"
7-
],
8-
"rules": {
9-
"@typescript-eslint/no-this-alias": "off",
10-
"n/handle-callback-err": "off",
11-
"no-restricted-syntax": "off",
12-
"no-labels": "off"
13-
}
14-
}
15-
]
2+
"extends": ["@antfu"]
163
}

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ dist
44
dev-dist
55
# intellij stuff
66
.idea/
7-
# routify
8-
.routify/
7+
/test-results/
8+
/playwright-report/
9+
/playwright/.cache/

CONTRIBUTING.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Hi! We are really excited that you are interested in contributing to `@vite-pwa/sveltekit`. Before submitting your contribution, please make sure to take a moment and read through the following guide.
44

55
Refer also to https://github.com/antfu/contribute.
6+
67
## Set up your local development environment
78

89
The `@vite-pwa/sveltekit` repo is a monorepo using pnpm workspaces. The package manager used to install and link dependencies must be [pnpm](https://pnpm.io/).
@@ -13,7 +14,7 @@ To develop and test the `@vite-pwa/sveltekit` package:
1314

1415
2. Ensure using the latest Node.js (16.14+)
1516

16-
3. `@vite-pwa/sveltekit` uses pnpm v7. If you are working on multiple projects with different versions of pnpm, it's recommend to enable [Corepack](https://github.com/nodejs/corepack) by running `corepack enable`.
17+
3. `@vite-pwa/sveltekit` uses pnpm v8. If you are working on multiple projects with different versions of pnpm, it's recommend to enable [Corepack](https://github.com/nodejs/corepack) by running `corepack enable`.
1718

1819
4. Check out a branch where you can work and commit your changes:
1920
```shell
@@ -26,15 +27,12 @@ git checkout -b my-new-branch
2627

2728
## Testing changes
2829

29-
The `vite-plugin-pwa` repo includes a set of examples where you can test the changes (you can find them on examples folder), you should check your changes against each framework using the `cli` for running examples:
30-
- `pnpm run examples`: select `vue` framework and default options on the cli (you can also test another options)
31-
- `pnpm run examples`: select `react` framework and default options on the cli (you can also test another options)
32-
- `pnpm run examples`: select `preact` framework and default options on the cli (you can also test another options)
33-
- `pnpm run examples`: select `svelte` framework and default options on the cli (you can also test another options)
34-
- `pnpm run examples`: select `sveltekit` framework and default options on the cli (you can also test another options)
35-
- `pnpm run examples`: select `solid` framework and default options on the cli (you can also test another options)
30+
To test your changes locally, change to `examples/sveltekit-ts` folder and run `pnpm run build-<example> && pnpm run preview` where `<example>` is the name of the example you want to test.
31+
32+
## Running tests
33+
34+
Before running tests, you'll need to install [Playwright](https://playwright.dev/) Chromium browser: `pnpm playwright install chromium`.
3635

37-
> The default options from the `cli` are just to check your changes are not breaking major ui/app frameworks build: `generateSW` strategy, `Prompt for update` behavior and `Enable periodic SW updates` to `no`.
36+
Run `pnpm run test` in `@vite-pwa/sveltekit`'s root folder or inside `examples/sveltekit-ts` folder after build `@vite-pwa/sveltekit`.
3837

39-
> If your changes are specific to some behavior, just use the corresponding option on the `cli`.
4038

examples/sveltekit-ts/adapter.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import AdapterNode from '@sveltejs/adapter-node';
2+
import AdpaterStatic from '@sveltejs/adapter-static';
3+
4+
export const nodeAdapter = process.env.NODE_ADAPTER === 'true'
5+
6+
export const adapter = nodeAdapter ? AdapterNode() : AdpaterStatic()
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {test, expect} from '@playwright/test';
2+
import {generateSW} from "../pwa.mjs";
3+
4+
test('The service worker is registered and cache storage is present', async ({ page}) => {
5+
await page.goto('/');
6+
7+
const swURL = await page.evaluate(async () => {
8+
const registration = await Promise.race([
9+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
10+
// @ts-ignore
11+
navigator.serviceWorker.ready,
12+
new Promise((_, reject) => setTimeout(() => reject(new Error('Service worker registration failed: time out')), 10000))
13+
])
14+
return registration.active?.scriptURL
15+
});
16+
const swName = generateSW ? 'sw.js' : 'prompt-sw.js'
17+
expect(swURL).toBe(`http://localhost:4173/${swName}`);
18+
19+
const cacheContents = await page.evaluate(async () => {
20+
const cacheState: Record<string, Array<string>> = {};
21+
for (const cacheName of await caches.keys()) {
22+
const cache = await caches.open(cacheName);
23+
cacheState[cacheName] = (await cache.keys()).map((req) => req.url);
24+
}
25+
return cacheState;
26+
});
27+
28+
expect(Object.keys(cacheContents).length).toEqual(1)
29+
30+
const key = 'workbox-precache-v2-http://localhost:4173/'
31+
32+
expect(Object.keys(cacheContents)[0]).toEqual(key)
33+
34+
const urls = cacheContents[key].map(url => url.slice('http://localhost:4173/'.length))
35+
36+
/*
37+
'http://localhost:4173/about?__WB_REVISION__=38251751d310c9b683a1426c22c135a2',
38+
'http://localhost:4173/?__WB_REVISION__=073370aa3804305a787b01180cd6b8aa',
39+
'http://localhost:4173/manifest.webmanifest?__WB_REVISION__=27df2fa4f35d014b42361148a2207da3'
40+
*/
41+
expect(urls.some(url => url.startsWith('manifest.webmanifest?__WB_REVISION__='))).toEqual(true)
42+
expect(urls.some(url => url.startsWith('?__WB_REVISION__='))).toEqual(true)
43+
expect(urls.some(url => url.startsWith('about?__WB_REVISION__='))).toEqual(true)
44+
});

examples/sveltekit-ts/package.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,27 @@
44
"scripts": {
55
"sw-dev": "vite dev",
66
"dev": "vite dev",
7-
"build": "vite build",
7+
"build-generate-sw": "GENERATE_SW=true vite build",
8+
"build-generate-sw-node": "NODE_ADAPTER=true GENERATE_SW=true vite build",
9+
"build-inject-manifest": "vite build",
10+
"build-inject-manifest-node": "NODE_ADAPTER=true vite build",
811
"build-self-destroying": "SELF_DESTROYING_SW=true vite build",
9-
"preview": "vite preview",
12+
"preview": "vite preview --port=4173",
13+
"preview-node": "PORT=4173 node build",
1014
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
1115
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
1216
"lint": "eslint .",
13-
"lint-fix": "nr lint --fix"
17+
"lint-fix": "nr lint --fix",
18+
"test-generate-sw": "nr build-generate-sw && GENERATE_SW=true vitest run && GENERATE_SW=true playwright test",
19+
"test-generate-sw-node": "nr build-generate-sw-node && NODE_ADAPTER=true GENERATE_SW=true vitest run && NODE_ADAPTER=true GENERATE_SW=true playwright test",
20+
"test-inject-manifest": "nr build-inject-manifest && vitest run && playwright test",
21+
"test-inject-manifest-node": "nr build-inject-manifest-node && NODE_ADAPTER=true vitest run && NODE_ADAPTER=true playwright test",
22+
"test": "nr test-generate-sw && nr test-generate-sw-node && nr test-inject-manifest && nr test-inject-manifest-node"
1423
},
1524
"devDependencies": {
25+
"@playwright/test": "^1.33.0",
1626
"@sveltejs/adapter-static": "^2.0.2",
27+
"@sveltejs/adapter-node": "^1.2.4",
1728
"@sveltejs/kit": "^1.18.0",
1829
"@types/cookie": "^0.5.1",
1930
"@typescript-eslint/eslint-plugin": "^5.59.6",
@@ -25,7 +36,8 @@
2536
"svelte-check": "^3.3.2",
2637
"svelte-preprocess": "^5.0.3",
2738
"tslib": "^2.5.2",
28-
"typescript": "^5.0.4"
39+
"typescript": "^5.0.4",
40+
"vitest": "^0.31.1"
2941
},
3042
"type": "module",
3143
"dependencies": {
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { defineConfig, devices } from '@playwright/test'
2+
3+
const url = 'http://localhost:4173'
4+
5+
import { nodeAdapter } from './adapter.mjs'
6+
7+
/**
8+
* Read environment variables from file.
9+
* https://github.com/motdotla/dotenv
10+
*/
11+
// require('dotenv').config();
12+
13+
/**
14+
* See https://playwright.dev/docs/test-configuration.
15+
*/
16+
export default defineConfig({
17+
testDir: './client-test',
18+
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
19+
outputDir: 'test-results/',
20+
timeout: 5 * 1000,
21+
expect: {
22+
/**
23+
* Maximum time expect() should wait for the condition to be met.
24+
* For example in `await expect(locator).toHaveText();`
25+
*/
26+
timeout: 1000,
27+
},
28+
/* Run tests in files in parallel */
29+
fullyParallel: true,
30+
/* Fail the build on CI if you accidentally left test.only in the source code. */
31+
forbidOnly: !!process.env.CI,
32+
/* Retry on CI only */
33+
retries: 0,
34+
/* Opt out of parallel tests on CI. */
35+
workers: process.env.CI ? 1 : undefined,
36+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
37+
reporter: 'line',
38+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
39+
use: {
40+
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
41+
actionTimeout: 0,
42+
/* Base URL to use in actions like `await page.goto('/')`. */
43+
baseURL: url,
44+
45+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
46+
trace: 'on-first-retry',
47+
},
48+
49+
/* Configure projects for major browsers */
50+
projects: [
51+
{
52+
name: 'chromium',
53+
use: { ...devices['Desktop Chrome'] },
54+
},
55+
56+
// {
57+
// name: 'firefox',
58+
// use: { ...devices['Desktop Firefox'] },
59+
// },
60+
61+
// {
62+
// name: 'webkit',
63+
// use: { ...devices['Desktop Safari'] },
64+
// },
65+
66+
/* Test against mobile viewports. */
67+
// {
68+
// name: 'Mobile Chrome',
69+
// use: { ...devices['Pixel 5'] },
70+
// },
71+
// {
72+
// name: 'Mobile Safari',
73+
// use: { ...devices['iPhone 12'] },
74+
// },
75+
76+
/* Test against branded browsers. */
77+
// {
78+
// name: 'Microsoft Edge',
79+
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
80+
// },
81+
// {
82+
// name: 'Google Chrome',
83+
// use: { ..devices['Desktop Chrome'], channel: 'chrome' },
84+
// },
85+
],
86+
87+
/* Run your local dev server before starting the tests */
88+
webServer: {
89+
command: nodeAdapter ? 'pnpm run preview-node' : 'pnpm run preview',
90+
url,
91+
reuseExistingServer: !process.env.CI,
92+
},
93+
});

examples/sveltekit-ts/pwa.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const generateSW = true;
1+
export const generateSW = process.env.GENERATE_SW === 'true'

examples/sveltekit-ts/svelte.config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import adapter from '@sveltejs/adapter-static';
21
import preprocess from 'svelte-preprocess';
32
// you don't need to do this if you're using generateSW strategy in your app
4-
import { generateSW } from './pwa.mjs';
3+
import { generateSW } from './pwa.mjs'
4+
import { adapter } from './adapter.mjs'
55

66
/** @type {import('@sveltejs/kit').Config} */
77
const config = {
@@ -10,7 +10,7 @@ const config = {
1010
preprocess: preprocess(),
1111

1212
kit: {
13-
adapter: adapter(),
13+
adapter,
1414
serviceWorker: {
1515
register: false,
1616
},
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { existsSync, readFileSync } from 'node:fs'
3+
import { generateSW } from '../pwa.mjs'
4+
import { nodeAdapter } from '../adapter.mjs'
5+
6+
describe(`test-build: ${nodeAdapter ? 'node' : 'static'} adapter`, () => {
7+
it(`service worker is generated: ${generateSW ? 'sw.js' : 'prompt-sw.js'}`, () => {
8+
const swName = `./build/${nodeAdapter ? 'client/': ''}${generateSW ? 'sw.js' : 'prompt-sw.js'}`
9+
expect(existsSync(swName), `${swName} doesn't exist`).toBeTruthy()
10+
const webManifest = `./build/${nodeAdapter ? 'client/': ''}manifest.webmanifest`
11+
expect(existsSync(webManifest), `${webManifest} doesn't exist`).toBeTruthy()
12+
const swContent = readFileSync(swName, 'utf-8')
13+
let match: RegExpMatchArray | null
14+
if (generateSW) {
15+
match = swContent.match(/define\(\['\.\/(workbox-\w+)'/)
16+
expect(match && match.length === 2, `workbox-***.js entry not found in ${swName}`).toBeTruthy()
17+
const workboxName = `./build/${nodeAdapter ? 'client/': ''}${match?.[1]}.js`
18+
expect(existsSync(workboxName),`${workboxName} doesn't exist`).toBeTruthy()
19+
}
20+
match = swContent.match(/"url":\s*"manifest\.webmanifest"/)
21+
expect(match && match.length === 1, 'missing manifest.webmanifest in sw precache manifest').toBeTruthy()
22+
match = swContent.match(/"url":\s*"\/"/)
23+
expect(match && match.length === 1, 'missing entry point route (/) in sw precache manifest').toBeTruthy()
24+
match = swContent.match(/"url":\s*"about"/)
25+
expect(match && match.length === 1,'missing about route (/about) in sw precache manifest').toBeTruthy()
26+
if (nodeAdapter) {
27+
match = swContent.match(/"url":\s*"server\//)
28+
expect(match === null, 'found server/ entries in sw precache manifest').toBeTruthy()
29+
}
30+
})
31+
})

0 commit comments

Comments
 (0)