import { test, expect } from "#tests/e2e/fixtures/silence" test.describe("Silence Settings Step (Integration)", () => { test.describe("Initial State", () => { test("should display the title and description", async ({ silencePage, }) => { const { silenceStep } = silencePage await expect( silenceStep.getByText("Параметры обнаружения тишины"), ).toBeVisible() await expect( silenceStep.getByText( "Настройте параметры для автоматического обнаружения тихих участков в видео", ), ).toBeVisible() }) test("should show all three sliders with default values", async ({ silencePage, }) => { const { silenceStep } = silencePage // Min silence duration slider: default 200 ms const durationSlider = silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Мин. длительность тишины" }) await expect(durationSlider).toBeVisible() await expect(durationSlider.getByText("200 мс")).toBeVisible() await expect( durationSlider.locator("input[type='range']"), ).toHaveValue("200") // Silence threshold slider: default 16 dB const thresholdSlider = silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Порог тишины" }) await expect(thresholdSlider).toBeVisible() await expect(thresholdSlider.getByText("16 дБ")).toBeVisible() await expect( thresholdSlider.locator("input[type='range']"), ).toHaveValue("16") // Padding slider: default 100 ms const paddingSlider = silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Отступ" }) await expect(paddingSlider).toBeVisible() await expect(paddingSlider.getByText("100 мс")).toBeVisible() await expect( paddingSlider.locator("input[type='range']"), ).toHaveValue("100") }) test("should display help texts for each slider", async ({ silencePage, }) => { const { silenceStep } = silencePage await expect( silenceStep.getByText( "Минимальная длительность тихого участка для обнаружения", ), ).toBeVisible() await expect( silenceStep.getByText( "Уровень громкости ниже которого звук считается тишиной", ), ).toBeVisible() await expect( silenceStep.getByText( "Дополнительный отступ по краям тихих участков", ), ).toBeVisible() }) test("should show back and forward navigation buttons", async ({ silencePage, }) => { const { silenceStep } = silencePage const backButton = silenceStep.getByRole("button", { name: "Назад", }) const forwardButton = silenceStep.getByRole("button", { name: "Далее", }) await expect(backButton).toBeVisible() await expect(backButton).toBeEnabled() await expect(forwardButton).toBeVisible() await expect(forwardButton).toBeEnabled() }) }) test.describe("Slider Interactions", () => { test("should update min silence duration slider value when changed", async ({ silencePage, }) => { const { silenceStep } = silencePage const slider = silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Мин. длительность тишины" }) const input = slider.locator("input[type='range']") await input.fill("500") await expect(input).toHaveValue("500") await expect(slider.getByText("500 мс")).toBeVisible() }) test("should update silence threshold slider value when changed", async ({ silencePage, }) => { const { silenceStep } = silencePage const slider = silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Порог тишины" }) const input = slider.locator("input[type='range']") await input.fill("24") await expect(input).toHaveValue("24") await expect(slider.getByText("24 дБ")).toBeVisible() }) test("should update padding slider value when changed", async ({ silencePage, }) => { const { silenceStep } = silencePage const slider = silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Отступ" }) const input = slider.locator("input[type='range']") await input.fill("250") await expect(input).toHaveValue("250") await expect(slider.getByText("250 мс")).toBeVisible() }) }) test.describe("Successful Submission", () => { test("should submit with default values and navigate to Processing step", async ({ silencePage, }) => { const { page, silenceStep } = silencePage await silenceStep .getByRole("button", { name: "Далее" }) .click() // Should navigate to Processing step await expect( page.locator("[data-testid='ProcessingStep']"), ).toBeVisible({ timeout: 10_000 }) }) test("should send correct request body with default values to the API", async ({ silencePage, }) => { const { page, projectId, silenceStep } = silencePage let postBody: Record | null = null page.on("request", (req) => { if ( req.url().includes("/api/tasks/silence-detect/") && req.method() === "POST" ) { postBody = req.postDataJSON() } }) await silenceStep .getByRole("button", { name: "Далее" }) .click() await expect( page.locator("[data-testid='ProcessingStep']"), ).toBeVisible({ timeout: 10_000 }) expect(postBody).not.toBeNull() expect(postBody!.project_id).toBe(projectId) expect(postBody!.min_silence_duration_ms).toBe(200) expect(postBody!.silence_threshold_db).toBe(16) expect(postBody!.padding_ms).toBe(100) // file_key should be a non-empty string populated from the uploaded file expect(postBody!.file_key).toBeTruthy() expect(typeof postBody!.file_key).toBe("string") }) test("should send correct request body with modified slider values", async ({ silencePage, }) => { const { page, silenceStep } = silencePage // Modify all three sliders await silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Мин. длительность тишины" }) .locator("input[type='range']") .fill("800") await silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Порог тишины" }) .locator("input[type='range']") .fill("30") await silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Отступ" }) .locator("input[type='range']") .fill("375") let postBody: Record | null = null page.on("request", (req) => { if ( req.url().includes("/api/tasks/silence-detect/") && req.method() === "POST" ) { postBody = req.postDataJSON() } }) await silenceStep .getByRole("button", { name: "Далее" }) .click() await expect( page.locator("[data-testid='ProcessingStep']"), ).toBeVisible({ timeout: 10_000 }) expect(postBody).not.toBeNull() expect(postBody!.min_silence_duration_ms).toBe(800) expect(postBody!.silence_threshold_db).toBe(30) expect(postBody!.padding_ms).toBe(375) }) }) test.describe("Navigation", () => { test("should navigate back to Verify step when back button is clicked", async ({ silencePage, }) => { const { page, silenceStep } = silencePage await silenceStep .getByRole("button", { name: "Назад" }) .click() await expect( page.locator("[data-testid='VerifyStep']"), ).toBeVisible({ timeout: 10_000 }) // Silence Settings step should no longer be visible await expect(silenceStep).not.toBeVisible() }) }) test.describe("Error States", () => { test("should stay on silence settings step when API returns network error", async ({ silencePage, }) => { const { page, silenceStep } = silencePage await page.route("**/api/tasks/silence-detect/**", (route) => route.abort(), ) await page.route("**/api/tasks/silence-detect/", (route) => route.abort(), ) await silenceStep .getByRole("button", { name: "Далее" }) .click() // Should NOT navigate to Processing step await expect( page.locator("[data-testid='ProcessingStep']"), ).not.toBeVisible({ timeout: 5_000 }) // Should remain on Silence Settings step await expect(silenceStep).toBeVisible() }) test("should stay on silence settings step when API returns 500", async ({ silencePage, }) => { const { page, silenceStep } = silencePage await page.route("**/api/tasks/silence-detect/**", (route) => route.fulfill({ status: 500, contentType: "application/json", body: JSON.stringify({ detail: "Internal Server Error", }), }), ) await page.route("**/api/tasks/silence-detect/", (route) => route.fulfill({ status: 500, contentType: "application/json", body: JSON.stringify({ detail: "Internal Server Error", }), }), ) await silenceStep .getByRole("button", { name: "Далее" }) .click() // Should NOT navigate to Processing step await expect( page.locator("[data-testid='ProcessingStep']"), ).not.toBeVisible({ timeout: 5_000 }) // Should remain on Silence Settings step await expect(silenceStep).toBeVisible() }) test("should allow retrying after a network failure", async ({ silencePage, }) => { const { page, silenceStep } = silencePage // First attempt: abort the request await page.route("**/api/tasks/silence-detect/**", (route) => route.abort(), ) await page.route("**/api/tasks/silence-detect/", (route) => route.abort(), ) await silenceStep .getByRole("button", { name: "Далее" }) .click() // Should stay on silence settings await expect( page.locator("[data-testid='ProcessingStep']"), ).not.toBeVisible({ timeout: 5_000 }) await expect(silenceStep).toBeVisible() // Remove intercepts and retry await page.unrouteAll({ behavior: "ignoreErrors" }) await silenceStep .getByRole("button", { name: "Далее" }) .click() // Should succeed and navigate to Processing step await expect( page.locator("[data-testid='ProcessingStep']"), ).toBeVisible({ timeout: 10_000 }) }) test("should show pending text and disable buttons while submission is in flight", async ({ silencePage, }) => { const { page, silenceStep } = silencePage // Delay the API response to observe the pending state await page.route( "**/api/tasks/silence-detect/**", async (route) => { await new Promise((r) => setTimeout(r, 3000)) await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ job_id: "fake-job-id" }), }) }, ) await page.route( "**/api/tasks/silence-detect/", async (route) => { await new Promise((r) => setTimeout(r, 3000)) await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ job_id: "fake-job-id" }), }) }, ) await silenceStep .getByRole("button", { name: "Далее" }) .click() // Submit button should show "Запуск..." and be disabled const submitButton = silenceStep.getByRole("button", { name: "Запуск...", }) await expect(submitButton).toBeVisible({ timeout: 2_000 }) await expect(submitButton).toBeDisabled() // Back button should also be disabled during pending state const backButton = silenceStep.getByRole("button", { name: "Назад", }) await expect(backButton).toBeDisabled() }) }) test.describe("State Persistence", () => { test("should persist modified silence settings across page reloads", async ({ silencePage, }) => { const { page, silenceStep } = silencePage // Change the min silence duration slider const durationSlider = silenceStep .locator("[data-testid='Slider']") .filter({ hasText: "Мин. длительность тишины" }) await durationSlider.locator("input[type='range']").fill("750") // Verify the value changed await expect(durationSlider.getByText("750 мс")).toBeVisible() // Wait for the debounced save to persist (1000ms debounce + network) await page.waitForTimeout(2500) // Reload the page await page.reload() await page.locator("[data-testid='ProjectWizard']").waitFor() // Should restore to the silence settings step with persisted value const restoredStep = page.locator( "[data-testid='SilenceSettingsStep']", ) await expect(restoredStep).toBeVisible({ timeout: 10_000 }) const restoredSlider = restoredStep .locator("[data-testid='Slider']") .filter({ hasText: "Мин. длительность тишины" }) await expect(restoredSlider.getByText("750 мс")).toBeVisible({ timeout: 5_000, }) await expect( restoredSlider.locator("input[type='range']"), ).toHaveValue("750") }) }) })