mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Merge branch 'unstable' into fix-moderation-test-visibility-6633763958860183568
This commit is contained in:
7 files changed
+130
-35
No files matched your search
@@ -0,0 +1,66 @@
|
||||
import { profileCache } from "../js/state/profileCache.js";
|
||||
|
||||
// Polyfill localStorage
|
||||
const store = new Map();
|
||||
global.localStorage = {
|
||||
getItem: (key) => store.get(key) || null,
|
||||
setItem: (key, value) => store.set(key, value),
|
||||
removeItem: (key) => store.delete(key),
|
||||
clear: () => store.clear(),
|
||||
};
|
||||
|
||||
// Polyfill requestIdleCallback
|
||||
global.requestIdleCallback = (cb) => setTimeout(() => {
|
||||
cb({
|
||||
timeRemaining: () => 50,
|
||||
didTimeout: false
|
||||
});
|
||||
}, 0);
|
||||
|
||||
// Polyfill window (might be needed by logger or others)
|
||||
if (typeof window === 'undefined') {
|
||||
global.window = global;
|
||||
}
|
||||
|
||||
function generateLargeData() {
|
||||
const data = [];
|
||||
// Generate roughly 2MB of JSON data
|
||||
for (let i = 0; i < 20000; i++) {
|
||||
data.push({ id: i, content: "x".repeat(100) });
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
async function runBenchmark() {
|
||||
const pubkey = "0".repeat(64);
|
||||
const section = "watchHistory";
|
||||
const data = generateLargeData();
|
||||
|
||||
// Set active profile to ensure logic runs
|
||||
profileCache.setActiveProfile(pubkey);
|
||||
|
||||
console.log("Benchmarking setProfileData with large payload...");
|
||||
|
||||
// Warmup
|
||||
profileCache.setProfileData(pubkey, section, [{id: 1}]);
|
||||
|
||||
const start = performance.now();
|
||||
// We call it multiple times to amplify the effect, but a single call with 2MB is enough to show blocking
|
||||
profileCache.setProfileData(pubkey, section, data);
|
||||
const end = performance.now();
|
||||
|
||||
console.log(`Execution time (Synchronous part): ${(end - start).toFixed(4)} ms`);
|
||||
|
||||
// Wait for idle callback
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
// Verify data is "stored"
|
||||
const stored = localStorage.getItem(profileCache.getStorageKey(pubkey, section));
|
||||
if (stored) {
|
||||
console.log("Data successfully stored in localStorage (length: " + stored.length + ")");
|
||||
} else {
|
||||
console.log("Data NOT found in localStorage");
|
||||
}
|
||||
}
|
||||
|
||||
runBenchmark();
|
||||
@@ -75,7 +75,8 @@
|
||||
id: "1".repeat(64),
|
||||
title: "Fixture: Blurred Preview",
|
||||
pubkey: "a".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
url: "https://example.com/dummy.mp4"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
@@ -103,7 +104,8 @@
|
||||
id: "2".repeat(64),
|
||||
title: "Fixture: Autoplay Blocked",
|
||||
pubkey: "e".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
url: "https://example.com/dummy.mp4"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
@@ -127,7 +129,8 @@
|
||||
id: "3".repeat(64),
|
||||
title: "Fixture: Override Persistence",
|
||||
pubkey: "f".repeat(64),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
url: "https://example.com/dummy.mp4"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
@@ -152,7 +155,8 @@
|
||||
id: "4".repeat(64),
|
||||
title: "Fixture: Trusted Report Hide",
|
||||
pubkey: "aa".repeat(32),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
url: "https://example.com/dummy.mp4"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: true,
|
||||
@@ -177,7 +181,8 @@
|
||||
id: "5".repeat(64),
|
||||
title: "Fixture: Trusted Mute Hide",
|
||||
pubkey: "bb".repeat(32),
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg"
|
||||
thumbnail: "/assets/jpg/video-thumbnail-fallback.jpg",
|
||||
url: "https://example.com/dummy.mp4"
|
||||
},
|
||||
moderation: {
|
||||
blockAutoplay: false,
|
||||
@@ -607,6 +612,12 @@
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
card.onModerationHide = () => {
|
||||
overrides = setOverride(overrides, video.id, { showAnyway: false });
|
||||
refreshModeration();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
const root = card.getRoot();
|
||||
|
||||
@@ -16,6 +16,15 @@ const SECTION_TO_NOTE_TYPE = {
|
||||
"profile": NOTE_TYPES.PROFILE_METADATA,
|
||||
};
|
||||
|
||||
// Helper to defer non-critical work to idle periods
|
||||
function runIdle(callback) {
|
||||
if (typeof requestIdleCallback !== "undefined") {
|
||||
requestIdleCallback(callback);
|
||||
} else {
|
||||
setTimeout(callback, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeProfileString(value) {
|
||||
if (typeof value !== "string") {
|
||||
return "";
|
||||
@@ -316,17 +325,19 @@ class ProfileCache {
|
||||
const storageKey = this.getStorageKey(pubkey, section);
|
||||
const tier = this.getStorageTier(section);
|
||||
|
||||
// Save to storage
|
||||
// Save to storage (Optimized: defer blocking I/O)
|
||||
if (tier === STORAGE_TIERS.LOCAL_STORAGE && typeof localStorage !== "undefined") {
|
||||
try {
|
||||
if (data === null || data === undefined) {
|
||||
localStorage.removeItem(storageKey);
|
||||
} else {
|
||||
localStorage.setItem(storageKey, JSON.stringify(data));
|
||||
runIdle(() => {
|
||||
try {
|
||||
if (data === null || data === undefined) {
|
||||
localStorage.removeItem(storageKey);
|
||||
} else {
|
||||
localStorage.setItem(storageKey, JSON.stringify(data));
|
||||
}
|
||||
} catch (error) {
|
||||
userLogger.warn(`[ProfileCache] Failed to save ${section} for ${pubkey}`, error);
|
||||
}
|
||||
} catch (error) {
|
||||
userLogger.warn(`[ProfileCache] Failed to save ${section} for ${pubkey}`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update memory cache
|
||||
|
||||
@@ -1856,9 +1856,9 @@ export class VideoCard {
|
||||
target.hidden = false;
|
||||
target.removeAttribute("hidden");
|
||||
target.removeAttribute("aria-hidden");
|
||||
// If target is summary container, ensure it's displayed as block
|
||||
// If target is summary container, ensure it's displayed as flex (to match bv-stack)
|
||||
if (target !== slot) {
|
||||
target.style.setProperty("display", "block", "important");
|
||||
target.style.setProperty("display", "flex", "important");
|
||||
} else {
|
||||
target.style.removeProperty("display");
|
||||
}
|
||||
@@ -2009,7 +2009,7 @@ export class VideoCard {
|
||||
|
||||
if (!this.hiddenSummaryEl) {
|
||||
this.hiddenSummaryEl = this.createElement("div", {
|
||||
classNames: ["p-md", "bv-stack", "bv-stack--tight"],
|
||||
classNames: ["p-md", "bv-stack", "bv-stack--tight", "items-start"],
|
||||
});
|
||||
this.hiddenSummaryEl.dataset.moderationHiddenContainer = "true";
|
||||
this.hiddenSummaryEl.setAttribute("role", "group");
|
||||
|
||||
@@ -79,7 +79,7 @@ test("ProfileCache: resolveAddressKey", (t) => {
|
||||
assert.equal(keyUnknown, `bitvid:profile:${pubkey}:unknownSection:v1`);
|
||||
});
|
||||
|
||||
test("ProfileCache: set and get (memory and persistence)", (t) => {
|
||||
test("ProfileCache: set and get (memory and persistence)", async (t) => {
|
||||
const pubkey = "c".repeat(64);
|
||||
profileCache.setActiveProfile(pubkey);
|
||||
|
||||
@@ -98,6 +98,10 @@ test("ProfileCache: set and get (memory and persistence)", (t) => {
|
||||
|
||||
// Check persistence
|
||||
const storageKey = profileCache.getStorageKey(pubkey, section);
|
||||
|
||||
// Wait for async write (runIdle/setTimeout)
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
|
||||
const storedRaw = localStorage.getItem(storageKey);
|
||||
assert.ok(storedRaw);
|
||||
assert.deepEqual(JSON.parse(storedRaw), data);
|
||||
@@ -110,7 +114,7 @@ test("ProfileCache: set and get (memory and persistence)", (t) => {
|
||||
assert.deepEqual(reloaded, data);
|
||||
});
|
||||
|
||||
test("ProfileCache: setProfile normalization and storage", (t) => {
|
||||
test("ProfileCache: setProfile normalization and storage", async (t) => {
|
||||
const pubkey = "d".repeat(64);
|
||||
const profileInput = {
|
||||
name: " Test User ",
|
||||
@@ -131,6 +135,10 @@ test("ProfileCache: setProfile normalization and storage", (t) => {
|
||||
|
||||
// Verify persistence
|
||||
const storageKey = profileCache.getStorageKey(pubkey, "profile");
|
||||
|
||||
// Wait for async write (runIdle/setTimeout)
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
|
||||
const stored = JSON.parse(localStorage.getItem(storageKey));
|
||||
assert.deepEqual(stored.profile, entry.profile);
|
||||
});
|
||||
|
||||
@@ -44,6 +44,16 @@ if (typeof globalThis.window.localStorage === "undefined") {
|
||||
globalThis.window.localStorage = globalThis.localStorage;
|
||||
}
|
||||
|
||||
// Polyfill requestIdleCallback for synchronous testing if undefined
|
||||
if (typeof globalThis.requestIdleCallback === "undefined") {
|
||||
globalThis.requestIdleCallback = (cb) => cb();
|
||||
globalThis.cancelIdleCallback = () => {};
|
||||
if (globalThis.window) {
|
||||
globalThis.window.requestIdleCallback = globalThis.requestIdleCallback;
|
||||
globalThis.window.cancelIdleCallback = globalThis.cancelIdleCallback;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof globalThis.window.crypto === "undefined" && typeof globalThis.crypto !== "undefined") {
|
||||
globalThis.window.crypto = globalThis.crypto;
|
||||
}
|
||||
|
||||
@@ -89,14 +89,11 @@ test.describe("moderation fixtures", () => {
|
||||
|
||||
await expect(overrideCard).toHaveAttribute("data-autoplay-policy", "blocked");
|
||||
await expect(thumbnail).toHaveAttribute("data-thumbnail-state", "blurred");
|
||||
// TODO: Investigate why visibility check fails in fixture environment (headless)
|
||||
// await expect(showAnywayButton).toBeVisible();
|
||||
await expect(showAnywayButton).toBeVisible();
|
||||
await expect(restoreButtonQuery).toHaveCount(0);
|
||||
|
||||
// TODO: Investigate why click times out in fixture environment (headless)
|
||||
// await showAnywayButton.click({ force: true });
|
||||
await showAnywayButton.click();
|
||||
|
||||
/*
|
||||
await expect(overrideCard).toHaveAttribute(
|
||||
"data-moderation-override",
|
||||
"show-anyway"
|
||||
@@ -143,7 +140,6 @@ test.describe("moderation fixtures", () => {
|
||||
await expect(
|
||||
reloadedCard.getByRole("button", { name: "Show anyway" })
|
||||
).toBeVisible();
|
||||
*/
|
||||
});
|
||||
|
||||
test("trusted report hide fixture supports show anyway override", async ({ page }) => {
|
||||
@@ -166,14 +162,11 @@ test.describe("moderation fixtures", () => {
|
||||
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");
|
||||
// TODO: Investigate visibility check failure (fails with 0x0 size in headless)
|
||||
// await expect(showAnywayButton).toBeVisible();
|
||||
await expect(showAnywayButton).toBeVisible();
|
||||
await expect(restoreButtonQuery).toHaveCount(0);
|
||||
|
||||
// TODO: Investigate click timeout failure
|
||||
// await showAnywayButton.click({ force: true });
|
||||
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(
|
||||
@@ -190,7 +183,6 @@ test.describe("moderation fixtures", () => {
|
||||
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 ({
|
||||
@@ -227,10 +219,8 @@ test.describe("moderation fixtures", () => {
|
||||
await expect(showAnywayLocator).toHaveAttribute("aria-label", "Show anyway");
|
||||
await expect(restoreButtonQuery).toHaveCount(0);
|
||||
|
||||
// TODO: Investigate click timeout failure
|
||||
// await showAnywayButton.click({ force: true });
|
||||
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(
|
||||
@@ -249,6 +239,5 @@ test.describe("moderation fixtures", () => {
|
||||
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();
|
||||
*/
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user