Merge branch 'unstable' into chore/ai/dependency-scan-report-750138199951940544

This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-01-29 08:19:27 -05:00
commit 386719652d
12 files changed
+919 -384

No files matched your search

+12 -4
View File
@@ -312,6 +312,8 @@ test("NIP-07 login falls back when structured permissions fail", async () => {
const result = await nip07Provider.login({ nostrClient });
const pubkey = result.pubkey;
assert.equal(pubkey, HEX_PUBKEY);
// With Structured -> String -> Plain order, this succeeds at step 2 (String).
// So we expect 2 calls.
assert.ok(env.enableCalls.length >= 2, "should retry with alternate payloads");
const [objectCall, stringCall, plainCall] = env.enableCalls;
assert.ok(Array.isArray(objectCall?.permissions));
@@ -353,14 +355,18 @@ test("NIP-07 login supports extensions that only allow enable() without payload"
const result = await nip07Provider.login({ nostrClient });
const pubkey = result.pubkey;
assert.equal(pubkey, HEX_PUBKEY);
// With Structured -> String -> Plain order, we expect 3 calls:
// 1. Structured (rejected)
// 2. String (rejected)
// 3. Plain (accepted)
assert.equal(
env.enableCalls.length,
3,
"should fall back to plain enable() after explicit variants fail",
"should eventually succeed with plain enable()",
);
const [objectCall, stringCall, plainCall] = env.enableCalls;
assert.ok(Array.isArray(objectCall?.permissions));
assert.ok(Array.isArray(stringCall?.permissions));
assert.ok(objectCall !== undefined);
assert.ok(stringCall !== undefined);
assert.equal(plainCall, undefined, "last attempt should use plain enable()");
} finally {
env.restore();
@@ -401,7 +407,9 @@ test("NIP-07 login quickly retries when a permission payload stalls", async () =
duration < 500,
`login should fall back quickly from stalled enable payloads (duration: ${duration}ms)`,
);
assert.equal(env.enableCalls.length, 2, "should attempt structured and string payload variants before success");
// Structured (stalls) -> String (resolves)
// Plain is skipped because String resolved.
assert.equal(env.enableCalls.length, 2, "should attempt structured (stalls) and then string payload");
const [objectCall, stringCall] = env.enableCalls;
assert.ok(Array.isArray(objectCall?.permissions));
assert.equal(typeof objectCall.permissions[0], "object");
@@ -0,0 +1,122 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import VideoSettingsMenuController from '../js/ui/videoSettingsMenuController.js';
test('VideoSettingsMenuController - requestMenu opens popover', async () => {
let popoverOpened = false;
const mockPopover = {
open: async () => { popoverOpened = true; },
close: () => {},
destroy: () => {},
isOpen: () => false,
};
const createPopover = () => mockPopover;
const createVideoSettingsMenuPanel = () => ({ querySelectorAll: () => [] });
const controller = new VideoSettingsMenuController({
createPopover,
createVideoSettingsMenuPanel,
});
const trigger = { ownerDocument: {} };
await controller.requestMenu({ trigger });
assert.equal(popoverOpened, true);
});
test('VideoSettingsMenuController - closeMenu closes popover', async () => {
let popoverClosed = false;
const mockPopover = {
open: async () => {},
close: () => { popoverClosed = true; return true; },
destroy: () => {},
isOpen: () => true,
};
const createPopover = () => mockPopover;
const createVideoSettingsMenuPanel = () => ({ querySelectorAll: () => [] });
const controller = new VideoSettingsMenuController({
createPopover,
createVideoSettingsMenuPanel,
});
const trigger = { ownerDocument: {} };
controller.requestMenu({ trigger }); // creates entry, calls close because isOpen is true
// reset closed flag because requestMenu closed it
popoverClosed = false;
controller.closeMenu({ trigger });
assert.equal(popoverClosed, true);
});
test('VideoSettingsMenuController - requestMenu toggles if open', async () => {
let popoverClosed = false;
const mockPopover = {
open: async () => {},
close: () => { popoverClosed = true; return true; },
destroy: () => {},
isOpen: () => true,
};
const createPopover = () => mockPopover;
const createVideoSettingsMenuPanel = () => ({ querySelectorAll: () => [] });
const controller = new VideoSettingsMenuController({
createPopover,
createVideoSettingsMenuPanel,
});
const trigger = { ownerDocument: {} };
controller.requestMenu({ trigger }); // closes because isOpen=true
popoverClosed = false;
controller.requestMenu({ trigger }); // closes again
assert.equal(popoverClosed, true);
});
test('VideoSettingsMenuController - closeAll closes all popovers', async () => {
let closeCount = 0;
const createPopover = () => {
let open = false;
return {
open: async () => { open = true; },
close: () => {
if (open) {
closeCount++;
open = false;
return true;
}
return false;
},
destroy: () => {},
isOpen: () => open,
};
};
const createVideoSettingsMenuPanel = () => ({ querySelectorAll: () => [] });
const controller = new VideoSettingsMenuController({
createPopover,
createVideoSettingsMenuPanel,
});
const trigger1 = { ownerDocument: {}, id: 1 };
const trigger2 = { ownerDocument: {}, id: 2 };
// These will open them because isOpen starts as false
controller.requestMenu({ trigger: trigger1 });
controller.requestMenu({ trigger: trigger2 });
// Wait a tick for async open
await Promise.resolve();
controller.closeAll();
assert.equal(closeCount, 2);
});
+7 -4
View File
@@ -7,12 +7,15 @@ test.describe("embed layout and styling", () => {
await page.goto("/embed.html?pointer=nevent123", { waitUntil: "domcontentloaded" });
// 1. Verify CSS Variable
const accentColor = await page.evaluate(() => {
return getComputedStyle(document.documentElement).getPropertyValue("--bitvid-accent").trim();
});
// The configured color is #540011 (from config/instance-config.js)
const expectedAccent = THEME_ACCENT_OVERRIDES?.light?.accent || "#ff6b6b";
expect(accentColor).toBe(expectedAccent);
// Wait for the accent color to be applied by js/embed.js
await expect.poll(async () => {
return page.evaluate(() => {
return getComputedStyle(document.documentElement).getPropertyValue("--bitvid-accent").trim();
});
}).toBe(expectedAccent);
// 2. Verify Video Element Styling
const video = page.locator("#embedVideo");
+5 -21
View File
@@ -10,26 +10,12 @@ declare global {
}
test.describe("overlay layering tokens", () => {
async function dismissDisclaimerModal(page: Page) {
const modal = page.locator("#disclaimerModal");
if ((await modal.count()) === 0) {
return;
}
test("mobile sidebar shares desktop rail behavior", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.evaluate(() => {
try {
window.localStorage?.setItem("hasSeenDisclaimer", "true");
} catch (error) {
console.warn("Failed to persist disclaimer state", error);
}
document
.querySelectorAll<HTMLElement>("#disclaimerModal")
.forEach((node) => {
node.classList.add("hidden");
node.setAttribute("data-open", "false");
});
document.documentElement?.classList.remove("modal-open");
document.body?.classList.remove("modal-open");
// Pre-suppress the disclaimer modal to avoid race conditions with app hydration
await page.addInitScript(() => {
window.localStorage.setItem("hasSeenDisclaimer", "true");
});
await page.waitForFunction(() =>
@@ -44,8 +30,6 @@ test.describe("overlay layering tokens", () => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/index.html", { waitUntil: "networkidle" });
await dismissDisclaimerModal(page);
// Wait for initial fade-in to complete so opacity doesn't interfere with visibility checks
await page.waitForFunction(
() => !document.getElementById("sidebar")?.classList.contains("fade-in")