feat: Add integration tests - #195
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a full integration-testing scaffold (Playwright + Docker-backed backend) and expands the unit/component test setup (Vitest projects + Testing Library) to improve confidence in both frontend state modules and end-to-end user journeys.
Changes:
- Added Playwright “integration” test suite that boots a local docker-compose backend and runs against a Vite integration mode.
- Split Vitest into unit vs jsdom/component projects and added a large set of new unit/component tests across state, API helpers, and SignalR handlers.
- Introduced small runtime fixes (auth state reset when unauthenticated, safer navigation state tracking, empty-state UI) plus basic security headers via
hooks.server.ts.
Reviewed changes
Copilot reviewed 59 out of 60 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| vitest.setup.ts | Adds jest-dom matchers for Vitest (jsdom/component tests). |
| vite.config.ts | Adds integration-mode env loading, Vite proxying for /1 & /2, Vitest multi-project setup, and integration TLS behavior. |
| src/routes/+error.svelte | Makes afterNavigate handling null-safe with optional chaining. |
| src/routes/(app)/shares/user/outgoing/+page.svelte | Adds outgoing-shares empty state UI. |
| src/routes/(app)/settings/api-tokens/+page.svelte | Fixes token creation state update to use reassignment (reactivity-friendly). |
| src/lib/state/user-state.test.svelte.ts | Adds tests for user state behavior and refreshSelf paths. |
| src/lib/state/user-shares-state.test.svelte.ts | Adds tests for shares state refresh functions and error handling. |
| src/lib/state/shared-hubs-state.test.svelte.ts | Adds tests for shared hubs state initialization and refresh. |
| src/lib/state/live-control-state.test.svelte.ts | Adds extensive tests for live-control state, WebSocket handling, and helpers. |
| src/lib/state/hubs-state.test.svelte.ts | Adds tests for hubs state and refreshOwnHubs behavior. |
| src/lib/state/color-scheme-state.test.svelte.ts | Adds jsdom tests for theme handling and DarkReader detection. |
| src/lib/state/breadcrumbs-state.test.svelte.ts | Adds tests for breadcrumb registration lifecycle via fixture component. |
| src/lib/state/backend-metadata-state.test.svelte.ts | Adds tests for backend metadata initialization and error cases. |
| src/lib/state/fixtures/BreadcrumbRegistrar.svelte | Test fixture component for breadcrumb registration. |
| src/lib/signalr/handlers/OtaRollback.test.ts | Adds tests for OTA rollback SignalR handler effects/toasts. |
| src/lib/signalr/handlers/OtaInstallProgress.test.ts | Adds tests for OTA progress handler updates and validation. |
| src/lib/signalr/handlers/Log.test.ts | Adds tests for SignalR log validation and listener dispatch behavior. |
| src/lib/signalr/handlers/DeviceUpdate.test.ts | Adds tests for device update handler routing and validation. |
| src/lib/signalr/handlers/DeviceStatus.test.ts | Adds tests for device status handler behavior and validation. |
| src/lib/components/ui/data-table/mergeObjects.test.ts | Adds tests for mergeObjects proxy/override behavior. |
| src/lib/components/dialog-manager/dialog-store.test.svelte.ts | Adds tests for dialog store creation/removal and confirm/alert flows. |
| src/lib/api/pwnedPasswords.test.ts | Adds tests for HIBP range lookup parsing and error handling. |
| src/lib/api/firmwareCDN.test.ts | Adds tests for firmware CDN fetching/parsing and hash verification. |
| src/hooks.server.ts | Adds security headers (nosniff, SAMEORIGIN) globally. |
| src/hooks.client.ts | Resets userState when backend says the user is unauthenticated. |
| scripts/dev-integration.mjs | Starts docker-compose integration stack, waits for backend, then launches Vite integration mode. |
| README.md | Adds CI badge entries for the new integration workflow. |
| pnpm-lock.yaml | Adds Testing Library + jsdom dependencies and updates Vitest peer graph. |
| playwright.e2e.config.ts | Adds separate “full E2E” Playwright config targeting a remote environment. |
| playwright.config.ts | Replaces default config with integration-focused Playwright setup + docker-backed webServer. |
| package.json | Adds dev:integration, splits test:integration vs test:e2e, updates test script. |
| eslint.config.js | Disables no-explicit-any for test files. |
| e2e/integration/smoke.spec.ts | Adds baseline smoke coverage for integration harness + fixtures. |
| e2e/integration/signalr.spec.ts | Adds SignalR lifecycle checks (no errors) and request observation. |
| e2e/integration/shockers.spec.ts | Adds basic route render/empty-state checks for shockers/hubs pages. |
| e2e/integration/shares.spec.ts | Adds basic route checks for share pages + non-500 on fake share link. |
| e2e/integration/sessions-connections.spec.ts | Adds basic checks for sessions and connections pages. |
| e2e/integration/public-pages.spec.ts | Adds checks for public pages and auth redirects for protected routes. |
| e2e/integration/oauth-and-password-reset.spec.ts | Adds basic UI checks for OAuth buttons and password reset page behavior. |
| e2e/integration/live-control.spec.ts | Adds basic route checks for live-control related pages and fake IDs. |
| e2e/integration/lib/test-fixtures.ts | Adds API-driven user lifecycle + cookie injection fixtures for integration tests. |
| e2e/integration/lib/global-teardown.ts | Adds CI-only docker-compose shutdown for integration runs. |
| e2e/integration/lib/global-setup.ts | Placeholder globalSetup (docker started by webServer script). |
| e2e/integration/lib/env.ts | Centralizes integration test env defaults. |
| e2e/integration/lib/api-client.ts | Adds API client helpers for signup/activate/login/delete/logout via backend + MailPit. |
| e2e/integration/cross-cutting.spec.ts | Adds cross-cutting checks (headers, 404 behavior, navigation, responsiveness). |
| e2e/integration/auth.spec.ts | Adds integration-level tests for login/signup/logout flows. |
| e2e/integration/api-tokens.spec.ts | Adds integration tests for token creation/listing/deletion via UI. |
| e2e/integration/admin.spec.ts | Adds non-admin coverage ensuring admin routes don’t 500 and handle denial. |
| e2e/integration/account-settings.spec.ts | Adds basic settings/profile/session UI checks. |
| e2e/indexpage.test.ts | Removes the old single Playwright test in favor of the new structure. |
| e2e/e2e/signup-verify.spec.ts | Adds full E2E signup/verify/login tests (MailPit-gated). |
| e2e/e2e/login-logout.spec.ts | Adds full E2E UI-level login/logout structure tests. |
| e2e/e2e/lib/test-fixtures.ts | Adds full E2E fixtures including Turnstile bypass and teardown deletion. |
| e2e/e2e/lib/mailpit.ts | Adds MailPit API helpers for polling emails and extracting links. |
| e2e/e2e/lib/env.ts | Adds full E2E env defaults (next.openshock.dev, optional MailPit). |
| e2e/e2e/lib/api-client.ts | Minimal API client for full E2E teardown/logout. |
| docker-compose.integration.yml | Adds integration backend stack (postgres/redis/mailpit/api) for local+CI. |
| .github/workflows/ci-integration.yml | Adds CI job to run integration tests with Playwright + cached browsers. |
| .env.integration | Adds Vite integration-mode env values (proxy target + site URLs). |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Replace docker-compose.integration.yml with scripts/integration-stack.mjs; update dev-integration setup/teardown and session-connection tests accordingly.
Resolve conflicts and migrate OpenAPI client to @hey-api/openapi-ts. Set cpu-features, protobufjs, and ssh2 build scripts to false in pnpm-workspace.yaml (dev-only Testcontainers deps with optional builds).
Resolve conflicts and realign integration-test work with develop's regenerated API: - vite.config.ts: keep develop's pure resolveServerConfig + localDevChecksPlugin refactor; layer the branch's integration features on top (API proxy, VITE_ env loading, isVitest guard, NODE_TLS for integration mode, unit/components vitest projects). - package.json: take develop's dependency bumps, keep the added test deps (@testing-library/*, jsdom, testcontainers) and integration scripts. - Drop hooks.server.ts to match develop (temporal polyfill now self-guards in hooks.client.ts); the security headers it added are out of scope here. - shares/outgoing: use develop's EmptyState empty state. - Update state tests to develop's tree-shaken API surface (usersGetSelf, userShares*, backendMetadata.set) and ApiUserSelf.hasPassword. - Fix Log.test.ts to assert the (sender, log) listener signature. - Remove cross-cutting security-header tests for the dropped headers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the self-signed/mkcert TLS setup from the integration harness and talk to the API and Vite dev server over plain HTTP instead. The browser only ever talks to the Vite origin (same-origin, no CORS), and the API proxy strips Secure/SameSite=None from set-cookie so the session cookie survives over HTTP. Removes NODE_TLS_REJECT_UNAUTHORIZED and ignoreHTTPSErrors workarounds.
|
Ready to review this PR? Stage has broken it down into 8 individual chapters for you: Chapters generated by Stage for commit 769107b on Jun 23, 2026 3:18pm UTC. |
The rebase onto develop landed these tests on a codebase where the API client exposes flat per-endpoint functions instead of grouped Api objects, userState/backendMetadata semantics changed, and several tested modules (pwnedPasswords, dialog-manager, data-table mergeObjects, color-scheme state) moved into @openshock/svelte-core. - Rewrite mocks against the flat $lib/api exports - Update live-control tests for the v2 LCG endpoint (host/port/pathPrefix) - Rewrite backend-metadata tests for the set()-only store - Add hasPassword to ApiUserSelf fixtures - Drop tests whose subjects now live in svelte-core (port them there) - Declare allowBuilds for testcontainers' native subdeps
- prettier: instrumentation.server.ts (unformatted on develop, fails ci-lint there too) - eslint: justify the deliberate this-alias in AlsContextManager.bind
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 53 changed files in this pull request and generated no new comments.
Suppressed comments (6)
.github/workflows/ci-integration.yml:33
- Other workflows pin GitHub Actions to commit SHAs (e.g.
.github/workflows/ci-build.yml:37). Pinpnpm/action-setuphere as well to avoid depending on a mutable@v6tag.
- uses: pnpm/action-setup@v6
.github/workflows/ci-integration.yml:42
- Other workflows pin GitHub Actions to commit SHAs (e.g.
.github/workflows/ci-build.yml:32). Pinactions/setup-nodehere as well to keep supply-chain protections consistent.
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: 'pnpm'
scripts/dev-integration.mjs:81
shutdown()re-sends SIGINT/SIGTERM to the current process while those signals still have handlers installed (the loop below). That prevents the default termination behaviour and can leavedev:integrationhanging after Ctrl+C / SIGTERM (stack stops, but process never exits). Remove the signal handlers before re-killing, or justprocess.exit()after cleanup.
e2e/integration/signalr.spec.ts:23- Typo in variable name:
signarErrorslooks like it should besignalrErrors(matches the test intent and regex).
// Filter out noise unrelated to SignalR
const signarErrors = errors.filter((e) => /signalr|websocket|hub|negotiate/i.test(e));
expect(signarErrors).toHaveLength(0);
.github/workflows/ci-integration.yml:32
- Other workflows pin GitHub Actions to full commit SHAs (e.g.
.github/workflows/ci-build.yml:27), but this workflow uses floating version tags. Pinning here too improves supply-chain safety and keeps conventions consistent.
This issue also appears in the following locations of the same file:
- line 33
- line 38
- name: Checkout
uses: actions/checkout@v6
e2e/integration/lib/test-fixtures.ts:47
- Defaulting cookie
domainto the backend host (apiHost) means auth cookies won’t be sent when TEST_FRONTEND_URL and TEST_BACKEND_URL have different hostnames (e.g. 127.0.0.1 vs localhost). Default to the frontend hostname for Playwright context cookies in this harness (and keepapiHostonly as a fallback/consistency check).
domain: attrMap['domain'] ?? apiHost,
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 53 changed files in this pull request and generated no new comments.
Suppressed comments (4)
vite.config.ts:273
- Integration mode relies on a fixed baseURL (default http://localhost:5173) in Playwright. Consider enabling
strictPort: trueso Vite fails fast instead of silently switching to another port when 5173 is already in use (which would make the integration runner probe the wrong origin).
vite.config.ts:202 LocalServer.configis used as the Viteserverconfig shape, but it currently omitsstrictPort. In integration mode the Playwright baseURL is fixed to http://localhost:5173; without strictPort, Vite will auto-increment the port if 5173 is taken, causing the integration runner to probe the wrong URL.
This issue also appears on line 269 of the same file.
vite.config.ts:261
proxyRes.headers['set-cookie']can be either a string or an array of strings. Calling.mapunconditionally will throw when only a single cookie is set (string case). Normalize to an array before mapping.
e2e/integration/signalr.spec.ts:23- Typo in variable name:
signarErrors→signalErrors. Keeping the correct spelling makes the intent clearer and avoids copy/paste typos elsewhere.
const signarErrors = errors.filter((e) => /signalr|websocket|hub|negotiate/i.test(e));
expect(signarErrors).toHaveLength(0);
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
openshock-app | be25347 | Aug 04 2026, 11:25 AM |
No description provided.