mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-07 13:22:54 +00:00
Merge branch 'unstable' into chore/ai/dependency-scan-report-750138199951940544
This commit is contained in:
12 files changed
+919
-384
No files matched your search
@@ -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,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");
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in new issue
Block a user