Files
Oxicloud/frontend/src/app.html
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

82 lines
2.6 KiB
HTML

<!doctype html>
<html lang="en" data-color-scheme>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
<meta name="color-scheme" content="light dark" />
%sveltekit.head%
<!--
Anti-FOUC theme init — runs synchronously before first paint.
Ported from static/js/core/theme-init.js. Keeps the legacy
`oxicloud_theme` localStorage key and `data-color-scheme` attribute
so existing users keep their preference across the migration.
Placed AFTER %sveltekit.head% so it follows the CSP <meta> SvelteKit
injects there, hence it IS governed by that policy. svelte.config.js
parses this file, finds this <script> by id="theme-init", and adds its
SHA-256 to script-src automatically — so it's allowed inline (zero extra
request, no 'unsafe-inline') and the hash stays in sync if you edit it.
-->
<script id="theme-init">
(function () {
try {
var s = localStorage.getItem('oxicloud_theme');
var h = document.documentElement;
if (s === 'light' || s === 'dark') h.setAttribute('data-color-scheme', s);
else h.removeAttribute('data-color-scheme');
} catch (e) {
/* localStorage unavailable — fall back to OS preference */
}
})();
</script>
<!--
Instant boot splash. Paints on HTML parse (before the app bundle and its
CSS load), covering the otherwise-blank gap during JS download/parse for a
faster perceived first paint. The root layout removes `#app-splash` as soon
as it mounts. `light-dark()` + the early `color-scheme` rules match the
resolved theme so there's no colour flash when the app CSS arrives.
-->
<style>
html[data-color-scheme='dark'] {
color-scheme: dark;
}
html[data-color-scheme='light'] {
color-scheme: light;
}
#app-splash {
position: fixed;
inset: 0;
z-index: 9999;
display: grid;
place-items: center;
background: light-dark(#f5f7fa, #0f172a);
}
#app-splash__spinner {
width: 38px;
height: 38px;
border-radius: 50%;
border: 3px solid light-dark(#e2e8f0, #334155);
border-top-color: #ff5e3a;
animation: app-splash-spin 0.7s linear infinite;
}
@keyframes app-splash-spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
#app-splash__spinner {
animation: none;
}
}
</style>
</head>
<body data-sveltekit-preload-data="hover">
<div id="app-splash" role="status" aria-label="Loading">
<div id="app-splash__spinner"></div>
</div>
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>