Files
Oxicloud/tests/e2e/spa/coverage-helpers.ts
T
Bradley Nelson e3823ce470 test(e2e): Playwright + Vitest coverage harness and test instrumentation
Add an end-to-end and unit test suite for the SvelteKit frontend:

- Playwright e2e specs (tests/e2e/spa) with a throwaway container stack,
  codegen scenarios, and an Istanbul-based coverage report pipeline.
- Vitest unit tests across API endpoints, components, stores and composables.
- `data-testid` hooks on interactive elements (AppShell, FileViewer,
  ShareDialog, search, photos, files breadcrumbs, login/Nextcloud flows,
  public share pages) so the e2e suite can target them deterministically.
- Serve the SPA app-shell CSP from a <meta> policy (svelte.config.js) plus a
  middleware that skips the CSP header on HTML; move the Nextcloud Login Flow
  v2 grant page to the SvelteKit /nextcloud/login route.
- `just front-codegen` recipe and start-server-spa.sh harness.

Make the test environment robust and consistent:
- Install a deterministic in-memory localStorage/sessionStorage in the Vitest
  setup so storage behaves identically across Node versions (Node 26 ships a
  native Web Storage global that otherwise shadows jsdom's).
- Pin devenv to Node 26 + PostgreSQL 18 and pin every CI job to Node 26.3.0
  so the dev shell and CI run the same toolchain versions.

Repair the API/WebDAV (hurl) suite, which had drifted from the backend:
- Migrate the removed `/api/folders/{id}/listing` endpoint to `/resources`
  (cursor-paginated `{items:[{resource_type,resource}]}` shape) across the
  batch-copy, grants, nested-group, and WebDAV NC tests + the dav_helpers
  wipe routine.
- Stop photos_etag from uploading the dedup-tracked fixture so the dedup
  blob-lifecycle test can own its content-addressed blob exclusively.
- dedup_create now asserts the idempotent same-content re-upload (201 +
  existing file id) instead of the stale 409 expectation.

Generated coverage reports, nyc output and the e2e server runtime data dir
are gitignored rather than committed.
2026-06-22 00:05:06 -06:00

52 lines
2.0 KiB
TypeScript

import * as fs from 'fs';
import * as path from 'path';
import { Page, expect } from '@playwright/test';
import { test as base, TEST_ADMIN } from '../scenarios/helpers';
/**
* `test` for the SvelteKit SPA coverage suite. Extends the shared `test`
* (JS-error guard + container/stack support) with an auto fixture that, after
* every test, reads the Istanbul `window.__coverage__` accumulated by the
* instrumented build and appends it to `.nyc_output/` for an nyc report.
*
* Import `test`/`expect` from here in every `spa/*.spec.ts`.
*/
const NYC_DIR = path.join(__dirname, '..', '.nyc_output');
export const test = base.extend<{ collectCoverage: void }>({
collectCoverage: [
async ({ page }, use, testInfo) => {
await use();
// The instrumented bundle accumulates into window.__coverage__ across
// SPA navigations; read it once at test end. May be undefined if the
// page never loaded the app (pure-API test) — skip those.
const coverage = await page
.evaluate(() => (window as unknown as { __coverage__?: unknown }).__coverage__)
.catch(() => undefined);
if (coverage) {
const file = path.join(
NYC_DIR,
`coverage-${testInfo.workerIndex}-${testInfo.testId}.json`,
);
fs.writeFileSync(file, JSON.stringify(coverage));
}
},
{ auto: true },
],
});
export { expect, TEST_ADMIN };
/**
* Sign in through the SvelteKit login UI and wait until the app shell is
* mounted. Uses stable `data-testid` selectors (kept in VITE_E2E builds).
*/
export async function uiLogin(page: Page, admin = TEST_ADMIN): Promise<void> {
await page.goto('/login');
await page.getByTestId('login-username-input').fill(admin.username);
await page.getByTestId('login-password-input').fill(admin.password);
await page.getByTestId('login-submit-btn').click();
await page.waitForURL('**/files**', { timeout: 15_000 });
await expect(page.getByTestId('appshell-logo-link')).toBeVisible({ timeout: 15_000 });
}