From 9b18c9b0ba5bcd88f9a44e7e4ac27825c2d2d893 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sat, 25 Oct 2025 18:01:57 -0400 Subject: [PATCH] Extract moderation helpers for video card --- js/ui/components/VideoCard.js | 275 +--------------------- js/ui/moderationUiHelpers.js | 329 +++++++++++++++++++++++++++ tests/moderation/video-card.test.mjs | 77 ++++++- 3 files changed, 410 insertions(+), 271 deletions(-) create mode 100644 js/ui/moderationUiHelpers.js diff --git a/js/ui/components/VideoCard.js b/js/ui/components/VideoCard.js index 8f59843c..673bd1e2 100644 --- a/js/ui/components/VideoCard.js +++ b/js/ui/components/VideoCard.js @@ -1,6 +1,10 @@ import { normalizeDesignSystemContext } from "../../designSystem.js"; import { updateVideoCardSourceVisibility } from "../../utils/cardSourceVisibility.js"; import { userLogger } from "../../utils/logger.js"; +import { + applyModerationContextDatasets, + normalizeVideoModerationContext, +} from "../moderationUiHelpers.js"; const SVG_NAMESPACE = "http://www.w3.org/2000/svg"; @@ -974,162 +978,7 @@ export class VideoCard { } getModerationContext() { - const moderation = - this.video?.moderation && typeof this.video.moderation === "object" - ? this.video.moderation - : null; - - const summary = - moderation?.summary && typeof moderation.summary === "object" - ? moderation.summary - : null; - - let reportType = ""; - if (typeof moderation?.reportType === "string" && moderation.reportType.trim()) { - reportType = moderation.reportType.trim().toLowerCase(); - } - - if (!reportType && summary && summary.types && typeof summary.types === "object") { - for (const [type, stats] of Object.entries(summary.types)) { - if (stats && Number.isFinite(stats.trusted) && Math.floor(stats.trusted) > 0) { - reportType = String(type).toLowerCase(); - break; - } - } - } - - let trustedCount = Number.isFinite(moderation?.trustedCount) - ? Math.max(0, Math.floor(moderation.trustedCount)) - : 0; - - if (!trustedCount && summary && summary.types && typeof summary.types === "object") { - for (const stats of Object.values(summary.types)) { - if (stats && Number.isFinite(stats.trusted)) { - trustedCount = Math.max(trustedCount, Math.floor(stats.trusted)); - } - } - } - - const reporterDisplayNames = Array.isArray(moderation?.reporterDisplayNames) - ? moderation.reporterDisplayNames - .map((name) => (typeof name === "string" ? name.trim() : "")) - .filter(Boolean) - : []; - - const trustedMuted = moderation?.trustedMuted === true; - let trustedMuteCount = Number.isFinite(moderation?.trustedMuteCount) - ? Math.max(0, Math.floor(moderation.trustedMuteCount)) - : 0; - - if (!trustedMuteCount && Array.isArray(moderation?.trustedMuters)) { - const muters = moderation.trustedMuters - .map((value) => (typeof value === "string" ? value.trim() : "")) - .filter(Boolean); - trustedMuteCount = muters.length; - } - - const trustedMuteDisplayNames = Array.isArray(moderation?.trustedMuterDisplayNames) - ? moderation.trustedMuterDisplayNames - .map((name) => (typeof name === "string" ? name.trim() : "")) - .filter(Boolean) - : []; - - const original = - moderation?.original && typeof moderation.original === "object" - ? moderation.original - : {}; - - const activeHidden = moderation?.hidden === true; - const originalHidden = original.hidden === true; - const hideReasonActive = - typeof moderation?.hideReason === "string" ? moderation.hideReason.trim() : ""; - const hideBypassActive = - typeof moderation?.hideBypass === "string" ? moderation.hideBypass.trim() : ""; - const originalHideReason = - typeof original.hideReason === "string" ? original.hideReason.trim() : ""; - const originalHideBypass = - typeof original.hideBypass === "string" ? original.hideBypass.trim() : ""; - const originalHideTriggered = original.hideTriggered === true; - - const normalizeHideCounts = (input) => { - if (!input || typeof input !== "object") { - return null; - } - const normalized = {}; - let hasValue = false; - if (Number.isFinite(input.trustedMuteCount)) { - normalized.trustedMuteCount = Math.max(0, Math.floor(input.trustedMuteCount)); - hasValue = true; - } - if (Number.isFinite(input.trustedReportCount)) { - normalized.trustedReportCount = Math.max(0, Math.floor(input.trustedReportCount)); - hasValue = true; - } - return hasValue ? normalized : null; - }; - - const baseHideCounts = { - trustedMuteCount, - trustedReportCount: trustedCount, - }; - - const activeHideCounts = - normalizeHideCounts(moderation?.hideCounts) || - (activeHidden || hideReasonActive ? { ...baseHideCounts } : null); - const originalHideCounts = - normalizeHideCounts(original?.hideCounts) || - (originalHideTriggered ? { ...baseHideCounts } : null); - - const effectiveHideReason = hideReasonActive || originalHideReason; - const effectiveHideCounts = activeHideCounts || originalHideCounts; - - const blurReason = - typeof moderation?.blurReason === "string" ? moderation.blurReason.trim() : ""; - const originalBlurReason = - typeof original?.blurReason === "string" ? original.blurReason.trim() : ""; - - const context = { - reportType, - friendlyType: reportType ? reportType.replace(/[_-]+/g, " ").trim() : "", - trustedCount, - reporterDisplayNames, - trustedMuted, - trustedMuteCount, - trustedMuteDisplayNames, - blurReason, - originalBlurReason, - originalBlur: original.blurThumbnail === true, - originalBlockAutoplay: original.blockAutoplay === true, - activeBlur: moderation?.blurThumbnail === true, - activeBlockAutoplay: moderation?.blockAutoplay === true, - overrideActive: moderation?.viewerOverride?.showAnyway === true, - activeHidden, - originalHidden, - originalHideTriggered, - hideReason: hideReasonActive, - hideCounts: activeHideCounts, - hideBypass: hideBypassActive, - originalHideReason, - originalHideCounts, - originalHideBypass, - effectiveHideReason, - effectiveHideCounts, - }; - - context.shouldShow = - context.originalBlur || - context.originalBlockAutoplay || - context.trustedCount > 0 || - context.trustedMuted || - context.overrideActive || - context.originalHidden || - context.activeHidden || - context.originalHideTriggered; - - context.allowOverride = - context.originalBlur || context.originalBlockAutoplay || context.originalHidden; - - return context; + return normalizeVideoModerationContext(this.video?.moderation); } buildModerationReasonText(context) { @@ -2372,114 +2221,12 @@ export class VideoCard { } applyModerationDatasets(context = this.getModerationContext()) { - if (!this.root) { - return; - } - - if (context.originalBlockAutoplay && !context.overrideActive) { - this.root.dataset.autoplayPolicy = "blocked"; - } else if (this.root.dataset.autoplayPolicy) { - delete this.root.dataset.autoplayPolicy; - } - - if (context.overrideActive) { - this.root.dataset.moderationOverride = "show-anyway"; - } else if (this.root.dataset.moderationOverride) { - delete this.root.dataset.moderationOverride; - } - - if (this.thumbnailEl && !this.shouldMaskNsfwForOwner) { - if (context.activeBlur) { - this.thumbnailEl.dataset.thumbnailState = "blurred"; - } else if (this.thumbnailEl.dataset.thumbnailState === "blurred") { - delete this.thumbnailEl.dataset.thumbnailState; - } - } - - if (this.authorPicEl && !this.shouldMaskNsfwForOwner) { - if (context.activeBlur) { - this.authorPicEl.dataset.visualState = "blurred"; - } else if (this.authorPicEl.dataset.visualState) { - delete this.authorPicEl.dataset.visualState; - } - } - - const reportCount = Math.max(0, Number(context.trustedCount) || 0); - if (reportCount > 0) { - this.root.dataset.moderationReportCount = String(reportCount); - if (context.reportType) { - this.root.dataset.moderationReportType = context.reportType; - } else if (this.root.dataset.moderationReportType) { - delete this.root.dataset.moderationReportType; - } - } else { - if (this.root.dataset.moderationReportType) { - delete this.root.dataset.moderationReportType; - } - if (this.root.dataset.moderationReportCount) { - delete this.root.dataset.moderationReportCount; - } - } - - if (context.trustedMuted) { - this.root.dataset.moderationTrustedMute = "true"; - const muteCount = Math.max(0, Number(context.trustedMuteCount) || 0); - if (muteCount > 0) { - this.root.dataset.moderationTrustedMuteCount = String(muteCount); - } else if (this.root.dataset.moderationTrustedMuteCount) { - delete this.root.dataset.moderationTrustedMuteCount; - } - } else { - if (this.root.dataset.moderationTrustedMute) { - delete this.root.dataset.moderationTrustedMute; - } - if (this.root.dataset.moderationTrustedMuteCount) { - delete this.root.dataset.moderationTrustedMuteCount; - } - } - - if (context.activeHidden && !context.overrideActive) { - this.root.dataset.moderationHidden = "true"; - const reason = context.effectiveHideReason || context.hideReason; - if (reason) { - this.root.dataset.moderationHideReason = reason; - } else if (this.root.dataset.moderationHideReason) { - delete this.root.dataset.moderationHideReason; - } - - const counts = context.hideCounts || context.effectiveHideCounts || null; - const muteCount = counts && Number.isFinite(counts.trustedMuteCount) - ? Math.max(0, Number(counts.trustedMuteCount)) - : null; - const reportCountHide = counts && Number.isFinite(counts.trustedReportCount) - ? Math.max(0, Number(counts.trustedReportCount)) - : null; - - if (muteCount !== null) { - this.root.dataset.moderationHideTrustedMuteCount = String(muteCount); - } else if (this.root.dataset.moderationHideTrustedMuteCount) { - delete this.root.dataset.moderationHideTrustedMuteCount; - } - - if (reportCountHide !== null) { - this.root.dataset.moderationHideTrustedReportCount = String(reportCountHide); - } else if (this.root.dataset.moderationHideTrustedReportCount) { - delete this.root.dataset.moderationHideTrustedReportCount; - } - } else { - if (this.root.dataset.moderationHidden) { - delete this.root.dataset.moderationHidden; - } - if (this.root.dataset.moderationHideReason) { - delete this.root.dataset.moderationHideReason; - } - if (this.root.dataset.moderationHideTrustedMuteCount) { - delete this.root.dataset.moderationHideTrustedMuteCount; - } - if (this.root.dataset.moderationHideTrustedReportCount) { - delete this.root.dataset.moderationHideTrustedReportCount; - } - } + applyModerationContextDatasets(context, { + root: this.root, + thumbnail: this.thumbnailEl, + avatar: this.authorPicEl, + shouldMaskNsfwForOwner: this.shouldMaskNsfwForOwner, + }); } applyPlaybackDatasets() { diff --git a/js/ui/moderationUiHelpers.js b/js/ui/moderationUiHelpers.js new file mode 100644 index 00000000..473704a8 --- /dev/null +++ b/js/ui/moderationUiHelpers.js @@ -0,0 +1,329 @@ +export function normalizeVideoModerationContext(moderationInput) { + const moderation = + moderationInput && typeof moderationInput === "object" + ? moderationInput + : null; + + const summary = + moderation?.summary && typeof moderation.summary === "object" + ? moderation.summary + : null; + + let reportType = ""; + if (typeof moderation?.reportType === "string" && moderation.reportType.trim()) { + reportType = moderation.reportType.trim().toLowerCase(); + } + + if (!reportType && summary && summary.types && typeof summary.types === "object") { + for (const [type, stats] of Object.entries(summary.types)) { + if (stats && Number.isFinite(stats.trusted) && Math.floor(stats.trusted) > 0) { + reportType = String(type).toLowerCase(); + break; + } + } + } + + let trustedCount = Number.isFinite(moderation?.trustedCount) + ? Math.max(0, Math.floor(moderation.trustedCount)) + : 0; + + if (!trustedCount && summary && summary.types && typeof summary.types === "object") { + for (const stats of Object.values(summary.types)) { + if (stats && Number.isFinite(stats.trusted)) { + trustedCount = Math.max(trustedCount, Math.floor(stats.trusted)); + } + } + } + + const reporterDisplayNames = Array.isArray(moderation?.reporterDisplayNames) + ? moderation.reporterDisplayNames + .map((name) => (typeof name === "string" ? name.trim() : "")) + .filter(Boolean) + : []; + + const trustedMuted = moderation?.trustedMuted === true; + let trustedMuteCount = Number.isFinite(moderation?.trustedMuteCount) + ? Math.max(0, Math.floor(moderation.trustedMuteCount)) + : 0; + + if (!trustedMuteCount && Array.isArray(moderation?.trustedMuters)) { + const muters = moderation.trustedMuters + .map((value) => (typeof value === "string" ? value.trim() : "")) + .filter(Boolean); + trustedMuteCount = muters.length; + } + + const trustedMuteDisplayNames = Array.isArray(moderation?.trustedMuterDisplayNames) + ? moderation.trustedMuterDisplayNames + .map((name) => (typeof name === "string" ? name.trim() : "")) + .filter(Boolean) + : []; + + const original = + moderation?.original && typeof moderation.original === "object" + ? moderation.original + : {}; + + const activeHidden = moderation?.hidden === true; + const originalHidden = original.hidden === true; + const hideReasonActive = + typeof moderation?.hideReason === "string" ? moderation.hideReason.trim() : ""; + const hideBypassActive = + typeof moderation?.hideBypass === "string" ? moderation.hideBypass.trim() : ""; + const originalHideReason = + typeof original.hideReason === "string" ? original.hideReason.trim() : ""; + const originalHideBypass = + typeof original.hideBypass === "string" ? original.hideBypass.trim() : ""; + const originalHideTriggered = original.hideTriggered === true; + + const normalizeHideCounts = (input) => { + if (!input || typeof input !== "object") { + return null; + } + const normalized = {}; + let hasValue = false; + if (Number.isFinite(input.trustedMuteCount)) { + normalized.trustedMuteCount = Math.max(0, Math.floor(input.trustedMuteCount)); + hasValue = true; + } + if (Number.isFinite(input.trustedReportCount)) { + normalized.trustedReportCount = Math.max(0, Math.floor(input.trustedReportCount)); + hasValue = true; + } + return hasValue ? normalized : null; + }; + + const baseHideCounts = { + trustedMuteCount, + trustedReportCount: trustedCount, + }; + + const activeHideCounts = + normalizeHideCounts(moderation?.hideCounts) || + (activeHidden || hideReasonActive ? { ...baseHideCounts } : null); + const originalHideCounts = + normalizeHideCounts(original?.hideCounts) || + (originalHideTriggered ? { ...baseHideCounts } : null); + + const effectiveHideReason = hideReasonActive || originalHideReason; + const effectiveHideCounts = activeHideCounts || originalHideCounts; + + const blurReason = + typeof moderation?.blurReason === "string" ? moderation.blurReason.trim() : ""; + const originalBlurReason = + typeof original?.blurReason === "string" ? original.blurReason.trim() : ""; + + const context = { + reportType, + friendlyType: reportType ? reportType.replace(/[_-]+/g, " ").trim() : "", + trustedCount, + reporterDisplayNames, + trustedMuted, + trustedMuteCount, + trustedMuteDisplayNames, + blurReason, + originalBlurReason, + originalBlur: original.blurThumbnail === true, + originalBlockAutoplay: original.blockAutoplay === true, + activeBlur: moderation?.blurThumbnail === true, + activeBlockAutoplay: moderation?.blockAutoplay === true, + overrideActive: moderation?.viewerOverride?.showAnyway === true, + activeHidden, + originalHidden, + originalHideTriggered, + hideReason: hideReasonActive, + hideCounts: activeHideCounts, + hideBypass: hideBypassActive, + originalHideReason, + originalHideCounts, + originalHideBypass, + effectiveHideReason, + effectiveHideCounts, + }; + + context.shouldShow = + context.originalBlur || + context.originalBlockAutoplay || + context.trustedCount > 0 || + context.trustedMuted || + context.overrideActive || + context.originalHidden || + context.activeHidden || + context.originalHideTriggered; + + context.allowOverride = + context.originalBlur || context.originalBlockAutoplay || context.originalHidden; + + return context; +} + +function collectElements(...inputs) { + const result = []; + const seen = new Set(); + inputs.forEach((input) => { + if (!input) { + return; + } + const values = Array.isArray(input) ? input : [input]; + values.forEach((value) => { + if (!value || typeof value !== "object") { + return; + } + if (!("dataset" in value)) { + return; + } + if (seen.has(value)) { + return; + } + seen.add(value); + result.push(value); + }); + }); + return result; +} + +export function applyModerationContextDatasets( + context, + { + root, + roots, + thumbnail, + thumbnails, + avatar, + avatars, + shouldMaskNsfwForOwner = false, + } = {}, +) { + if (!context || typeof context !== "object") { + return; + } + + const rootElements = collectElements(root, roots); + const thumbnailElements = shouldMaskNsfwForOwner + ? [] + : collectElements(thumbnail, thumbnails); + const avatarElements = shouldMaskNsfwForOwner ? [] : collectElements(avatar, avatars); + + const shouldBlur = context.activeBlur && !context.overrideActive; + + thumbnailElements.forEach((el) => { + if (shouldBlur) { + el.dataset.thumbnailState = "blurred"; + } else if (el.dataset.thumbnailState === "blurred") { + delete el.dataset.thumbnailState; + } + }); + + avatarElements.forEach((el) => { + if (shouldBlur) { + el.dataset.visualState = "blurred"; + } else if (el.dataset.visualState) { + delete el.dataset.visualState; + } + }); + + const applyToRoots = (callback) => { + rootElements.forEach((el) => { + if (el) { + callback(el); + } + }); + }; + + applyToRoots((el) => { + if (context.originalBlockAutoplay && !context.overrideActive) { + el.dataset.autoplayPolicy = "blocked"; + } else if (el.dataset.autoplayPolicy) { + delete el.dataset.autoplayPolicy; + } + + if (context.overrideActive) { + el.dataset.moderationOverride = "show-anyway"; + } else if (el.dataset.moderationOverride) { + delete el.dataset.moderationOverride; + } + }); + + const reportCount = Math.max(0, Number(context.trustedCount) || 0); + applyToRoots((el) => { + if (reportCount > 0) { + el.dataset.moderationReportCount = String(reportCount); + if (context.reportType) { + el.dataset.moderationReportType = context.reportType; + } else if (el.dataset.moderationReportType) { + delete el.dataset.moderationReportType; + } + } else { + if (el.dataset.moderationReportType) { + delete el.dataset.moderationReportType; + } + if (el.dataset.moderationReportCount) { + delete el.dataset.moderationReportCount; + } + } + }); + + applyToRoots((el) => { + if (context.trustedMuted) { + el.dataset.moderationTrustedMute = "true"; + const muteCount = Math.max(0, Number(context.trustedMuteCount) || 0); + if (muteCount > 0) { + el.dataset.moderationTrustedMuteCount = String(muteCount); + } else if (el.dataset.moderationTrustedMuteCount) { + delete el.dataset.moderationTrustedMuteCount; + } + } else { + if (el.dataset.moderationTrustedMute) { + delete el.dataset.moderationTrustedMute; + } + if (el.dataset.moderationTrustedMuteCount) { + delete el.dataset.moderationTrustedMuteCount; + } + } + }); + + applyToRoots((el) => { + if (context.activeHidden && !context.overrideActive) { + el.dataset.moderationHidden = "true"; + const reason = context.effectiveHideReason || context.hideReason; + if (reason) { + el.dataset.moderationHideReason = reason; + } else if (el.dataset.moderationHideReason) { + delete el.dataset.moderationHideReason; + } + + const counts = context.hideCounts || context.effectiveHideCounts || null; + const muteCount = counts && Number.isFinite(counts.trustedMuteCount) + ? Math.max(0, Number(counts.trustedMuteCount)) + : null; + const reportCountHide = counts && Number.isFinite(counts.trustedReportCount) + ? Math.max(0, Number(counts.trustedReportCount)) + : null; + + if (muteCount !== null) { + el.dataset.moderationHideTrustedMuteCount = String(muteCount); + } else if (el.dataset.moderationHideTrustedMuteCount) { + delete el.dataset.moderationHideTrustedMuteCount; + } + + if (reportCountHide !== null) { + el.dataset.moderationHideTrustedReportCount = String(reportCountHide); + } else if (el.dataset.moderationHideTrustedReportCount) { + delete el.dataset.moderationHideTrustedReportCount; + } + } else { + if (el.dataset.moderationHidden) { + delete el.dataset.moderationHidden; + } + if (el.dataset.moderationHideReason) { + delete el.dataset.moderationHideReason; + } + if (el.dataset.moderationHideTrustedMuteCount) { + delete el.dataset.moderationHideTrustedMuteCount; + } + if (el.dataset.moderationHideTrustedReportCount) { + delete el.dataset.moderationHideTrustedReportCount; + } + } + }); +} diff --git a/tests/moderation/video-card.test.mjs b/tests/moderation/video-card.test.mjs index e565f15c..e8172b08 100644 --- a/tests/moderation/video-card.test.mjs +++ b/tests/moderation/video-card.test.mjs @@ -9,6 +9,10 @@ import { withMockedNostrTools, createModerationAppHarness, } from "../helpers/moderation-test-helpers.mjs"; +import { + applyModerationContextDatasets, + normalizeVideoModerationContext, +} from "../../js/ui/moderationUiHelpers.js"; function setupDom(t) { const dom = new JSDOM("", { @@ -159,7 +163,9 @@ test("VideoCard renders moderation badges and respects viewer override", async ( card.moderationActionButton.click(); await new Promise((resolve) => setTimeout(resolve, 0)); - const contextAfterOverride = card.getModerationContext(); + const contextAfterOverride = normalizeVideoModerationContext( + card.video?.moderation, + ); assert.equal(contextAfterOverride.overrideActive, true); assert.equal(contextAfterOverride.activeBlockAutoplay, false); @@ -223,7 +229,9 @@ test( document.body.appendChild(card.getRoot()); - const contextBeforeOverride = card.getModerationContext(); + const contextBeforeOverride = normalizeVideoModerationContext( + card.video?.moderation, + ); assert.equal(contextBeforeOverride.activeHidden, true); assert.equal(contextBeforeOverride.effectiveHideReason, "trusted-report-hide"); assert.equal(contextBeforeOverride.allowOverride, true); @@ -243,7 +251,9 @@ test( card.moderationActionButton.click(); await new Promise((resolve) => setTimeout(resolve, 0)); - const contextAfterOverride = card.getModerationContext(); + const contextAfterOverride = normalizeVideoModerationContext( + card.video?.moderation, + ); assert.equal(contextAfterOverride.overrideActive, true); assert.equal(contextAfterOverride.activeHidden, false); assert.equal(contextAfterOverride.effectiveHideReason, "trusted-report-hide"); @@ -305,7 +315,10 @@ test("VideoCard blurs thumbnails when trusted mute triggers without reports", as assert.equal(video.moderation.blurThumbnail, true); assert.equal(video.moderation.blurReason, "trusted-mute"); - assert.equal(card.getModerationContext().activeBlur, true); + assert.equal( + normalizeVideoModerationContext(card.video?.moderation).activeBlur, + true, + ); assert.equal(card.thumbnailEl.dataset.thumbnailState, "blurred"); assert.equal(card.authorPicEl.dataset.visualState, "blurred"); assert.equal(card.getRoot().dataset.autoplayPolicy, "blocked"); @@ -364,7 +377,10 @@ test("VideoCard hide action restores trusted mute hide state after override", as document.body.appendChild(card.getRoot()); - assert.equal(card.getModerationContext().activeHidden, true); + assert.equal( + normalizeVideoModerationContext(card.video?.moderation).activeHidden, + true, + ); assert.equal(card.getRoot().dataset.moderationHidden, "true"); card.onModerationOverride = ({ video: overrideVideo, card: overrideCard }) => @@ -373,7 +389,9 @@ test("VideoCard hide action restores trusted mute hide state after override", as card.moderationActionButton.click(); await new Promise((resolve) => setTimeout(resolve, 0)); - const contextAfterOverride = card.getModerationContext(); + const contextAfterOverride = normalizeVideoModerationContext( + card.video?.moderation, + ); assert.equal(contextAfterOverride.overrideActive, true); assert.equal(contextAfterOverride.activeHidden, false); assert.ok(card.moderationHideButton); @@ -384,9 +402,54 @@ test("VideoCard hide action restores trusted mute hide state after override", as card.moderationHideButton.click(); await new Promise((resolve) => setTimeout(resolve, 0)); - const contextAfterHide = card.getModerationContext(); + const contextAfterHide = normalizeVideoModerationContext(card.video?.moderation); assert.equal(contextAfterHide.overrideActive, false); assert.equal(contextAfterHide.activeHidden, true); assert.equal(card.getRoot().dataset.moderationHidden, "true"); assert.equal(card.moderationHideButton, null); }); + +test("applyModerationContextDatasets clears blur when overrides are active", (t) => { + const { document } = setupDom(t); + + const root = document.createElement("div"); + const thumbnail = document.createElement("div"); + const avatar = document.createElement("div"); + + const baselineContext = normalizeVideoModerationContext({ + blurThumbnail: true, + reportType: "nudity", + trustedCount: 1, + original: { blockAutoplay: true }, + }); + + applyModerationContextDatasets(baselineContext, { + root, + thumbnail, + avatar, + }); + + assert.equal(root.dataset.autoplayPolicy, "blocked"); + assert.equal(root.dataset.moderationOverride, undefined); + assert.equal(thumbnail.dataset.thumbnailState, "blurred"); + assert.equal(avatar.dataset.visualState, "blurred"); + + const overrideContext = normalizeVideoModerationContext({ + blurThumbnail: true, + reportType: "nudity", + trustedCount: 1, + viewerOverride: { showAnyway: true }, + original: { blockAutoplay: true }, + }); + + applyModerationContextDatasets(overrideContext, { + root, + thumbnail, + avatar, + }); + + assert.equal(root.dataset.autoplayPolicy, undefined); + assert.equal(root.dataset.moderationOverride, "show-anyway"); + assert.equal(thumbnail.dataset.thumbnailState, undefined); + assert.equal(avatar.dataset.visualState, undefined); +});