mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-03 03:12:31 +00:00
Add automated moderation coverage
This commit is contained in:
1 parent
84c2b647e7
commit
157fa86a8f
4 files changed
+582
-2
No files matched your search
@@ -0,0 +1,317 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Moderation Fixtures • bitvid</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="/css/tailwind.generated.css" />
|
||||
<style>
|
||||
body {
|
||||
background-color: #0f172a;
|
||||
color: #f8fafc;
|
||||
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
main {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1.5rem 4rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
section {
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
section h2 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
section p {
|
||||
color: #cbd5f5;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-ds="new">
|
||||
<main id="moderationFixtureRoot">
|
||||
<h1>Moderation Test Fixtures</h1>
|
||||
<p>
|
||||
These scenarios mirror the QA vectors in <code>docs/moderation/testing.md</code> so
|
||||
automated checks can assert blur thresholds, autoplay blocking, and local
|
||||
override persistence.
|
||||
</p>
|
||||
</main>
|
||||
<script type="module">
|
||||
import { VideoCard } from "/js/ui/components/VideoCard.js";
|
||||
|
||||
const STORAGE_KEY = "bitvid:moderation:fixture-overrides";
|
||||
const fixtures = [
|
||||
{
|
||||
key: "blur-threshold",
|
||||
heading: "Blur threshold (≥3 trusted nudity reports)",
|
||||
description:
|
||||
"Card should be blurred and autoplay disabled until the viewer overrides it.",
|
||||
video: {
|
||||
id: "1".repeat(64),
|
||||
title: "Fixture: Blurred Preview",
|
||||
pubkey: "a".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
blurThumbnail: true,
|
||||
trustedCount: 3,
|
||||
reportType: "nudity",
|
||||
},
|
||||
reporters: [
|
||||
{ name: "Jess F1", pubkey: "b".repeat(64), latest: 1_700_000_010 },
|
||||
{ name: "Mira F1", pubkey: "c".repeat(64), latest: 1_700_000_020 },
|
||||
{ name: "Omar F1", pubkey: "d".repeat(64), latest: 1_700_000_030 },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "autoplay-threshold",
|
||||
heading: "Autoplay threshold (≥2 trusted nudity reports)",
|
||||
description:
|
||||
"Card should block autoplay but stay unblurred so users can inspect the thumbnail.",
|
||||
video: {
|
||||
id: "2".repeat(64),
|
||||
title: "Fixture: Autoplay Blocked",
|
||||
pubkey: "e".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
blurThumbnail: false,
|
||||
trustedCount: 2,
|
||||
reportType: "nudity",
|
||||
},
|
||||
reporters: [
|
||||
{ name: "Jess F1", pubkey: "b".repeat(64), latest: 1_700_000_010 },
|
||||
{ name: "Mira F1", pubkey: "c".repeat(64), latest: 1_700_000_020 },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "show-anyway",
|
||||
heading: "\"Show anyway\" override (persists locally)",
|
||||
description:
|
||||
"Card starts blurred and blocked; activating \"Show anyway\" should persist across reloads.",
|
||||
enableOverride: true,
|
||||
video: {
|
||||
id: "3".repeat(64),
|
||||
title: "Fixture: Override Persistence",
|
||||
pubkey: "f".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
blurThumbnail: true,
|
||||
trustedCount: 3,
|
||||
reportType: "nudity",
|
||||
},
|
||||
reporters: [
|
||||
{ name: "Jess F1", pubkey: "b".repeat(64), latest: 1_700_000_010 },
|
||||
{ name: "Mira F1", pubkey: "c".repeat(64), latest: 1_700_000_020 },
|
||||
{ name: "Omar F1", pubkey: "d".repeat(64), latest: 1_700_000_030 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
window.__MODERATION_FIXTURE_STORAGE_KEY__ = STORAGE_KEY;
|
||||
|
||||
function loadOverrides() {
|
||||
try {
|
||||
const raw = window.localStorage?.getItem(STORAGE_KEY);
|
||||
if (!raw) {
|
||||
return {};
|
||||
}
|
||||
const parsed = JSON.parse(raw);
|
||||
return parsed && typeof parsed === "object" ? parsed : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function persistOverrides(next) {
|
||||
try {
|
||||
window.localStorage?.setItem(STORAGE_KEY, JSON.stringify(next));
|
||||
} catch {
|
||||
/* ignore persistence issues in test fixtures */
|
||||
}
|
||||
}
|
||||
|
||||
function buildSummary(fixture) {
|
||||
if (!fixture?.moderation?.reportType) {
|
||||
return null;
|
||||
}
|
||||
const latest = Array.isArray(fixture.reporters)
|
||||
? fixture.reporters.reduce(
|
||||
(max, reporter) =>
|
||||
Number.isFinite(reporter?.latest)
|
||||
? Math.max(max, Math.floor(reporter.latest))
|
||||
: max,
|
||||
0,
|
||||
)
|
||||
: 0;
|
||||
return {
|
||||
eventId: fixture.video.id,
|
||||
totalTrusted: fixture.moderation.trustedCount,
|
||||
types: {
|
||||
[fixture.moderation.reportType]: {
|
||||
trusted: fixture.moderation.trustedCount,
|
||||
total: fixture.moderation.trustedCount,
|
||||
latest,
|
||||
},
|
||||
},
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
function buildModerationState(fixture, overrides, videoId) {
|
||||
const overrideEntry = overrides?.[videoId];
|
||||
const base = {
|
||||
reportType: fixture.moderation.reportType,
|
||||
trustedCount: fixture.moderation.trustedCount,
|
||||
reporterDisplayNames: Array.isArray(fixture.reporters)
|
||||
? fixture.reporters.map((reporter) => reporter.name)
|
||||
: [],
|
||||
reporterPubkeys: Array.isArray(fixture.reporters)
|
||||
? fixture.reporters.map((reporter) => reporter.pubkey)
|
||||
: [],
|
||||
trustedReporters: Array.isArray(fixture.reporters)
|
||||
? fixture.reporters.map((reporter) => ({
|
||||
pubkey: reporter.pubkey,
|
||||
latest: reporter.latest,
|
||||
}))
|
||||
: [],
|
||||
original: {
|
||||
blockAutoplay: fixture.moderation.blockAutoplay === true,
|
||||
blurThumbnail: fixture.moderation.blurThumbnail === true,
|
||||
},
|
||||
};
|
||||
|
||||
const summary = buildSummary(fixture);
|
||||
if (summary) {
|
||||
base.summary = summary;
|
||||
}
|
||||
|
||||
if (overrideEntry?.showAnyway) {
|
||||
base.viewerOverride = {
|
||||
showAnyway: true,
|
||||
updatedAt: Number.isFinite(overrideEntry.updatedAt)
|
||||
? Math.floor(overrideEntry.updatedAt)
|
||||
: Date.now(),
|
||||
};
|
||||
base.blockAutoplay = false;
|
||||
base.blurThumbnail = false;
|
||||
} else {
|
||||
base.blockAutoplay = fixture.moderation.blockAutoplay === true;
|
||||
base.blurThumbnail = fixture.moderation.blurThumbnail === true;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
function setOverride(overrides, videoId, entry) {
|
||||
const next = { ...overrides };
|
||||
next[videoId] = {
|
||||
...entry,
|
||||
updatedAt: Number.isFinite(entry?.updatedAt)
|
||||
? Math.floor(entry.updatedAt)
|
||||
: Date.now(),
|
||||
};
|
||||
persistOverrides(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
function createCard(fixture, overrides) {
|
||||
const video = {
|
||||
...fixture.video,
|
||||
};
|
||||
video.moderation = buildModerationState(fixture, overrides, video.id);
|
||||
|
||||
const card = new VideoCard({
|
||||
document,
|
||||
video,
|
||||
shareUrl: "#",
|
||||
formatters: {
|
||||
formatTimeAgo: () => "just now",
|
||||
formatNumber: (value) => {
|
||||
if (typeof value === "number") {
|
||||
return value.toLocaleString("en-US");
|
||||
}
|
||||
return String(value ?? "");
|
||||
},
|
||||
},
|
||||
helpers: {
|
||||
isMagnetSupported: () => true,
|
||||
escapeHtml: (value) => String(value ?? ""),
|
||||
toLocaleString: (value, locales, options) => {
|
||||
if (value && typeof value.toLocaleString === "function") {
|
||||
return value.toLocaleString(locales || "en-US", options);
|
||||
}
|
||||
return String(value ?? "");
|
||||
},
|
||||
},
|
||||
assets: {
|
||||
fallbackThumbnailSrc: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
},
|
||||
designSystem: {
|
||||
getMode: () => "new",
|
||||
isNew: () => true,
|
||||
},
|
||||
});
|
||||
|
||||
if (fixture.enableOverride) {
|
||||
card.onModerationOverride = () => {
|
||||
overrides = setOverride(overrides, video.id, { showAnyway: true });
|
||||
card.video.moderation = buildModerationState(
|
||||
fixture,
|
||||
overrides,
|
||||
video.id,
|
||||
);
|
||||
card.refreshModerationUi();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
const root = card.getRoot();
|
||||
root.dataset.testId = fixture.key;
|
||||
return { card, root };
|
||||
}
|
||||
|
||||
function renderFixtures() {
|
||||
const root = document.getElementById("moderationFixtureRoot");
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
let overrides = loadOverrides();
|
||||
|
||||
fixtures.forEach((fixture) => {
|
||||
const section = document.createElement("section");
|
||||
|
||||
const heading = document.createElement("h2");
|
||||
heading.textContent = fixture.heading;
|
||||
section.appendChild(heading);
|
||||
|
||||
if (fixture.description) {
|
||||
const description = document.createElement("p");
|
||||
description.textContent = fixture.description;
|
||||
section.appendChild(description);
|
||||
}
|
||||
|
||||
const { root: cardRoot } = createCard(fixture, overrides);
|
||||
section.appendChild(cardRoot);
|
||||
root.appendChild(section);
|
||||
});
|
||||
}
|
||||
|
||||
renderFixtures();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,15 +2,23 @@
|
||||
|
||||
## Quick checklist
|
||||
- [ ] F1 set cached and used for “trusted reports”.
|
||||
- Automated coverage: `tests/moderation/trusted-report-count.test.mjs` (run via `npm run test:unit`).
|
||||
- [ ] Blur at ≥3 F1 reports of `nudity`; autoplay blocked at ≥2.
|
||||
- Automated coverage: `tests/visual/moderation.spec.ts` blur and autoplay scenarios (run via `npm run test:visual`).
|
||||
- [ ] “Show anyway” works and records only local pref.
|
||||
- Automated coverage: `tests/visual/moderation.spec.ts` show-anyway persistence scenario (run via `npm run test:visual`).
|
||||
- [ ] Mute list (10000) downranks/hides consistently.
|
||||
- Manual: subscribe to fixture B and verify muted author disappears from feeds after refresh.
|
||||
- [ ] Admin lists (30000) take effect only when subscribed.
|
||||
- Manual: load fixture C, toggle subscription, and confirm the hard-hide only applies while subscribed.
|
||||
- [ ] COUNT fallback shows placeholders and doesn’t crash UI.
|
||||
- Manual: point the client at a relay without COUNT support and confirm placeholders render instead of errors.
|
||||
- [ ] Comment threads render with NIP-10; moderation badges show in context.
|
||||
- Manual: open a reported thread and confirm badges + reply ordering remain intact.
|
||||
- [ ] Reports from muted reporters are ignored.
|
||||
- Automated coverage: `tests/moderation/trusted-report-count.test.mjs` (muted reporter case).
|
||||
|
||||
## Fixtures (create on test relays)
|
||||
## Fixtures (create on test relays / served in `/docs/moderation/fixtures/`)
|
||||
|
||||
### A) NIP-56 report events
|
||||
Create three distinct F1 reporter keys; each sends:
|
||||
@@ -66,6 +74,7 @@ Expected: when viewer subscribes to this list, author is hard-hidden.
|
||||
|
||||
## Automated checks (suggested)
|
||||
|
||||
* Unit test `trustedReportCount`.
|
||||
* `npm run test:unit` — covers `tests/moderation/trusted-report-count.test.mjs` for deduping and mute handling.
|
||||
* `npm run test:visual` — runs Playwright fixtures in `tests/visual/moderation.spec.ts` against `/docs/moderation/fixtures/index.html`.
|
||||
* Integration test: emit events over a local relay; assert UI state.
|
||||
* Regression test: ensure Home feed ignores reputation gating.
|
||||
@@ -0,0 +1,154 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { ModerationService } from "../../js/services/moderationService.js";
|
||||
|
||||
const nip19 = {
|
||||
npubEncode(hex) {
|
||||
if (typeof hex !== "string" || !hex) {
|
||||
throw new Error("invalid hex");
|
||||
}
|
||||
return `npub${hex}`;
|
||||
},
|
||||
decode(value) {
|
||||
if (typeof value !== "string" || !value.startsWith("npub")) {
|
||||
throw new Error("invalid npub");
|
||||
}
|
||||
return { type: "npub", data: value.slice(4) };
|
||||
},
|
||||
};
|
||||
|
||||
function withMockedNostrTools(t) {
|
||||
const hadWindow = typeof globalThis.window !== "undefined";
|
||||
const previousWindow = hadWindow ? globalThis.window : undefined;
|
||||
if (!hadWindow) {
|
||||
globalThis.window = {};
|
||||
}
|
||||
const previousGlobalTools = globalThis.NostrTools;
|
||||
const previousWindowTools = globalThis.window?.NostrTools;
|
||||
|
||||
globalThis.NostrTools = { nip19 };
|
||||
if (globalThis.window) {
|
||||
globalThis.window.NostrTools = globalThis.NostrTools;
|
||||
}
|
||||
|
||||
t.after(() => {
|
||||
if (typeof previousGlobalTools === "undefined") {
|
||||
delete globalThis.NostrTools;
|
||||
} else {
|
||||
globalThis.NostrTools = previousGlobalTools;
|
||||
}
|
||||
|
||||
if (globalThis.window) {
|
||||
if (typeof previousWindowTools === "undefined") {
|
||||
delete globalThis.window.NostrTools;
|
||||
} else {
|
||||
globalThis.window.NostrTools = previousWindowTools;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hadWindow) {
|
||||
delete globalThis.window;
|
||||
} else {
|
||||
globalThis.window = previousWindow;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createReport({ id, reporter, eventId, createdAt, type = "nudity" }) {
|
||||
return {
|
||||
kind: 1984,
|
||||
id,
|
||||
pubkey: reporter,
|
||||
created_at: createdAt,
|
||||
tags: [
|
||||
["e", eventId],
|
||||
["report", type],
|
||||
],
|
||||
content: "fixture report",
|
||||
};
|
||||
}
|
||||
|
||||
test("trustedReportCount dedupes multiple reports from the same trusted reporter", (t) => {
|
||||
withMockedNostrTools(t);
|
||||
|
||||
const eventId = "f".repeat(64);
|
||||
const reporterHex = "a".repeat(64);
|
||||
|
||||
const service = new ModerationService({
|
||||
logger: () => {},
|
||||
userBlocks: {
|
||||
async ensureLoaded() {},
|
||||
isBlocked() {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
service.trustedContacts = new Set([reporterHex]);
|
||||
|
||||
const firstReport = createReport({
|
||||
id: "1".repeat(64),
|
||||
reporter: reporterHex,
|
||||
eventId,
|
||||
createdAt: 1_700_000_000,
|
||||
});
|
||||
|
||||
const duplicateReport = createReport({
|
||||
id: "2".repeat(64),
|
||||
reporter: reporterHex,
|
||||
eventId,
|
||||
createdAt: 1_700_000_500,
|
||||
});
|
||||
|
||||
service.ingestReportEvent(firstReport);
|
||||
service.ingestReportEvent(duplicateReport);
|
||||
|
||||
const summary = service.getTrustedReportSummary(eventId);
|
||||
assert.equal(summary.types.nudity.total, 1);
|
||||
assert.equal(summary.types.nudity.trusted, 1);
|
||||
assert.equal(service.trustedReportCount(eventId, "nudity"), 1);
|
||||
});
|
||||
|
||||
test("trustedReportCount ignores reports from muted or blocked reporters", (t) => {
|
||||
withMockedNostrTools(t);
|
||||
|
||||
const eventId = "e".repeat(64);
|
||||
const mutedReporter = "b".repeat(64);
|
||||
const trustedReporter = "c".repeat(64);
|
||||
|
||||
const service = new ModerationService({
|
||||
logger: () => {},
|
||||
userBlocks: {
|
||||
async ensureLoaded() {},
|
||||
isBlocked(pubkey) {
|
||||
return pubkey === mutedReporter;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
service.trustedContacts = new Set([mutedReporter, trustedReporter]);
|
||||
|
||||
const mutedReport = createReport({
|
||||
id: "3".repeat(64),
|
||||
reporter: mutedReporter,
|
||||
eventId,
|
||||
createdAt: 1_700_000_100,
|
||||
});
|
||||
|
||||
const trustedReport = createReport({
|
||||
id: "4".repeat(64),
|
||||
reporter: trustedReporter,
|
||||
eventId,
|
||||
createdAt: 1_700_000_200,
|
||||
});
|
||||
|
||||
service.ingestReportEvent(mutedReport);
|
||||
service.ingestReportEvent(trustedReport);
|
||||
|
||||
const summary = service.getTrustedReportSummary(eventId);
|
||||
assert.equal(summary.types.nudity.total, 1);
|
||||
assert.equal(summary.types.nudity.trusted, 1);
|
||||
assert.equal(service.trustedReportCount(eventId, "nudity"), 1);
|
||||
const reporters = service.getTrustedReporters(eventId, "nudity");
|
||||
assert.deepEqual(reporters.map((entry) => entry.pubkey), [trustedReporter]);
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
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__";
|
||||
|
||||
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 }) => {
|
||||
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" });
|
||||
|
||||
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" });
|
||||
|
||||
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" });
|
||||
|
||||
const overrideCard = page.locator('[data-test-id="show-anyway"]');
|
||||
const thumbnail = overrideCard.locator('img[data-video-thumbnail]');
|
||||
const showAnywayButton = overrideCard.getByRole("button", { name: "Show anyway" });
|
||||
|
||||
await expect(overrideCard).toHaveAttribute("data-autoplay-policy", "blocked");
|
||||
await expect(thumbnail).toHaveAttribute("data-thumbnail-state", "blurred");
|
||||
await expect(showAnywayButton).toBeVisible();
|
||||
|
||||
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");
|
||||
await expect(overrideCard.getByRole("button", { name: "Show anyway" })).toHaveCount(0);
|
||||
|
||||
await page.reload({ waitUntil: "networkidle" });
|
||||
|
||||
const reloadedCard = page.locator('[data-test-id="show-anyway"]');
|
||||
const reloadedThumbnail = reloadedCard.locator('img[data-video-thumbnail]');
|
||||
|
||||
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(
|
||||
reloadedCard.getByRole("button", { name: "Show anyway" })
|
||||
).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user