mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
d3ec4748dd
* test(preview): buzz / membership / download e2e specs Extends the preview smoke harness with the first tranche of flow coverage (revenue/access surfaces), run report-only against deployed previews as the minted fixture users: - preview-buzz.spec.ts: /purchase/buzz + /pricing render package/plan options and a checkout affordance for a gate-passing paid member (gold); no transact. - preview-membership.spec.ts: gold (paid) sees the current-member view; tester (free) is SSR-redirected to /pricing and sees a subscribe CTA. Keys on the deterministic getServerSideProps redirect, not copy. - preview-download.spec.ts: gold + tester reach a model detail page from the listing (no hardcoded id) and see a download affordance. (restricted is NOT used — it fails the gate; the in-app comparison is free vs paid, and the code has no guaranteed free/paid download difference outside early-access.) Also anchor the preview-config testMatch to the filename (/(^|\/)preview-.*\.spec\.ts$/) — the unanchored form matched a parent dir named like a worktree (civitai-preview-*) and wrongly pulled the non-preview specs (auction/generation/example) into the preview run. Selectors are first-draft (broad/resilient) and get validated by the next report-only preview run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(preview): make download spec light + robust (was crashing the preview) The first draft browsed the heavy /models infinite-scroll listing and clicked the first card, for both gold AND tester across 4 workers. That load crashed the single-replica preview pod (exit 139) and cascaded fast failures into the rest of the suite (base smoke went red as collateral on PR #2467 run jpcgz). Rework: resolve a real model id from the public /api/v1/models API and navigate directly to /models/<id> — no listing, no fragile card selector — and assert as gold only (no free-vs-paid download difference exists outside early-access). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(preview): run smoke suite serially (workers:1) The expanded 19-test suite at workers:4 segfaulted the single-replica preview pod (exit 139) under concurrent heavy-page loads, cascading fast failures into unrelated tests (base smoke loads went red on PR #2467 run s97wq even after the download spec was lightened). The pod can't take concurrent load; run one page at a time. Suite is small + report-only, so serial (~2-3 min) is the right call. Reverses the earlier workers:4 — that suited a 12-test light suite, not this. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: rebuild preview to validate heap fix (2Gi/1536) * test(preview): authed warm-up of heavy pages + 60s nav timeout Mop up the last cold-load flake (`tester loads /models`) now that the pod OOM is fixed. Two changes: - Warm /models + /images (not just /) in setup, AUTHENTICATED with the gold cookie — an anon GET would just hit the gate's /login redirect and never warm the real listing render path. - navigationTimeout 45s -> 60s, aligned with the per-test timeout, so a cold /models goto gets the full budget instead of dying at 45s. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
2.9 KiB
TypeScript
60 lines
2.9 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
import { storageStatePath } from './preview-fixtures';
|
|
|
|
/**
|
|
* E2E: a model detail page renders + a download affordance is present for a
|
|
* gate-passing member, on a deployed PR preview.
|
|
*
|
|
* Resolves a real model id from the PUBLIC API (`/api/v1/models`) and navigates
|
|
* DIRECTLY to `/models/<id>` — deliberately NOT browsing the heavy `/models`
|
|
* infinite-scroll listing. An earlier draft clicked the first listing card;
|
|
* loading that feed concurrently across workers crashed the single-replica
|
|
* preview pod (exit 139) and cascaded failures into the rest of the suite (see
|
|
* PR #2467 run pr-preview-2467-jpcgz). Direct navigation is light and has no
|
|
* fragile card selector.
|
|
*
|
|
* Only `gold` (a gate-passing PAID member): per the source there is no
|
|
* free-vs-paid download difference outside early-access models
|
|
* (`ModelVersionDetails.tsx`: `canDownload = version.canDownload ||
|
|
* hasDownloadPermissions`), so one member role suffices to assert "a member sees
|
|
* a download affordance". `restricted` is never used — it fails the gate.
|
|
*/
|
|
|
|
const DETAIL_URL = /\/models\/\d+(\/|$|\?)/;
|
|
|
|
test.describe('model detail + download affordance (gold)', () => {
|
|
test.use({ storageState: storageStatePath('gold') });
|
|
|
|
test('gold reaches a model detail page and sees a download affordance', async ({ page }) => {
|
|
// Resolve a real, well-formed public model id from the documented v1 API.
|
|
// page.request shares the gold session cookies; the list endpoint is public
|
|
// regardless. nsfw=false keeps us on a model without browsing-level gating.
|
|
const res = await page.request.get('/api/v1/models?limit=1&nsfw=false');
|
|
expect(res.ok(), `/api/v1/models returned HTTP ${res.status()}`).toBeTruthy();
|
|
const body = await res.json();
|
|
const modelId = body?.items?.[0]?.id;
|
|
expect(modelId, 'public API returned at least one model id').toBeTruthy();
|
|
|
|
await page.goto(`/models/${modelId}`, { waitUntil: 'domcontentloaded' });
|
|
|
|
// Cleared the gate (not bounced to either gate destination) and on a detail URL.
|
|
expect(page.url(), 'should not redirect to /login').not.toContain('/login');
|
|
expect(page.url(), 'should not redirect to /preview-restricted').not.toContain(
|
|
'/preview-restricted'
|
|
);
|
|
await expect(page).toHaveURL(DETAIL_URL);
|
|
|
|
// Detail page renders its <h1> model title.
|
|
await expect(page.getByRole('heading', { level: 1 })).toBeVisible({ timeout: 30_000 });
|
|
|
|
// A download affordance: a "Download…" button/link, or the "Download" section
|
|
// header (ModelVersionDetails.tsx). Tolerant to a size suffix / "Selected".
|
|
const downloadControl = page
|
|
.getByRole('button', { name: /download/i })
|
|
.or(page.getByRole('link', { name: /download/i }))
|
|
.or(page.getByText(/^Download( |$)/))
|
|
.first();
|
|
await expect(downloadControl).toBeVisible({ timeout: 30_000 });
|
|
});
|
|
});
|