e3823ce470
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.
106 lines
5.2 KiB
Bash
Executable File
106 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Interactive Playwright codegen driver (invoked by `just front-codegen`).
|
||
#
|
||
# Pick a starting-point recorder from scenarios/codegen/, record against a
|
||
# throwaway container stack, then turn the recording into a real spec:
|
||
# record → name (re-prompts until free) → $EDITOR paste → assemble → run →
|
||
# reopen in --debug on failure.
|
||
set -euo pipefail
|
||
|
||
# Run from tests/e2e regardless of how we were invoked.
|
||
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
||
# ── Pick a recorder (menu of scenarios/codegen/*.spec.ts) ─────────────────────
|
||
shopt -s nullglob
|
||
files=(scenarios/codegen/*.spec.ts)
|
||
[[ ${#files[@]} -gt 0 ]] || { echo "no recorder files in scenarios/codegen/" >&2; exit 1; }
|
||
names=()
|
||
for f in "${files[@]}"; do names+=("$(basename "$f" .spec.ts)"); done
|
||
echo "Pick a codegen starting point:"
|
||
PS3="› "
|
||
select n in "${names[@]}"; do [[ -n "${n:-}" ]] && break; echo " invalid choice — enter a number"; done
|
||
echo "→ recorder: $n"
|
||
|
||
# ── Environment ───────────────────────────────────────────────────────────────
|
||
# NixOS (and distros where Playwright's bundled chromium can't run) need a
|
||
# system chromium. Respect an explicit PW_CHROMIUM_PATH, else auto-detect.
|
||
chromium="${PW_CHROMIUM_PATH:-$(command -v chromium 2>/dev/null || true)}"
|
||
if [[ -n "$chromium" ]]; then export PW_CHROMIUM_PATH="$chromium"; echo "→ chromium: $chromium"; fi
|
||
# Build the app image from the CURRENT source for codegen, so recordings reflect
|
||
# the latest frontend (and its data-testid hooks) instead of a stale image.
|
||
#
|
||
# We build it HERE with the buildx CLI rather than letting the Testcontainers
|
||
# fixture build it. The fixture's JS-driven BuildKit build does NOT reuse the
|
||
# local cargo cache mounts, so it cold-compiles the whole binary every run
|
||
# (>5 min) and blows past the 200 s stack-setup timeout. The CLI build uses the
|
||
# `builder-cache` stage and the shared cache mounts, so it recompiles only what
|
||
# changed (seconds on a warm cache); we then hand the tag to the fixture via
|
||
# $OXICLOUD_IMAGE, which makes it skip its own build entirely.
|
||
#
|
||
# Escape hatch: export OXICLOUD_IMAGE yourself to force-reuse a prebuilt image
|
||
# (it must be built with --build-arg VITE_E2E=1, and may be stale vs local edits).
|
||
if [[ -n "${OXICLOUD_IMAGE:-}" ]]; then
|
||
echo "→ OXICLOUD_IMAGE=$OXICLOUD_IMAGE (env override) — reusing it; needs VITE_E2E=1 and may be stale."
|
||
else
|
||
REPO_ROOT="$(cd ../.. && pwd)"
|
||
OXICLOUD_IMAGE="oxicloud-e2e:latest"
|
||
echo "→ building $OXICLOUD_IMAGE from current source (buildx, incremental — only changed crates/assets recompile)…"
|
||
DOCKER_BUILDKIT=1 docker build \
|
||
--build-arg BUILDER=builder-cache \
|
||
--build-arg BIN_DIR=/app/bin \
|
||
--build-arg VITE_E2E=1 \
|
||
--tag "$OXICLOUD_IMAGE" \
|
||
"$REPO_ROOT"
|
||
export OXICLOUD_IMAGE
|
||
echo "→ OXICLOUD_IMAGE=$OXICLOUD_IMAGE (prebuilt; fixture will reuse it)"
|
||
fi
|
||
export OXICLOUD_E2E_CONTAINERS=1
|
||
|
||
# ── Record ────────────────────────────────────────────────────────────────────
|
||
npx playwright test -c playwright.codegen.config.ts "scenarios/codegen/$n.spec.ts" --headed --workers=1
|
||
|
||
# ── Save the recording as a real spec ─────────────────────────────────────────
|
||
# Keeps the template's setup (apiLogin/goto), drops page.pause(), and splices in
|
||
# the steps you paste (handles a full codegen file or bare action lines).
|
||
echo
|
||
# Prompt for a spec name until it's valid and not already taken (blank = skip).
|
||
spec=""
|
||
while :; do
|
||
read -rp "Save recording to a spec? Enter a name (blank to skip): " out || true
|
||
[[ -z "${out:-}" ]] && break
|
||
spec="$(node scripts/finish-codegen.mjs --resolve "$out")" && break || true
|
||
done
|
||
[[ -n "$spec" ]] || exit 0
|
||
|
||
# Collect the recording in $VISUAL/$EDITOR (paste, then save & close). For GUI
|
||
# editors set a blocking flag, e.g. EDITOR="code --wait". Falls back to nano/vi,
|
||
# then to a Ctrl-D paste if no editor is available.
|
||
editor="${VISUAL:-${EDITOR:-}}"
|
||
[[ -z "$editor" ]] && editor="$(command -v nano 2>/dev/null || command -v vi 2>/dev/null || true)"
|
||
tmp="$(mktemp --suffix=.recording.ts)"
|
||
trap 'rm -f "$tmp"' EXIT
|
||
if [[ -n "$editor" ]]; then
|
||
echo "Opening $editor — paste the recorded output, then save & close."
|
||
$editor "$tmp"
|
||
recording="$(cat "$tmp")"
|
||
else
|
||
echo "No \$VISUAL/\$EDITOR set. Paste the recorded output, then press Ctrl-D:"
|
||
recording="$(cat)"
|
||
fi
|
||
|
||
if [[ -z "${recording//[[:space:]]/}" ]]; then
|
||
echo "(empty — nothing saved)"
|
||
exit 0
|
||
fi
|
||
|
||
printf '%s' "$recording" | node scripts/finish-codegen.mjs "scenarios/codegen/$n.spec.ts" "$out" >/dev/null
|
||
|
||
# ── Run the new spec; reopen in --debug on failure ────────────────────────────
|
||
echo "→ running $spec"
|
||
if npx playwright test -c playwright.containers.config.ts "$spec" --workers=1; then
|
||
echo "✓ $spec passed"
|
||
else
|
||
echo "✗ $spec failed — reopening in debug mode…"
|
||
npx playwright test -c playwright.containers.config.ts "$spec" --workers=1 --debug
|
||
fi
|