import path from "node:path" import { test as base, type Locator, type Page } from "@playwright/test" import { createProjectViaApi, deleteProjectViaApi, loginAsAdmin, } from "#tests/e2e/support/auth-api" export const MOCK_SEGMENTS = [ { start_ms: 5000, end_ms: 8000 }, { start_ms: 15000, end_ms: 19000 }, { start_ms: 32000, end_ms: 35000 }, { start_ms: 45000, end_ms: 50000 }, ] export const MOCK_DURATION_MS = 60000 export const MOCK_TOTAL_REMOVED_MS = MOCK_SEGMENTS.reduce( (sum, s) => sum + (s.end_ms - s.start_ms), 0, ) interface FragmentsPage { page: Page projectId: string fragmentsStep: Locator jobId: string } export const test = base.extend<{ fragmentsPage: FragmentsPage }>({ fragmentsPage: async ({ page }, use) => { const tokens = await loginAsAdmin() await page.context().addCookies([ { name: "access_token", value: tokens.accessToken, domain: "localhost", path: "/", }, { name: "refresh_token", value: tokens.refreshToken, domain: "localhost", path: "/", }, ]) const suffix = Date.now().toString(36) const projectId = await createProjectViaApi( tokens.accessToken, `fragments-test-${suffix}`, ) // Navigate to project wizard await page.goto(`/projects/${projectId}`) await page.locator("[data-testid='ProjectWizard']").waitFor() // Upload test video file const testVideoPath = path.resolve( __dirname, "../assets/test-video.mp4", ) const fileInput = page .locator("[data-testid='UploadStep']") .locator("input[type='file']") await fileInput.setInputFiles(testVideoPath) // Wait for wizard to advance to Verify step await page .locator("[data-testid='VerifyStep']") .waitFor({ timeout: 30_000 }) // Wait for file processing to complete await page .locator("[data-testid='VerifyStep']") .getByText("Готово к обработке") .waitFor({ timeout: 10_000 }) // Advance to Silence Settings step await page .getByRole("button", { name: "Далее: Настройки тишины" }) .click() const silenceStep = page.locator("[data-testid='SilenceSettingsStep']") await silenceStep.waitFor({ timeout: 10_000 }) // Intercept task status polling — initially return RUNNING let taskStatusResponse = { status: "RUNNING", progress_pct: 0, output_data: null as Record | null, } await page.route("**/api/tasks/status/**", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(taskStatusResponse), }) }) // Capture job_id from the silence-detect submission let capturedJobId = "" page.on("response", async (res) => { if ( res.url().includes("/api/tasks/silence-detect/") && res.request().method() === "POST" && res.status() === 200 ) { try { const body = await res.json() if (body?.job_id) capturedJobId = body.job_id } catch { // ignore } } }) // Click "Далее" to submit silence detection await silenceStep.getByRole("button", { name: "Далее" }).click() // Wait for ProcessingStep to appear await page .locator("[data-testid='ProcessingStep']") .waitFor({ timeout: 10_000 }) // Now switch the task status to DONE with mock data to trigger auto-advance taskStatusResponse = { status: "DONE", progress_pct: 100, output_data: { silent_segments: MOCK_SEGMENTS, duration_ms: MOCK_DURATION_MS, }, } // Wait for auto-advance to FragmentsStep (polls every 2s) const fragmentsStep = page.locator("[data-testid='FragmentsStep']") await fragmentsStep.waitFor({ timeout: 15_000 }) const fragmentsPage: FragmentsPage = { page, projectId, fragmentsStep, jobId: capturedJobId, } await use(fragmentsPage) // Cleanup: delete project try { await deleteProjectViaApi(tokens.accessToken, projectId) } catch { // Best-effort cleanup } }, }) export { expect } from "@playwright/test"