import { expect, test } from "@playwright/test" const USER_ID = "00000000-0000-0000-0000-000000000001" const PROJECT_ID = "75df675b-013b-4b1f-ab2d-075dadbcd0d9" const DETECT_JOB_ID = "00000000-0000-0000-0000-000000000050" const APPLY_JOB_ID = "00000000-0000-0000-0000-000000000051" const TRANSCRIPTION_JOB_ID = "00000000-0000-0000-0000-000000000052" const ORIGINAL_FILE_KEY = "projects/test/original-video.mp4" const ORIGINAL_FILE_URL = "http://localhost:4444/files/original-video.mp4" const CUT_FILE_KEY = "projects/test/cut-video.mp4" const CUT_FILE_URL = "http://localhost:4444/files/cut-video.mp4" const DEFAULT_USER = { id: USER_ID, username: "testuser", email: "test@example.com", first_name: "Test", last_name: "User", phone_number: null, avatar: null, email_verified: true, phone_verified: false, is_active: true, is_staff: false, is_superuser: false, date_joined: "2025-01-01T00:00:00Z", } const MOCK_SEGMENTS = [ { start_ms: 5000, end_ms: 8000 }, { start_ms: 15000, end_ms: 19000 }, ] test.describe("Silence Apply Flow", () => { test("should show processing for cut application and transcribe the processed video", async ({ page, }) => { let project: Record = { id: PROJECT_ID, owner_id: USER_ID, name: "Тестовый проект", description: null, language: "auto", folder: null, status: "DRAFT", workspace_state: { wizard: { current_step: "fragments", completed_steps: [ "upload", "verify", "silence-settings", "processing", ], primary_file_key: ORIGINAL_FILE_KEY, video_url: ORIGINAL_FILE_URL, original_file_name: "original-video.mp4", silence_settings: { min_silence_duration_ms: 200, silence_threshold_db: 16, padding_ms: 100, }, active_job_id: null, active_job_type: null, silence_job_id: DETECT_JOB_ID, transcription_artifact_id: null, caption_preset_id: null, caption_style_config: null, captioned_video_path: null, captioned_video_file_id: null, }, }, is_active: true, created_at: "2025-06-01T00:00:00Z", updated_at: "2025-06-01T00:00:00Z", } let savedWizardState: Record | null = null let applyStatus = "RUNNING" let transcriptionRequestBody: Record | null = null await page.context().addCookies([ { name: "access_token", value: "fake-access-jwt", domain: "localhost", path: "/", }, { name: "refresh_token", value: "fake-refresh-jwt", domain: "localhost", path: "/", }, ]) await page.route("**/api/users/me/", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(DEFAULT_USER), }) }) await page.route(`**/api/projects/${PROJECT_ID}/`, async (route) => { if (route.request().method() === "GET") { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(project), }) return } if (route.request().method() === "PATCH") { const body = route.request().postDataJSON() as { workspace_state?: { wizard?: Record } } savedWizardState = body.workspace_state?.wizard ?? null project = { ...project, workspace_state: body.workspace_state ?? project.workspace_state, } await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(project), }) return } await route.fallback() }) await page.route("**/api/files/get_file/**", async (route) => { const url = new URL(route.request().url()) const filePath = url.searchParams.get("file_path") const fileUrl = filePath === CUT_FILE_KEY ? CUT_FILE_URL : ORIGINAL_FILE_URL await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ file_url: fileUrl, file_path: filePath, }), }) }) await page.route("**/api/tasks/status/**", async (route) => { const url = route.request().url() if (url.includes(DETECT_JOB_ID)) { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ status: "DONE", job_type: "SILENCE_DETECT", progress_pct: 100, output_data: { silent_segments: MOCK_SEGMENTS, duration_ms: 30000, }, }), }) return } if (url.includes(APPLY_JOB_ID)) { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ status: applyStatus, job_type: "SILENCE_APPLY", progress_pct: applyStatus === "DONE" ? 100 : 30, output_data: applyStatus === "DONE" ? { file_path: CUT_FILE_KEY, file_url: CUT_FILE_URL, } : null, }), }) return } await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ status: "RUNNING", progress_pct: 0, output_data: null, }), }) }) await page.route("**/api/tasks/silence-apply/", async (route) => { await route.fulfill({ status: 202, contentType: "application/json", body: JSON.stringify({ job_id: APPLY_JOB_ID }), }) }) await page.route("**/api/tasks/transcription-generate/", async (route) => { transcriptionRequestBody = route.request().postDataJSON() as Record< string, unknown > await route.fulfill({ status: 202, contentType: "application/json", body: JSON.stringify({ job_id: TRANSCRIPTION_JOB_ID }), }) }) await page.goto(`/projects/${PROJECT_ID}`) const fragmentsStep = page.locator("[data-testid='FragmentsStep']") await expect(fragmentsStep).toBeVisible() await fragmentsStep.getByRole("button", { name: "Применить" }).click() await expect(page.locator("[data-testid='ProcessingStep']")).toBeVisible() await expect .poll(() => savedWizardState?.active_job_type ?? null) .toBe("SILENCE_APPLY") await expect .poll(() => savedWizardState?.current_step ?? null) .toBe("processing") applyStatus = "DONE" const transcriptionStep = page.locator( "[data-testid='TranscriptionSettingsStep']", ) await expect(transcriptionStep).toBeVisible({ timeout: 10_000 }) await expect .poll(() => savedWizardState?.primary_file_key ?? null) .toBe(CUT_FILE_KEY) await transcriptionStep .getByRole("button", { name: "Сгенерировать субтитры" }) .click() expect(transcriptionRequestBody).toMatchObject({ file_key: CUT_FILE_KEY, project_id: PROJECT_ID, }) }) })