chore: add github test actions
Some checks are pending
Tests / Unit (happy-dom) (push) Waiting to run
Tests / E2E (Chromium + Firefox + WebKit) (push) Waiting to run
Tests / Size limit (push) Waiting to run

This commit is contained in:
ayo 2026-07-05 16:10:35 +02:00
parent ef20daa140
commit 6af7598973
9 changed files with 271 additions and 88 deletions

67
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,67 @@
name: Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
# Cancel superseded runs on the same ref (e.g. new pushes to a PR).
concurrency:
group: tests-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
unit:
name: Unit (happy-dom)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
with:
version: 11
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm test
e2e:
name: E2E (Chromium + Firefox + WebKit)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
with:
version: 11
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
# Downloads the browser binaries and their OS libraries (Firefox/WebKit
# need system deps that `--with-deps` installs via apt on the runner).
- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium firefox webkit
- run: pnpm test:e2e:all
size:
name: Size limit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
with:
version: 11
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
# `pnpm size-limit` builds `dist/` (tsc + esbuild) and checks each entry
# against the byte budgets declared in package.json `size-limit`.
- run: pnpm size-limit

View file

@ -1,4 +1,5 @@
npm run lint --cache # Lean: lint + format staged files, then the fast unit suite.
npm run test:all # Heavier checks (browser e2e, size-limit) run on pre-push; the full
npm run build # cross-browser matrix runs in CI.
npx size-limit pnpm exec lint-staged
pnpm test

4
.husky/pre-push Executable file
View file

@ -0,0 +1,4 @@
# Heavier gate before code leaves the machine: browser e2e (Chromium only —
# Firefox/WebKit + their system deps are exercised in CI) and the size budget.
pnpm test:e2e
pnpm size-limit

View file

@ -17,7 +17,7 @@ This file provides guidance to AI coding agents when working with code in this r
- `pnpm size-limit` — enforces the byte budgets declared in `package.json` `size-limit` (e.g. `WebComponent.js` ≤ 1.2 KB). Keep additions tiny; this gate is a core project value. - `pnpm size-limit` — enforces the byte budgets declared in `package.json` `size-limit` (e.g. `WebComponent.js` ≤ 1.2 KB). Keep additions tiny; this gate is a core project value.
- `pnpm docs` — run the Astro docs site (`docs/` workspace) locally. - `pnpm docs` — run the Astro docs site (`docs/` workspace) locally.
- `pnpm demo` — run the Vite examples showcase (`demo/` workspace) locally; `pnpm demo:build` builds it. - `pnpm demo` — run the Vite examples showcase (`demo/` workspace) locally; `pnpm demo:build` builds it.
- `pnpm test:e2e` — run the browser e2e specs (`test/e2e/`) in Chromium via Vitest browser mode; `pnpm test:all` runs unit + e2e. - `pnpm test:e2e` — run the browser e2e specs (`test/e2e/`) via Vitest browser mode (Playwright). Defaults to Chromium; `pnpm test:e2e:firefox` / `:webkit` / `:all` target the other engines (the `E2E_BROWSERS` env var, comma-separated, selects instances). `pnpm test:all` runs unit + default e2e.
pnpm is mandatory (a `preinstall` `only-allow pnpm` guard enforces it). This is a pnpm workspace with two sub-packages: `docs/` (Astro docs site) and `demo/` (Vite examples showcase, which consumes the root `web-component-base` package as a `workspace:*` dependency). The runnable example sources live in `demo/examples/` and import the package by name (`web-component-base`), not via relative `src/` paths. pnpm is mandatory (a `preinstall` `only-allow pnpm` guard enforces it). This is a pnpm workspace with two sub-packages: `docs/` (Astro docs site) and `demo/` (Vite examples showcase, which consumes the root `web-component-base` package as a `workspace:*` dependency). The runnable example sources live in `demo/examples/` and import the package by name (`web-component-base`), not via relative `src/` paths.

View file

@ -12,6 +12,12 @@ export default [
'no-unused-vars': 'warn', 'no-unused-vars': 'warn',
}, },
}, },
// Config, build, and script files run in Node — give them Node globals
// (e.g. `process`) on top of the browser defaults.
{
files: ['**/*.config.{js,mjs,cjs}', 'scripts/**/*.{js,mjs}'],
languageOptions: { globals: globals.node },
},
{ {
ignores: ['site/*', '**/dist/*'], ignores: ['site/*', '**/dist/*'],
}, },

View file

@ -30,8 +30,12 @@
"dev": "pnpm -F demo dev", "dev": "pnpm -F demo dev",
"test": "vitest --run", "test": "vitest --run",
"test:watch": "vitest", "test:watch": "vitest",
"test:e2e": "vitest --run --config vitest.e2e.config.mjs", "test:e2e": "E2E_BROWSERS=chromium vitest --run --config vitest.e2e.config.mjs",
"test:all": "pnpm test && pnpm test:e2e", "test:e2e:chromium": "E2E_BROWSERS=chromium vitest --run --config vitest.e2e.config.mjs",
"test:e2e:firefox": "E2E_BROWSERS=firefox vitest --run --config vitest.e2e.config.mjs",
"test:e2e:webkit": "E2E_BROWSERS=webkit vitest --run --config vitest.e2e.config.mjs",
"test:e2e:all": "vitest --run --config vitest.e2e.config.mjs",
"test:all": "pnpm test && pnpm test:e2e:all",
"demo": "pnpm -F demo dev", "demo": "pnpm -F demo dev",
"demo:build": "pnpm -F demo build", "demo:build": "pnpm -F demo build",
"docs": "pnpm -F docs start", "docs": "pnpm -F docs start",
@ -44,6 +48,13 @@
"lint": "eslint . --config eslint.config.mjs", "lint": "eslint . --config eslint.config.mjs",
"prepare": "husky" "prepare": "husky"
}, },
"lint-staged": {
"*.{js,mjs,cjs}": [
"eslint --config eslint.config.mjs --fix",
"prettier --write"
],
"*.{json,css,html,md,yml,yaml}": "prettier --write"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/ayo-run/web-component-base.git" "url": "git+https://github.com/ayo-run/web-component-base.git"
@ -73,6 +84,7 @@
"globals": "^17.1.0", "globals": "^17.1.0",
"happy-dom": "^20.3.7", "happy-dom": "^20.3.7",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^17.0.8",
"netlify-cli": "^23.13.5", "netlify-cli": "^23.13.5",
"playwright": "^1.61.1", "playwright": "^1.61.1",
"prettier": "^3.8.1", "prettier": "^3.8.1",

View file

@ -16,13 +16,13 @@ importers:
version: 12.0.0(size-limit@12.0.0(jiti@2.6.1)) version: 12.0.0(size-limit@12.0.0(jiti@2.6.1))
'@vitest/browser': '@vitest/browser':
specifier: 4.0.18 specifier: 4.0.18
version: 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18) version: 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18)
'@vitest/browser-playwright': '@vitest/browser-playwright':
specifier: 4.0.18 specifier: 4.0.18
version: 4.0.18(playwright@1.61.1)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18) version: 4.0.18(playwright@1.61.1)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18)
'@vitest/coverage-v8': '@vitest/coverage-v8':
specifier: 4.0.18 specifier: 4.0.18
version: 4.0.18(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18))(vitest@4.0.18) version: 4.0.18(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18))(vitest@4.0.18)
bumpp: bumpp:
specifier: ^11.1.0 specifier: ^11.1.0
version: 11.1.0 version: 11.1.0
@ -44,6 +44,9 @@ importers:
husky: husky:
specifier: ^9.1.7 specifier: ^9.1.7
version: 9.1.7 version: 9.1.7
lint-staged:
specifier: ^17.0.8
version: 17.0.8
netlify-cli: netlify-cli:
specifier: ^23.13.5 specifier: ^23.13.5
version: 23.13.5(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(picomatch@4.0.3)(rollup@4.60.3)(supports-color@10.2.2) version: 23.13.5(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(picomatch@4.0.3)(rollup@4.60.3)(supports-color@10.2.2)
@ -70,7 +73,7 @@ importers:
version: 5.9.3 version: 5.9.3
vitest: vitest:
specifier: ^4.0.18 specifier: ^4.0.18
version: 4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2) version: 4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)
demo: demo:
dependencies: dependencies:
@ -89,10 +92,10 @@ importers:
dependencies: dependencies:
'@astrojs/starlight': '@astrojs/starlight':
specifier: ^0.39.1 specifier: ^0.39.1
version: 0.39.1(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2))(typescript@5.9.3) version: 0.39.1(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0))(typescript@5.9.3)
astro: astro:
specifier: ^6.3.1 specifier: ^6.3.1
version: 6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2) version: 6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0)
sharp: sharp:
specifier: ^0.34.5 specifier: ^0.34.5
version: 0.34.5 version: 0.34.5
@ -2680,6 +2683,10 @@ packages:
resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==}
engines: {node: '>=18.20'} engines: {node: '>=18.20'}
cli-truncate@5.2.0:
resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==}
engines: {node: '>=20'}
cli-width@3.0.0: cli-width@3.0.0:
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@ -3690,6 +3697,10 @@ packages:
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
get-east-asian-width@1.6.0:
resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==}
engines: {node: '>=18'}
get-intrinsic@1.3.0: get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -4432,10 +4443,19 @@ packages:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'} engines: {node: '>=14'}
lint-staged@17.0.8:
resolution: {integrity: sha512-B2P/d+jVW0UXOQ0MVMLrB/9ydA1P+zz6jYfdrbbEd9ur3S2rcbduFWKiUCC02Sm5hbC8nrm7y24WuYMG54HfxA==}
engines: {node: '>=22.22.1'}
hasBin: true
listhen@1.9.0: listhen@1.9.0:
resolution: {integrity: sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==} resolution: {integrity: sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==}
hasBin: true hasBin: true
listr2@10.2.2:
resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==}
engines: {node: '>=22.13.0'}
locate-path@6.0.0: locate-path@6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -6027,6 +6047,10 @@ packages:
resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
engines: {node: '>=18'} engines: {node: '>=18'}
slice-ansi@8.0.0:
resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==}
engines: {node: '>=20'}
smart-buffer@4.2.0: smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
@ -6146,6 +6170,10 @@ packages:
streamx@2.22.0: streamx@2.22.0:
resolution: {integrity: sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==} resolution: {integrity: sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==}
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
string-width@2.1.1: string-width@2.1.1:
resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -6166,6 +6194,10 @@ packages:
resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==}
engines: {node: '>=20'} engines: {node: '>=20'}
string-width@8.2.1:
resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==}
engines: {node: '>=20'}
string_decoder@1.1.1: string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
@ -6318,6 +6350,10 @@ packages:
resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==}
engines: {node: '>=18'} engines: {node: '>=18'}
tinyexec@1.2.4:
resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
engines: {node: '>=18'}
tinyglobby@0.2.15: tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
@ -6937,6 +6973,10 @@ packages:
resolution: {integrity: sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==} resolution: {integrity: sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
wrap-ansi@10.0.0:
resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==}
engines: {node: '>=20'}
wrap-ansi@6.2.0: wrap-ansi@6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -7015,11 +7055,6 @@ packages:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'} engines: {node: '>=18'}
yaml@2.8.2:
resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
engines: {node: '>= 14.6'}
hasBin: true
yaml@2.9.0: yaml@2.9.0:
resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
engines: {node: '>= 14.6'} engines: {node: '>= 14.6'}
@ -7113,12 +7148,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@astrojs/mdx@5.0.4(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2))': '@astrojs/mdx@5.0.4(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0))':
dependencies: dependencies:
'@astrojs/markdown-remark': 7.1.1 '@astrojs/markdown-remark': 7.1.1
'@mdx-js/mdx': 3.1.1 '@mdx-js/mdx': 3.1.1
acorn: 8.16.0 acorn: 8.16.0
astro: 6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2) astro: 6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0)
es-module-lexer: 2.1.0 es-module-lexer: 2.1.0
estree-util-visit: 2.0.0 estree-util-visit: 2.0.0
hast-util-to-html: 9.0.5 hast-util-to-html: 9.0.5
@ -7142,17 +7177,17 @@ snapshots:
stream-replace-string: 2.0.0 stream-replace-string: 2.0.0
zod: 4.4.3 zod: 4.4.3
'@astrojs/starlight@0.39.1(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2))(typescript@5.9.3)': '@astrojs/starlight@0.39.1(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0))(typescript@5.9.3)':
dependencies: dependencies:
'@astrojs/markdown-remark': 7.1.1 '@astrojs/markdown-remark': 7.1.1
'@astrojs/mdx': 5.0.4(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2)) '@astrojs/mdx': 5.0.4(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0))
'@astrojs/sitemap': 3.7.2 '@astrojs/sitemap': 3.7.2
'@pagefind/default-ui': 1.5.2 '@pagefind/default-ui': 1.5.2
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/js-yaml': 4.0.9 '@types/js-yaml': 4.0.9
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
astro: 6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2) astro: 6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0)
astro-expressive-code: 0.42.0(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2)) astro-expressive-code: 0.42.0(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0))
bcp-47: 2.1.0 bcp-47: 2.1.0
hast-util-from-html: 2.0.3 hast-util-from-html: 2.0.3
hast-util-select: 6.0.4 hast-util-select: 6.0.4
@ -7934,7 +7969,7 @@ snapshots:
minimatch: 9.0.5 minimatch: 9.0.5
read-pkg: 9.0.1 read-pkg: 9.0.1
semver: 7.7.2 semver: 7.7.2
yaml: 2.8.2 yaml: 2.9.0
yargs: 17.7.2 yargs: 17.7.2
'@netlify/build@35.5.10(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(picomatch@4.0.3)(rollup@4.60.3)': '@netlify/build@35.5.10(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(picomatch@4.0.3)(rollup@4.60.3)':
@ -7988,7 +8023,7 @@ snapshots:
ts-node: 10.9.2(@types/node@25.0.10)(typescript@5.9.3) ts-node: 10.9.2(@types/node@25.0.10)(typescript@5.9.3)
typescript: 5.9.3 typescript: 5.9.3
uuid: 11.1.0 uuid: 11.1.0
yaml: 2.8.2 yaml: 2.9.0
yargs: 17.7.2 yargs: 17.7.2
zod: 3.25.76 zod: 3.25.76
transitivePeerDependencies: transitivePeerDependencies:
@ -8033,7 +8068,7 @@ snapshots:
read-package-up: 11.0.0 read-package-up: 11.0.0
tomlify-j0.4: 3.0.0 tomlify-j0.4: 3.0.0
validate-npm-package-name: 5.0.1 validate-npm-package-name: 5.0.1
yaml: 2.8.2 yaml: 2.9.0
yargs: 17.7.2 yargs: 17.7.2
zod: 4.3.6 zod: 4.3.6
@ -8602,7 +8637,7 @@ snapshots:
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.8
estree-walker: 2.0.2 estree-walker: 2.0.2
picomatch: 4.0.3 picomatch: 4.0.4
optionalDependencies: optionalDependencies:
rollup: 4.60.3 rollup: 4.60.3
@ -9034,29 +9069,29 @@ snapshots:
- rollup - rollup
- supports-color - supports-color
'@vitest/browser-playwright@4.0.18(playwright@1.61.1)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18)': '@vitest/browser-playwright@4.0.18(playwright@1.61.1)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18)':
dependencies: dependencies:
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18) '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18)
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2)) '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))
playwright: 1.61.1 playwright: 1.61.1
tinyrainbow: 3.0.3 tinyrainbow: 3.0.3
vitest: 4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2) vitest: 4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- msw - msw
- utf-8-validate - utf-8-validate
- vite - vite
'@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18)': '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18)':
dependencies: dependencies:
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2)) '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))
'@vitest/utils': 4.0.18 '@vitest/utils': 4.0.18
magic-string: 0.30.21 magic-string: 0.30.21
pixelmatch: 7.1.0 pixelmatch: 7.1.0
pngjs: 7.0.0 pngjs: 7.0.0
sirv: 3.0.2 sirv: 3.0.2
tinyrainbow: 3.0.3 tinyrainbow: 3.0.3
vitest: 4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2) vitest: 4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)
ws: 8.19.0 ws: 8.19.0
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
@ -9064,7 +9099,7 @@ snapshots:
- utf-8-validate - utf-8-validate
- vite - vite
'@vitest/coverage-v8@4.0.18(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18))(vitest@4.0.18)': '@vitest/coverage-v8@4.0.18(@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18))(vitest@4.0.18)':
dependencies: dependencies:
'@bcoe/v8-coverage': 1.0.2 '@bcoe/v8-coverage': 1.0.2
'@vitest/utils': 4.0.18 '@vitest/utils': 4.0.18
@ -9076,9 +9111,9 @@ snapshots:
obug: 2.1.1 obug: 2.1.1
std-env: 3.10.0 std-env: 3.10.0
tinyrainbow: 3.0.3 tinyrainbow: 3.0.3
vitest: 4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2) vitest: 4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)
optionalDependencies: optionalDependencies:
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18) '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18)
'@vitest/expect@4.0.18': '@vitest/expect@4.0.18':
dependencies: dependencies:
@ -9089,13 +9124,13 @@ snapshots:
chai: 6.2.2 chai: 6.2.2
tinyrainbow: 3.0.3 tinyrainbow: 3.0.3
'@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))': '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))':
dependencies: dependencies:
'@vitest/spy': 4.0.18 '@vitest/spy': 4.0.18
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.21 magic-string: 0.30.21
optionalDependencies: optionalDependencies:
vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2) vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)
'@vitest/pretty-format@4.0.18': '@vitest/pretty-format@4.0.18':
dependencies: dependencies:
@ -9421,12 +9456,12 @@ snapshots:
astring@1.9.0: {} astring@1.9.0: {}
astro-expressive-code@0.42.0(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2)): astro-expressive-code@0.42.0(astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0)):
dependencies: dependencies:
astro: 6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2) astro: 6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0)
rehype-expressive-code: 0.42.0 rehype-expressive-code: 0.42.0
astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.8.2): astro@6.3.1(@netlify/blobs@10.5.0)(@types/node@25.0.10)(db0@0.3.1)(ioredis@5.6.0)(jiti@2.6.1)(rollup@4.60.3)(terser@5.39.0)(yaml@2.9.0):
dependencies: dependencies:
'@astrojs/compiler': 4.0.0 '@astrojs/compiler': 4.0.0
'@astrojs/internal-helpers': 0.9.0 '@astrojs/internal-helpers': 0.9.0
@ -9478,8 +9513,8 @@ snapshots:
unist-util-visit: 5.1.0 unist-util-visit: 5.1.0
unstorage: 1.17.5(@netlify/blobs@10.5.0)(db0@0.3.1)(ioredis@5.6.0) unstorage: 1.17.5(@netlify/blobs@10.5.0)(db0@0.3.1)(ioredis@5.6.0)
vfile: 6.0.3 vfile: 6.0.3
vite: 7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2) vite: 7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)
vitefu: 1.1.3(vite@7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2)) vitefu: 1.1.3(vite@7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))
xxhash-wasm: 1.1.0 xxhash-wasm: 1.1.0
yargs-parser: 22.0.0 yargs-parser: 22.0.0
zod: 4.4.3 zod: 4.4.3
@ -9839,6 +9874,11 @@ snapshots:
cli-spinners@3.4.0: {} cli-spinners@3.4.0: {}
cli-truncate@5.2.0:
dependencies:
slice-ansi: 8.0.0
string-width: 8.2.1
cli-width@3.0.0: {} cli-width@3.0.0: {}
cli-width@4.1.0: {} cli-width@4.1.0: {}
@ -10923,6 +10963,8 @@ snapshots:
get-east-asian-width@1.4.0: {} get-east-asian-width@1.4.0: {}
get-east-asian-width@1.6.0: {}
get-intrinsic@1.3.0: get-intrinsic@1.3.0:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.2 call-bind-apply-helpers: 1.0.2
@ -11836,6 +11878,15 @@ snapshots:
lilconfig@3.1.3: {} lilconfig@3.1.3: {}
lint-staged@17.0.8:
dependencies:
listr2: 10.2.2
picomatch: 4.0.4
string-argv: 0.3.2
tinyexec: 1.2.4
optionalDependencies:
yaml: 2.9.0
listhen@1.9.0: listhen@1.9.0:
dependencies: dependencies:
'@parcel/watcher': 2.5.6 '@parcel/watcher': 2.5.6
@ -11857,6 +11908,14 @@ snapshots:
untun: 0.1.3 untun: 0.1.3
uqr: 0.1.2 uqr: 0.1.2
listr2@10.2.2:
dependencies:
cli-truncate: 5.2.0
eventemitter3: 5.0.4
log-update: 6.1.0
rfdc: 1.4.1
wrap-ansi: 10.0.0
locate-path@6.0.0: locate-path@6.0.0:
dependencies: dependencies:
p-locate: 5.0.0 p-locate: 5.0.0
@ -14073,6 +14132,11 @@ snapshots:
ansi-styles: 6.2.3 ansi-styles: 6.2.3
is-fullwidth-code-point: 5.1.0 is-fullwidth-code-point: 5.1.0
slice-ansi@8.0.0:
dependencies:
ansi-styles: 6.2.3
is-fullwidth-code-point: 5.1.0
smart-buffer@4.2.0: {} smart-buffer@4.2.0: {}
smol-toml@1.6.1: {} smol-toml@1.6.1: {}
@ -14181,6 +14245,8 @@ snapshots:
optionalDependencies: optionalDependencies:
bare-events: 2.5.4 bare-events: 2.5.4
string-argv@0.3.2: {}
string-width@2.1.1: string-width@2.1.1:
dependencies: dependencies:
is-fullwidth-code-point: 2.0.0 is-fullwidth-code-point: 2.0.0
@ -14209,6 +14275,11 @@ snapshots:
get-east-asian-width: 1.4.0 get-east-asian-width: 1.4.0
strip-ansi: 7.1.2 strip-ansi: 7.1.2
string-width@8.2.1:
dependencies:
get-east-asian-width: 1.6.0
strip-ansi: 7.1.2
string_decoder@1.1.1: string_decoder@1.1.1:
dependencies: dependencies:
safe-buffer: 5.1.2 safe-buffer: 5.1.2
@ -14367,6 +14438,8 @@ snapshots:
tinyexec@1.1.2: {} tinyexec@1.1.2: {}
tinyexec@1.2.4: {}
tinyglobby@0.2.15: tinyglobby@0.2.15:
dependencies: dependencies:
fdir: 6.5.0(picomatch@4.0.3) fdir: 6.5.0(picomatch@4.0.3)
@ -14695,11 +14768,11 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
vfile-message: 4.0.3 vfile-message: 4.0.3
vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2): vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0):
dependencies: dependencies:
esbuild: 0.27.2 esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3) fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.3 picomatch: 4.0.4
postcss: 8.5.6 postcss: 8.5.6
rollup: 4.56.0 rollup: 4.56.0
tinyglobby: 0.2.15 tinyglobby: 0.2.15
@ -14708,22 +14781,7 @@ snapshots:
fsevents: 2.3.3 fsevents: 2.3.3
jiti: 2.6.1 jiti: 2.6.1
terser: 5.39.0 terser: 5.39.0
yaml: 2.8.2 yaml: 2.9.0
vite@7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2):
dependencies:
esbuild: 0.27.7
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.14
rollup: 4.60.3
tinyglobby: 0.2.16
optionalDependencies:
'@types/node': 25.0.10
fsevents: 2.3.3
jiti: 2.6.1
terser: 5.39.0
yaml: 2.8.2
vite@7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0): vite@7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0):
dependencies: dependencies:
@ -14740,14 +14798,14 @@ snapshots:
terser: 5.39.0 terser: 5.39.0
yaml: 2.9.0 yaml: 2.9.0
vitefu@1.1.3(vite@7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2)): vitefu@1.1.3(vite@7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)):
optionalDependencies: optionalDependencies:
vite: 7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2) vite: 7.3.3(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)
vitest@4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2): vitest@4.0.18(@opentelemetry/api@1.8.0)(@types/node@25.0.10)(@vitest/browser-playwright@4.0.18)(happy-dom@20.3.7)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0):
dependencies: dependencies:
'@vitest/expect': 4.0.18 '@vitest/expect': 4.0.18
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2)) '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))
'@vitest/pretty-format': 4.0.18 '@vitest/pretty-format': 4.0.18
'@vitest/runner': 4.0.18 '@vitest/runner': 4.0.18
'@vitest/snapshot': 4.0.18 '@vitest/snapshot': 4.0.18
@ -14764,12 +14822,12 @@ snapshots:
tinyexec: 1.0.2 tinyexec: 1.0.2
tinyglobby: 0.2.15 tinyglobby: 0.2.15
tinyrainbow: 3.0.3 tinyrainbow: 3.0.3
vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2) vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@opentelemetry/api': 1.8.0 '@opentelemetry/api': 1.8.0
'@types/node': 25.0.10 '@types/node': 25.0.10
'@vitest/browser-playwright': 4.0.18(playwright@1.61.1)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.8.2))(vitest@4.0.18) '@vitest/browser-playwright': 4.0.18(playwright@1.61.1)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(terser@5.39.0)(yaml@2.9.0))(vitest@4.0.18)
happy-dom: 20.3.7 happy-dom: 20.3.7
transitivePeerDependencies: transitivePeerDependencies:
- jiti - jiti
@ -14862,6 +14920,12 @@ snapshots:
wordwrap@0.0.3: {} wordwrap@0.0.3: {}
wrap-ansi@10.0.0:
dependencies:
ansi-styles: 6.2.3
string-width: 8.2.1
strip-ansi: 7.1.2
wrap-ansi@6.2.0: wrap-ansi@6.2.0:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
@ -14918,8 +14982,6 @@ snapshots:
yallist@5.0.0: {} yallist@5.0.0: {}
yaml@2.8.2: {}
yaml@2.9.0: {} yaml@2.9.0: {}
yargs-parser@21.1.1: {} yargs-parser@21.1.1: {}

View file

@ -1,24 +1,45 @@
# End-to-end example tests # End-to-end example tests
These specs load each `demo/examples/*` component into a **real browser** (Chromium These specs load each `demo/examples/*` component into a **real browser**
via Playwright) and drive it the way a user would. They exist because the v5 (Chromium, Firefox, and WebKit via Playwright) and drive it the way a user
correctness work targets behaviors that happy-dom cannot reproduce — custom would. They exist because the v5 correctness work targets behaviors that
element upgrade ordering, the constructor-attribute rule, empty-string/removed happy-dom cannot reproduce — custom element upgrade ordering, the
attributes, and adopted stylesheets — so the unit suite alone can pass while a constructor-attribute rule, empty-string/removed attributes, and adopted
real browser misbehaves. stylesheets — so the unit suite alone can pass while a real browser misbehaves.
Running across engines also catches cross-browser differences.
## Running ## Running
```sh ```sh
pnpm test:e2e # browser specs only (this folder) pnpm test:e2e # default: Chromium only (fast, universal)
pnpm test # fast unit suite (happy-dom), unchanged pnpm test:e2e:chromium # Chromium
pnpm test:all # both pnpm test:e2e:firefox # Firefox
pnpm test:e2e:webkit # WebKit (the engine behind Safari)
pnpm test:e2e:all # Chromium + Firefox + WebKit
pnpm test # fast unit suite (happy-dom), unchanged
pnpm test:all # unit + default e2e
``` ```
`pnpm test:e2e` stays on Chromium so it works everywhere with minimal setup; use
`test:e2e:all` (or a CI matrix) for the full cross-browser run. Each script just
sets the `E2E_BROWSERS` env var (comma-separated), which the config reads.
Config lives in `vitest.e2e.config.mjs` (Vitest browser mode + the Config lives in `vitest.e2e.config.mjs` (Vitest browser mode + the
`@vitest/browser-playwright` provider). It is kept separate from `@vitest/browser-playwright` provider), kept separate from `vitest.config.mjs`
`vitest.config.mjs` so `pnpm test` stays fast and needs no browser. The so `pnpm test` stays fast and needs no browser.
Chromium binary is installed with `npx playwright install chromium`.
### Browser setup
```sh
npx playwright install chromium firefox webkit # download the browsers
npx playwright install-deps # Linux only: system libraries
```
Firefox and especially WebKit need extra OS libraries on Linux (`libwoff1`,
etc.); `playwright install-deps` requires root. Without them those engines fail
to launch — which is why the default target is Chromium and CI installs the deps
before running `test:e2e:all`.
## Coverage ## Coverage

View file

@ -4,11 +4,21 @@ import { fileURLToPath } from 'node:url'
const src = (p) => fileURLToPath(new URL(`./src/${p}`, import.meta.url)) const src = (p) => fileURLToPath(new URL(`./src/${p}`, import.meta.url))
// End-to-end specs run against the real `src/` in a real browser (Chromium via // Which engines to run the e2e suite against. Defaults to all three Playwright
// engines (Chromium, Firefox, and WebKit — the engine behind Safari). Override
// with e.g. `E2E_BROWSERS=chromium pnpm test:e2e` in environments that only
// have some browsers installed (Firefox/WebKit also need system libs — see
// `npx playwright install-deps`).
const BROWSERS = (process.env.E2E_BROWSERS || 'chromium,firefox,webkit')
.split(',')
.map((name) => name.trim())
.filter(Boolean)
// End-to-end specs run against the real `src/` in a real browser (via
// Playwright), so behaviors that happy-dom cannot reproduce — custom-element // Playwright), so behaviors that happy-dom cannot reproduce — custom-element
// upgrade ordering, constructor attribute rules, adopted stylesheets — are // upgrade ordering, constructor attribute rules, adopted stylesheets — are
// actually exercised. Kept separate from the unit config so `pnpm test` stays // actually exercised across engines. Kept separate from the unit config so
// fast and headless-browser-free. // `pnpm test` stays fast and headless-browser-free.
export default defineConfig({ export default defineConfig({
// The demo examples import the `web-component-base` package; resolve it to // The demo examples import the `web-component-base` package; resolve it to
// local source (mirrors demo/vite.config.js) so e2e tests exercise src. // local source (mirrors demo/vite.config.js) so e2e tests exercise src.
@ -29,7 +39,7 @@ export default defineConfig({
provider: playwright(), provider: playwright(),
headless: true, headless: true,
screenshotFailures: false, screenshotFailures: false,
instances: [{ browser: 'chromium' }], instances: BROWSERS.map((browser) => ({ browser })),
}, },
}, },
}) })