import { expect, test } from "@playwright/test" const USER_ID = "00000000-0000-0000-0000-000000000001" const PROJECT_ID = "65df675b-013b-4b1f-ab2d-075dadbcd0d9" const CAPTION_PRESET_ID = "00000000-0000-0000-0000-000000000010" const TRANSCRIPTION_ARTIFACT_ID = "00000000-0000-0000-0000-000000000020" const TRANSCRIPTION_ID = "00000000-0000-0000-0000-000000000030" const CAPTION_JOB_ID = "00000000-0000-0000-0000-000000000040" const PRIMARY_FILE_KEY = "projects/test/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", } test.describe("Caption Settings Step", () => { test("should recover a missing transcription artifact from project data", 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: "caption-settings", completed_steps: [ "upload", "verify", "silence-settings", "processing", "fragments", "transcription-settings", "transcription-processing", "subtitle-revision", ], primary_file_key: PRIMARY_FILE_KEY, video_url: "http://localhost:9000/projects/test/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: null, transcription_artifact_id: null, caption_preset_id: CAPTION_PRESET_ID, caption_style_config: null, captioned_video_path: null, }, }, is_active: true, created_at: "2025-06-01T00:00:00Z", updated_at: "2025-06-01T00:00:00Z", } let savedWizardState: Record | null = null let generateRequestBody: Record | null = null let generateRequestCount = 0 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/media/artifacts/", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([ { id: TRANSCRIPTION_ARTIFACT_ID, project_id: PROJECT_ID, file_id: null, media_file_id: null, artifact_type: "TRANSCRIPTION_JSON", is_deleted: false, is_active: true, created_at: "2025-06-01T00:00:00Z", updated_at: "2025-06-01T00:00:00Z", }, ]), }) }) await page.route( `**/api/transcribe/transcriptions/by-artifact/${TRANSCRIPTION_ARTIFACT_ID}/`, async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ id: TRANSCRIPTION_ID, artifact_id: TRANSCRIPTION_ARTIFACT_ID, }), }) }, ) await page.route("**/api/captions/presets/", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([ { id: CAPTION_PRESET_ID, user_id: null, name: "Системный пресет", description: null, is_system: true, style_config: {}, preview_url: null, created_at: "2025-06-01T00:00:00Z", updated_at: "2025-06-01T00:00:00Z", }, ]), }) }) await page.route("**/api/tasks/captions-generate/", async (route) => { generateRequestCount += 1 generateRequestBody = route.request().postDataJSON() as Record< string, unknown > await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ job_id: CAPTION_JOB_ID }), }) }) await page.route("**/api/tasks/status/**", async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ status: "RUNNING", progress_pct: 0, output_data: null, }), }) }) await page.goto(`/projects/${PROJECT_ID}`) const captionStep = page.locator("[data-testid='CaptionSettingsStep']") const generateButton = captionStep.getByRole("button", { name: "Генерировать", }) await expect(captionStep).toBeVisible() await expect(captionStep.getByText("Системный пресет")).toBeVisible() await expect(generateButton).toBeEnabled() await expect .poll(() => savedWizardState?.transcription_artifact_id ?? null) .toBe(TRANSCRIPTION_ARTIFACT_ID) await generateButton.click() expect(generateRequestBody).toMatchObject({ video_s3_path: PRIMARY_FILE_KEY, transcription_id: TRANSCRIPTION_ID, project_id: PROJECT_ID, preset_id: CAPTION_PRESET_ID, }) expect(generateRequestCount).toBe(1) await expect .poll(() => savedWizardState?.current_step ?? null) .toBe("caption-processing") await expect .poll(() => savedWizardState?.active_job_id ?? null) .toBe(CAPTION_JOB_ID) await expect(page.locator("[data-testid='ProcessingStep']")).toBeVisible() }) })