diff --git a/.gitignore b/.gitignore index 9ca87db..0310043 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ dist data/kv_data.json assets/*.png !assets/cloud_gradient_logo.png + +test-results +playwright-report diff --git a/Dockerfile b/Dockerfile index 09f951e..e0aaed2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,6 +29,11 @@ WORKDIR /app # Install pnpm RUN npm install -g pnpm +# better-sqlite3 may fall back to a native node-gyp rebuild on Alpine. +# Keep the build toolchain available so the runtime install/rebuild is +# deterministic when a prebuilt binary is unavailable. +RUN apk add --no-cache python3 make g++ + # Copy package files COPY package.json pnpm-lock.yaml ./ diff --git a/package.json b/package.json index 202c1e8..294e9b9 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "dev": "sb dev src/index.tsx", "build": "sb build src/index.tsx", "start": "sb start", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "test:e2e": "playwright test", + "test:e2e:update": "playwright test --update-snapshots" }, "keywords": [], "author": "", @@ -31,6 +33,7 @@ "springboard-server": "0.16.0" }, "devDependencies": { + "@playwright/test": "^1.61.1", "@types/node": "^25.0.0", "@types/qrcode": "^1.5.6", "@types/react": "^19.2.7", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..bdc2b4b --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,26 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests/e2e', + outputDir: './test-results', + fullyParallel: false, + workers: 1, + reporter: [['list']], + use: { + baseURL: 'http://127.0.0.1:3217', + viewport: { width: 1920, height: 1080 }, + trace: 'retain-on-failure', + }, + webServer: { + command: 'npm rebuild better-sqlite3 && pnpm build && PORT=3217 pnpm start', + url: 'http://127.0.0.1:3217/backstage', + reuseExistingServer: !process.env.CI, + timeout: 120_000, + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7cdf18c..6724a07 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,6 +54,9 @@ importers: specifier: 0.16.0 version: 0.16.0(@springboardjs/data-storage@0.16.0(kysely@0.28.8))(@springboardjs/platforms-node@0.16.0(isomorphic-ws@4.0.1(ws@8.18.3))(springboard@0.16.0(immer@10.2.0)(json-rpc-2.0@1.7.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(rxjs@7.8.2))(ws@8.18.3))(hono@4.10.8)(springboard@0.16.0(immer@10.2.0)(json-rpc-2.0@1.7.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(rxjs@7.8.2)) devDependencies: + '@playwright/test': + specifier: ^1.61.1 + version: 1.61.1 '@types/node': specifier: ^25.0.0 version: 25.0.0 @@ -262,6 +265,7 @@ packages: '@hono/node-ws@1.2.0': resolution: {integrity: sha512-OBPQ8OSHBw29mj00wT/xGYtB6HY54j0fNSdVZ7gZM3TUeq0So11GXaWtFf1xWxQNfumKIsj0wRuLKWfVsO5GgQ==} engines: {node: '>=18.14.1'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. peerDependencies: '@hono/node-server': ^1.11.1 hono: ^4.6.0 @@ -274,6 +278,11 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@playwright/test@1.61.1': + resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==} + engines: {node: '>=18'} + hasBin: true + '@remix-run/router@1.23.1': resolution: {integrity: sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==} engines: {node: '>=14.0.0'} @@ -518,6 +527,11 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -546,6 +560,7 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true google-auth-library@10.5.0: @@ -722,6 +737,16 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + playwright-core@1.61.1: + resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.61.1: + resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==} + engines: {node: '>=18'} + hasBin: true + pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} @@ -729,6 +754,7 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true pump@3.0.3: @@ -1125,6 +1151,10 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@playwright/test@1.61.1': + dependencies: + playwright: 1.61.1 + '@remix-run/router@1.23.1': {} '@springboardjs/data-storage@0.16.0(kysely@0.28.8)': @@ -1377,6 +1407,9 @@ snapshots: fs-constants@1.0.0: {} + fsevents@2.3.2: + optional: true + function-bind@1.1.2: {} gaxios@7.1.3: @@ -1592,6 +1625,14 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + playwright-core@1.61.1: {} + + playwright@1.61.1: + dependencies: + playwright-core: 1.61.1 + optionalDependencies: + fsevents: 2.3.2 + pngjs@5.0.0: {} prebuild-install@7.1.3: diff --git a/src/components/ParticipantItem.tsx b/src/components/ParticipantItem.tsx index ea9543b..0ad7c25 100644 --- a/src/components/ParticipantItem.tsx +++ b/src/components/ParticipantItem.tsx @@ -54,6 +54,8 @@ export function ParticipantItem({ return (