Files
bitvid/tests/visual/moderation.spec.ts
T
google-labs-jules[bot]andPR0M3TH3AN c56238b329 Fix moderation fixture click timeout by preventing VideoCard auto-hide
The `VideoCard` component automatically hides itself if it detects no valid playback source (no URL or magnet). This caused the moderation visual test fixtures, which used dummy data without sources, to be hidden (`hidden=""` on root element) in the DOM, even though they were "visible" in terms of layout (display: block forced on inner container). This state prevented Playwright from interacting with the "Show anyway" button, causing click timeouts.

This change:
1. Updates `docs/moderation/fixtures/index.html` to provide a dummy URL for all fixture videos, ensuring the card remains visible and interactive.
2. Implements the missing `onModerationHide` handler in the fixture logic to support the full override/restore lifecycle in tests.
3. Uncomments and restores the failing assertions in `tests/visual/moderation.spec.ts`.
4. Removes `force: true` from click actions as the elements are now properly visible.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-05 23:08:55 +00:00

239 lines
9.5 KiB
TypeScript

import { expect, test } from "@playwright/test";
const FIXTURE_URL = "/docs/moderation/fixtures/index.html";
const STORAGE_KEY = "bitvid:moderation:fixture-overrides";
const SENTINEL_KEY = "__moderationFixturesInit__";
const RESTORE_BUTTON_LABEL = "Restore default moderation";
async function waitForFixtureReady(page) {
await page.waitForSelector('body[data-ready="true"]');
}
function setupStorageReset(page) {
return page.addInitScript(({ storageKey, sentinelKey }) => {
try {
const session = window.sessionStorage;
const local = window.localStorage;
if (session && !session.getItem(sentinelKey)) {
local?.removeItem(storageKey);
session.setItem(sentinelKey, "1");
}
} catch {
/* ignore reset failures in tests */
}
}, { storageKey: STORAGE_KEY, sentinelKey: SENTINEL_KEY });
}
test.beforeEach(async ({ page }) => {
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
page.on('pageerror', err => console.log('PAGE ERROR:', err.message));
await setupStorageReset(page);
});
test.afterEach(async ({ page }) => {
try {
await page.evaluate(({ key, sentinel }) => {
window.localStorage?.removeItem(key);
window.sessionStorage?.removeItem(sentinel);
}, { key: STORAGE_KEY, sentinel: SENTINEL_KEY });
} catch {
// ignore cleanup errors when page already closed
}
});
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);
const overrideCard = page.locator('[data-test-id="show-anyway"]');
const thumbnail = overrideCard.locator('img[data-video-thumbnail]');
await expect(overrideCard).toHaveAttribute(
"data-moderation-override-available",
"true"
);
const showAnywayButton = overrideCard.getByRole("button", { name: "Show anyway" });
const restoreButtonQuery = overrideCard.getByRole("button", {
name: RESTORE_BUTTON_LABEL,
});
await expect(overrideCard).toHaveAttribute("data-autoplay-policy", "blocked");
await expect(thumbnail).toHaveAttribute("data-thumbnail-state", "blurred");
await expect(showAnywayButton).toBeVisible();
await expect(restoreButtonQuery).toHaveCount(0);
await showAnywayButton.click();
await expect(overrideCard).toHaveAttribute(
"data-moderation-override",
"show-anyway"
);
await expect(overrideCard).not.toHaveAttribute("data-autoplay-policy", "blocked");
await expect(thumbnail).not.toHaveAttribute("data-thumbnail-state", "blurred");
const restoreButton = overrideCard.getByRole("button", {
name: RESTORE_BUTTON_LABEL,
});
await expect(restoreButton).toBeVisible();
await expect(overrideCard.getByRole("button", { name: "Show anyway" })).toHaveCount(0);
await page.reload({ waitUntil: "networkidle" });
await waitForFixtureReady(page);
const reloadedCard = page.locator('[data-test-id="show-anyway"]');
const reloadedThumbnail = reloadedCard.locator('img[data-video-thumbnail]');
const reloadedRestoreButton = reloadedCard.getByRole("button", {
name: RESTORE_BUTTON_LABEL,
});
await expect(reloadedCard).toHaveAttribute(
"data-moderation-override",
"show-anyway"
);
await expect(reloadedCard).not.toHaveAttribute("data-autoplay-policy", "blocked");
await expect(reloadedThumbnail).not.toHaveAttribute("data-thumbnail-state", "blurred");
await expect(reloadedRestoreButton).toBeVisible();
await expect(
reloadedCard.getByRole("button", { name: "Show anyway" })
).toHaveCount(0);
await reloadedRestoreButton.click();
await expect(reloadedCard).not.toHaveAttribute(
"data-moderation-override",
"show-anyway"
);
await expect(reloadedCard).toHaveAttribute("data-autoplay-policy", "blocked");
await expect(reloadedThumbnail).toHaveAttribute("data-thumbnail-state", "blurred");
await expect(
reloadedCard.getByRole("button", { name: RESTORE_BUTTON_LABEL })
).toHaveCount(0);
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");
await expect(showAnywayButton).toBeVisible();
await expect(restoreButtonQuery).toHaveCount(0);
await showAnywayButton.click();
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");
await expect(showAnywayButton).toBeVisible();
await expect(restoreButtonQuery).toHaveCount(0);
await showAnywayButton.click();
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();
});
});