diff --git a/examples/reproducers/issue-moderation-ui/README.md b/examples/reproducers/issue-moderation-ui/README.md index 2269d7d7..ed163592 100644 --- a/examples/reproducers/issue-moderation-ui/README.md +++ b/examples/reproducers/issue-moderation-ui/README.md @@ -1,24 +1,22 @@ -# Moderation UI "Show Anyway" Button Visibility Issue Reproducer +# Moderation UI Failure Reproducer -This reproducer demonstrates the issue where the "Show anyway" button in the moderation UI is not visible (or not interactable) in the test environment, even though the logic seems correct. +This reproducer demonstrates a known issue where the "Show anyway" button and related moderation badges fail to render or be interactable in the test environment (headless mode), despite logic indicating they should be present. ## Issue Description -- **Known Issue**: `KNOWN_ISSUES.md`: Moderation UI (`tests/visual/moderation.spec.ts`): The "Show anyway" button and related moderation badges fail to render in the test environment, causing `toBeVisible()` assertions to fail. -- **Symptoms**: `Error: element(s) not found` when asserting visibility of the "Show anyway" button. +As noted in `KNOWN_ISSUES.md`: +> **Moderation UI (`tests/visual/moderation.spec.ts`)**: The "Show anyway" button and related moderation badges fail to render in the test environment, causing `toBeVisible()` assertions to fail. The logic for creating the button appears correct (`allowOverride` is true), but the button element is not found in the DOM during the test execution. ## How to Run -1. Navigate to the repository root. -2. Ensure dependencies are installed and the project is built: +1. Ensure the project is built: ```bash - npm ci npm run build - npx playwright install chromium ``` -3. Run the reproducer script using the provided config: + +2. Run the reproducer test using the custom config (which sets the correct test directory and server context): ```bash - npx playwright test -c examples/reproducers/issue-moderation-ui/playwright.config.ts + npx playwright test -c examples/reproducers/playwright.config.ts examples/reproducers/issue-moderation-ui/repro.spec.ts ``` ## Expected Output -The test `repro: show anyway override button visibility` should **fail** with a timeout or "element not found" error on the assertion `await expect(showAnywayButton).toBeVisible();`. +The test should FAIL with timeout errors waiting for the "Show anyway" button to be visible or clickable. diff --git a/examples/reproducers/issue-moderation-ui/repro.spec.ts b/examples/reproducers/issue-moderation-ui/repro.spec.ts index 794d9e79..bb5fde79 100644 --- a/examples/reproducers/issue-moderation-ui/repro.spec.ts +++ b/examples/reproducers/issue-moderation-ui/repro.spec.ts @@ -40,6 +40,34 @@ test.afterEach(async ({ page }) => { }); test.describe("moderation fixtures", () => { + test("blur fixture masks thumbnails and blocks autoplay", async ({ page }) => { + await page.goto(FIXTURE_URL, { waitUntil: "networkidle" }); + await waitForFixtureReady(page); + + const blurCard = page.locator('[data-test-id="blur-threshold"]'); + const thumbnail = blurCard.locator('img[data-video-thumbnail]'); + + await expect(blurCard).toHaveAttribute("data-moderation-report-count", "3"); + await expect(blurCard).toHaveAttribute("data-autoplay-policy", "blocked"); + await expect(thumbnail).toHaveAttribute("data-thumbnail-state", "blurred"); + await expect(blurCard).not.toHaveAttribute( + "data-moderation-override", + "show-anyway" + ); + }); + + test("autoplay fixture disables preview without blurring", async ({ page }) => { + await page.goto(FIXTURE_URL, { waitUntil: "networkidle" }); + await waitForFixtureReady(page); + + const autoplayCard = page.locator('[data-test-id="autoplay-threshold"]'); + const thumbnail = autoplayCard.locator('img[data-video-thumbnail]'); + + await expect(autoplayCard).toHaveAttribute("data-moderation-report-count", "2"); + await expect(autoplayCard).toHaveAttribute("data-autoplay-policy", "blocked"); + await expect(thumbnail).not.toHaveAttribute("data-thumbnail-state", "blurred"); + }); + test("show anyway override persists across reloads", async ({ page }) => { await page.goto(FIXTURE_URL, { waitUntil: "networkidle" }); await waitForFixtureReady(page); @@ -59,14 +87,13 @@ test.describe("moderation fixtures", () => { await expect(overrideCard).toHaveAttribute("data-autoplay-policy", "blocked"); await expect(thumbnail).toHaveAttribute("data-thumbnail-state", "blurred"); - // Uncommented failures: + // Uncommented failing assertions await expect(showAnywayButton).toBeVisible(); await expect(restoreButtonQuery).toHaveCount(0); - // Uncommented failure: + // Uncommented failing interactions await showAnywayButton.click({ force: true }); - /* await expect(overrideCard).toHaveAttribute( "data-moderation-override", "show-anyway" @@ -113,6 +140,105 @@ test.describe("moderation fixtures", () => { await expect( reloadedCard.getByRole("button", { name: "Show anyway" }) ).toBeVisible(); - */ + }); + + test("trusted report hide fixture supports show anyway override", async ({ page }) => { + await page.goto(FIXTURE_URL, { waitUntil: "networkidle" }); + await waitForFixtureReady(page); + + const hideCard = page.locator('[data-test-id="trusted-report-hide"]'); + const badge = hideCard.locator('[data-moderation-badge="true"]'); + + await expect(hideCard).toHaveAttribute( + "data-moderation-override-available", + "true" + ); + const showAnywayButton = hideCard.getByRole("button", { name: "Show anyway" }); + const restoreButtonQuery = hideCard.getByRole("button", { + name: RESTORE_BUTTON_LABEL, + }); + + await expect(hideCard).toHaveAttribute("data-moderation-hidden", "true"); + await expect(hideCard).toHaveAttribute("data-moderation-hide-reason", "trusted-report-hide"); + await expect(hideCard).toHaveAttribute("data-moderation-hide-trusted-report-count", "3"); + await expect(badge).toContainText("Hidden · 3 trusted spam reports"); + + // Uncommented failing assertions + await expect(showAnywayButton).toBeVisible(); + await expect(restoreButtonQuery).toHaveCount(0); + + // Uncommented failing interactions + await showAnywayButton.click({ force: true }); + + await expect(hideCard).not.toHaveAttribute("data-moderation-hidden", "true"); + await expect(hideCard).toHaveAttribute("data-moderation-override", "show-anyway"); + await expect(hideCard.locator('[data-moderation-badge="true"]')).toContainText( + "Showing despite 3 trusted spam reports", + ); + + const restoreButton = hideCard.getByRole("button", { + name: RESTORE_BUTTON_LABEL, + }); + await expect(restoreButton).toBeVisible(); + + await restoreButton.click(); + + await expect(hideCard).toHaveAttribute("data-moderation-hidden", "true"); + await expect(hideCard).not.toHaveAttribute("data-moderation-override", "show-anyway"); + await expect(hideCard.getByRole("button", { name: "Show anyway" })).toBeVisible(); + }); + + test("trusted mute hide fixture annotates mute reason and can be overridden", async ({ + page, + }) => { + await page.goto(FIXTURE_URL, { waitUntil: "networkidle" }); + await waitForFixtureReady(page); + + const muteCard = page.locator('[data-test-id="trusted-mute-hide"]'); + const badge = muteCard.locator('[data-moderation-badge="true"]'); + const thumbnail = muteCard.locator('img[data-video-thumbnail]'); + + await expect(muteCard).toHaveAttribute( + "data-moderation-override-available", + "true" + ); + const showAnywayButton = muteCard.getByRole("button", { name: "Show anyway" }); + const restoreButtonQuery = muteCard.getByRole("button", { + name: RESTORE_BUTTON_LABEL, + }); + + await expect(muteCard).toHaveAttribute("data-moderation-hidden", "true"); + await expect(muteCard).toHaveAttribute("data-moderation-hide-reason", "trusted-mute-hide"); + await expect(muteCard).toHaveAttribute("data-moderation-hide-trusted-mute-count", "1"); + await expect(muteCard).toHaveAttribute("data-moderation-trusted-mute", "true"); + await expect(muteCard).toHaveAttribute("data-moderation-trusted-mute-count", "1"); + await expect(badge).toContainText("Hidden · 1 trusted mute"); + await expect(thumbnail).toHaveAttribute("data-thumbnail-state", "blurred"); + + // Uncommented failing assertions + await expect(showAnywayButton).toBeVisible(); + await expect(restoreButtonQuery).toHaveCount(0); + + // Uncommented failing interactions + await showAnywayButton.click({ force: true }); + + await expect(muteCard).not.toHaveAttribute("data-moderation-hidden", "true"); + await expect(muteCard).toHaveAttribute("data-moderation-override", "show-anyway"); + await expect(muteCard.locator('[data-moderation-badge="true"]')).toContainText( + "Showing despite 1 trusted mute", + ); + await expect(thumbnail).not.toHaveAttribute("data-thumbnail-state", "blurred"); + + const restoreButton = muteCard.getByRole("button", { + name: RESTORE_BUTTON_LABEL, + }); + await expect(restoreButton).toBeVisible(); + + await restoreButton.click(); + + await expect(muteCard).toHaveAttribute("data-moderation-hidden", "true"); + await expect(muteCard).not.toHaveAttribute("data-moderation-override", "show-anyway"); + await expect(thumbnail).toHaveAttribute("data-thumbnail-state", "blurred"); + await expect(muteCard.getByRole("button", { name: "Show anyway" })).toBeVisible(); }); }); diff --git a/examples/reproducers/playwright.config.ts b/examples/reproducers/playwright.config.ts index 5330254d..d6d71c53 100644 --- a/examples/reproducers/playwright.config.ts +++ b/examples/reproducers/playwright.config.ts @@ -1,30 +1,18 @@ import { defineConfig } from "@playwright/test"; - -const HOST = process.env.PLAYWRIGHT_HOST ?? "127.0.0.1"; -const PORT = Number.parseInt(process.env.PLAYWRIGHT_PORT ?? "4173", 10); -const BASE_URL = process.env.PLAYWRIGHT_BASE_URL ?? `http://${HOST}:${PORT}`; +import baseConfig from "../../playwright.config.ts"; export default defineConfig({ - testDir: "../../", - outputDir: "../../artifacts/test-results-repro", - timeout: 60_000, - reporter: [["list"]], - use: { - baseURL: BASE_URL, - viewport: { width: 1280, height: 720 }, - ignoreHTTPSErrors: true, + ...baseConfig, + testDir: ".", + webServer: { + ...baseConfig.webServer, + cwd: "../../", }, projects: [ { name: "repro", - testMatch: "examples/reproducers/**/*.spec.ts", + testMatch: "**/*.spec.ts", + use: baseConfig.use, }, ], - webServer: { - command: `npx http-server . -p ${PORT} -a ${HOST} -c-1 --silent`, - cwd: "../../", - url: `${BASE_URL}/docs/kitchen-sink.html`, - reuseExistingServer: !process.env.CI, - timeout: 120_000, - }, });