Skip to content

Commit 9e1e4f2

Browse files
committed
test: add e2e tests with playwright
1 parent f36ff09 commit 9e1e4f2

File tree

21 files changed

+549
-9
lines changed

21 files changed

+549
-9
lines changed

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,36 @@ jobs:
8585

8686
- name: 🧪 Run Type Tests
8787
run: bun run test:types --continue --filter='!./examples/*'
88+
89+
playwright:
90+
name: "🧪 Playwright"
91+
runs-on: ubuntu-latest
92+
timeout-minutes: 15
93+
strategy:
94+
fail-fast: false
95+
matrix:
96+
shardIndex: [1, 2, 3, 4]
97+
shardTotal: [4]
98+
steps:
99+
- name: ⬇️ Checkout repo
100+
uses: actions/checkout@v4
101+
with:
102+
fetch-depth: 2
103+
104+
- name: 📤 Setup Node
105+
uses: actions/setup-node@v4
106+
with:
107+
node-version: 20.13.1
108+
109+
- name: 📤 Install Bun
110+
uses: oven-sh/setup-bun@v1
111+
112+
- name: 📤 Install dependencies
113+
run: bun install
114+
115+
- name: 📤 Install Playwright browsers
116+
run: bunx playwright install --with-deps
117+
working-directory: packages/core
118+
119+
- name: 🧪 Run Playwright tests
120+
run: bun test:e2e --continue -- --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ coverage
4242

4343
.nyc_output
4444

45+
# playwright
46+
**/tests/e2e/report
47+
**/tests/e2e/blob-report
48+
**/tests/e2e/results
49+
4550
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
4651

4752
.grunt

bun.lockb

2.19 KB
Binary file not shown.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"format": "biome format . --write",
2828
"---------------------------------------------------------------------------------": "",
2929
"test": "turbo run test",
30+
"test:e2e": "turbo run test:e2e",
3031
"test:types": "turbo run test:types",
3132
"test:watch": "turbo run test:watch",
3233
"----------------------------------------------------------------------------------": "",

packages/core/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
"test": "vitest run",
9898
"test:types": "vitest run --typecheck",
9999
"test:watch": "vitest",
100+
"test:e2e": "playwright test",
100101
"---------------------------------------------------------------------------------": "",
101102
"postinstall": "svelte-kit sync"
102103
},
@@ -127,11 +128,13 @@
127128
"zod": "^3.22.2"
128129
},
129130
"devDependencies": {
130-
"@sveltejs/adapter-static": "3.0.2",
131+
"@playwright/test": "1.45.0",
132+
"@sveltejs/adapter-auto": "3.2.2",
131133
"@sveltejs/vite-plugin-svelte": "3.1.1",
132134
"@testing-library/jest-dom": "6.4.6",
133135
"@testing-library/svelte": "5.1.0",
134136
"@testing-library/user-event": "14.5.2",
137+
"@types/node": "20.14.9",
135138
"jsdom": "24.1.0",
136139
"svelte-check": "3.8.1",
137140
"tslib": "2.6.3",

packages/core/playwright.config.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { resolve } from "node:path";
2+
import { defineConfig, devices } from "@playwright/test";
3+
4+
function playwrightDir(partialPath: string) {
5+
return resolve("./tests/e2e", partialPath);
6+
}
7+
8+
const CI = !!process.env.CI;
9+
10+
export default defineConfig({
11+
testDir: playwrightDir("specs"),
12+
outputDir: playwrightDir("results"),
13+
webServer: {
14+
command: "bun vite build && bun vite preview",
15+
port: 4173,
16+
},
17+
use: {
18+
screenshot: "only-on-failure",
19+
trace: "retain-on-failure",
20+
},
21+
testMatch: /(.+\.)?(test|spec)\.[jt]s/,
22+
reporter: CI
23+
? [["github"]]
24+
: [
25+
[
26+
"html",
27+
{ outputFolder: playwrightDir("./report"), open: "on-failure" },
28+
],
29+
],
30+
projects: [
31+
{
32+
name: "chromium",
33+
use: {
34+
...devices["Desktop Chrome"],
35+
},
36+
},
37+
{
38+
name: "firefox",
39+
use: {
40+
...devices["Desktop Firefox"],
41+
},
42+
},
43+
{
44+
name: "webkit",
45+
use: {
46+
...devices["Desktop Safari"],
47+
},
48+
},
49+
{
50+
name: "Mobile Chrome",
51+
use: { ...devices["Pixel 5"] },
52+
},
53+
{
54+
name: "Mobile Safari",
55+
use: { ...devices["iPhone 14"] },
56+
},
57+
/** Test branded browsers */
58+
{
59+
name: "google",
60+
use: {
61+
...devices["Desktop Google Chrome"],
62+
},
63+
},
64+
{
65+
name: "msedge",
66+
use: {
67+
...devices["Desktop Edge"],
68+
},
69+
},
70+
],
71+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script lang="ts">
2+
import { page } from "$app/stores";
3+
import { useMultiSelectFilters } from "./_hooks/multi-select";
4+
5+
const [q, helpers] = useMultiSelectFilters($page.url);
6+
7+
function updateCategories(category: string) {
8+
const categories = q.categories.includes(category)
9+
? q.categories.filter((c) => c !== category)
10+
: [...q.categories, category];
11+
helpers.update({ categories });
12+
}
13+
</script>
14+
15+
<ul>
16+
<li>
17+
<label>
18+
<input
19+
type="checkbox"
20+
value="books"
21+
onchange={() => updateCategories("books")}
22+
checked={q.categories.includes("books")}
23+
/>
24+
Books
25+
</label>
26+
</li>
27+
<li>
28+
<label>
29+
<input
30+
type="checkbox"
31+
value="electronics"
32+
onchange={() => updateCategories("electronics")}
33+
checked={q.categories.includes("electronics")}
34+
/>
35+
Electronics
36+
</label>
37+
</li>
38+
</ul>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { sveltekit } from "$lib/adapters/sveltekit";
2+
import { createUseQueryParams } from "$lib/create-params.svelte";
3+
import { z } from "zod";
4+
5+
export type MultiSelect = z.infer<typeof MultiSelect>;
6+
const MultiSelect = z.object({
7+
categories: z
8+
.union([z.string().array(), z.string()])
9+
.default([])
10+
.transform((c) => (Array.isArray(c) ? c : [c])),
11+
});
12+
13+
export const useMultiSelectFilters = createUseQueryParams(MultiSelect, {
14+
adapter: sveltekit(),
15+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script>
2+
import { page } from "$app/stores";
3+
import { usePagination } from "./_hooks/pagination";
4+
5+
const [q] = usePagination($page.url);
6+
7+
const nextPage = () => {
8+
q.page = Math.min(q.page + 1, 10);
9+
};
10+
11+
const prevPage = () => {
12+
q.page = Math.max(q.page - 1, 1);
13+
};
14+
</script>
15+
16+
<h1>Current Page: {q.page}</h1>
17+
<h2>Page Size: {q.pageSize}</h2>
18+
19+
<button onclick={prevPage} disabled={q.page === 1}>Previous</button>
20+
<button onclick={nextPage} disabled={q.page === 10}>Next</button>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { sveltekit } from "$lib/adapters/sveltekit";
2+
import { createUseQueryParams } from "$lib/create-params.svelte";
3+
import { z } from "zod";
4+
5+
export type Pagination = z.infer<typeof Pagination>;
6+
const Pagination = z.object({
7+
page: z.coerce.number().default(1),
8+
pageSize: z.coerce.number().default(10),
9+
});
10+
11+
export const usePagination = createUseQueryParams(Pagination, {
12+
adapter: sveltekit(),
13+
});

0 commit comments

Comments
 (0)