This commit is contained in:
thePR0M3TH3AN committed 2026-02-22 19:21:28 -05:00
1 parent 8adaee87db
commit ee8569a99b
2 files changed
+524

No files matched your search

+305
View File
@@ -0,0 +1,305 @@
import { expect, test } from "./helpers/instrumentedTest";
test.describe("runtime UI utility coverage", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/docs/kitchen-sink.html?__test__=1", {
waitUntil: "networkidle",
});
});
test("exercises dynamic style scope registration and lifecycle", async ({ page }) => {
const result = await page.evaluate(async () => {
const {
registerScope,
setVariables,
releaseScope,
getScopeAttributeName,
} = await import("/js/designSystem/dynamicStyles.js");
const firstScope = registerScope("coverage-scope", [":scope", "& .title", "> .child"]);
const secondScope = registerScope("coverage-scope", [":scope"]);
const firstUpdate = setVariables(firstScope, {
"--card-gap": "12px",
"--muted": "",
invalid: "skip",
});
const secondUpdate = setVariables(firstScope, {
"--card-gap": 8,
});
const missingScopeUpdate = setVariables("missing-scope", { "--x": "1" });
const releaseMissing = releaseScope("missing-scope");
const releaseFirst = releaseScope(firstScope);
const releaseSecond = releaseScope(secondScope);
return {
firstScope,
secondScope,
firstUpdate,
secondUpdate,
missingScopeUpdate,
releaseMissing,
releaseFirst,
releaseSecond,
scopeAttr: getScopeAttributeName(),
styleTagCount: document.querySelectorAll("style[data-ds-dynamic='true']").length,
adoptedSheetCount: Array.isArray((document as any).adoptedStyleSheets)
? (document as any).adoptedStyleSheets.length
: 0,
};
});
expect(result.firstScope).toBe("coverage-scope");
expect(result.secondScope).toBe("coverage-scope-1");
expect(result.firstUpdate).toBe(true);
expect(result.secondUpdate).toBe(true);
expect(result.missingScopeUpdate).toBe(false);
expect(result.releaseMissing).toBe(false);
expect(result.releaseFirst).toBe(true);
expect(result.releaseSecond).toBe(true);
expect(result.scopeAttr).toBe("data-ds-style-id");
expect(result.styleTagCount + result.adoptedSheetCount).toBeGreaterThanOrEqual(1);
});
test("exercises tag preference menu rendering, actions, and state transitions", async ({
page,
}) => {
const result = await page.evaluate(async () => {
const {
createTagPreferenceMenu,
applyTagPreferenceMenuState,
TAG_PREFERENCE_ACTIONS,
} = await import("/js/ui/components/tagPreferenceMenu.js");
const actionEvents: Array<{ action: string; tag: string }> = [];
const loggedOut = createTagPreferenceMenu({
tag: "#BitVid",
isLoggedIn: false,
});
document.body.appendChild(loggedOut!.panel);
const loggedIn = createTagPreferenceMenu({
tag: " nostr ",
isLoggedIn: true,
membership: { state: "interest" },
onAction: (action: string, detail: any) => {
actionEvents.push({ action, tag: detail.normalizedTag });
},
});
document.body.appendChild(loggedIn!.panel);
loggedIn!.buttons.removeInterest.click();
loggedIn!.buttons.addDisinterest.click();
applyTagPreferenceMenuState({
buttons: loggedIn!.buttons,
membership: { state: "disinterest", interest: true, disinterest: true },
isLoggedIn: true,
});
return {
actions: TAG_PREFERENCE_ACTIONS,
loggedOutTag: loggedOut!.panel.dataset.tag,
loggedOutMessage: loggedOut!.panel.textContent,
loggedOutDisabled: {
addInterest: loggedOut!.buttons.addInterest.disabled,
removeInterest: loggedOut!.buttons.removeInterest.disabled,
addDisinterest: loggedOut!.buttons.addDisinterest.disabled,
removeDisinterest: loggedOut!.buttons.removeDisinterest.disabled,
},
loggedInTag: loggedIn!.panel.dataset.tag,
loggedInDisabledAfterTransition: {
addInterest: loggedIn!.buttons.addInterest.disabled,
removeInterest: loggedIn!.buttons.removeInterest.disabled,
addDisinterest: loggedIn!.buttons.addDisinterest.disabled,
removeDisinterest: loggedIn!.buttons.removeDisinterest.disabled,
},
actionEvents,
};
});
expect(result.actions.ADD_INTEREST).toBe("add-interest");
expect(result.loggedOutTag).toBe("bitvid");
expect(result.loggedInTag).toBe("nostr");
expect(result.loggedOutMessage).toContain("Sign in to personalize");
expect(result.loggedOutDisabled).toEqual({
addInterest: true,
removeInterest: true,
addDisinterest: true,
removeDisinterest: true,
});
expect(result.actionEvents.map((entry: any) => entry.action)).toEqual([
"remove-interest",
"add-disinterest",
]);
expect(result.actionEvents.every((entry: any) => entry.tag === "nostr")).toBe(true);
expect(result.loggedInDisabledAfterTransition).toEqual({
addInterest: false,
removeInterest: true,
addDisinterest: true,
removeDisinterest: false,
});
});
test("exercises docs view initialization, hash routing, drawer toggle, and error handling", async ({
page,
}) => {
const result = await page.evaluate(async () => {
document.body.innerHTML = `
<button data-docs-toc-toggle type="button" aria-expanded="false">Menu</button>
<nav id="docsTocList"></nav>
<div id="docsTocDrawer" class="hidden" data-open="false">
<div class="bv-modal-backdrop"></div>
<div data-docs-toc-panel tabindex="-1">
<nav id="docsTocDrawerNav"></nav>
</div>
</div>
<article id="markdown-container"></article>
`;
(window as any).marked = {
parse(markdown: string) {
if (markdown.includes("SECOND_DOC")) {
return '<h2 id="advanced">Advanced</h2><p>Second doc</p><a data-docs-toc-item href="#advanced">Advanced</a>';
}
return '<h2 id="intro">Intro</h2><pre><code>const x = 1;</code></pre><a data-docs-toc-item href="#intro">Intro</a>';
},
};
const highlighted: string[] = [];
(window as any).hljs = {
highlightElement(el: Element) {
highlighted.push(el.textContent || "");
},
};
const realFetch = window.fetch.bind(window);
const fetchLog: string[] = [];
window.fetch = (async (input: RequestInfo | URL) => {
const url = String(input);
fetchLog.push(url);
if (url.endsWith("content/docs/toc.json")) {
return new Response(
JSON.stringify({
items: [
{
slug: "intro",
title: "Intro",
path: "/content/docs/intro.md",
children: [
{
slug: "intro-nested",
title: "Nested",
path: "/content/docs/nested.md",
},
],
},
{
slug: "advanced",
title: "Advanced",
path: "/content/docs/advanced.md",
},
],
}),
{ status: 200, headers: { "Content-Type": "application/json" } },
);
}
if (url.endsWith("/content/docs/intro.md")) {
return new Response("INTRO_DOC", { status: 200 });
}
if (url.endsWith("/content/docs/advanced.md")) {
return new Response("SECOND_DOC", { status: 200 });
}
return realFetch(input);
}) as typeof fetch;
const { initDocsView } = await import("/js/docsView.js");
await initDocsView();
const initialTitle = document.title;
const initialHash = window.location.hash;
const topLevelLinks = Array.from(document.querySelectorAll("#docsTocList [data-docs-toc-item]")).map(
(el) => (el as HTMLElement).dataset.slug,
);
const drawerLinks = Array.from(document.querySelectorAll("#docsTocDrawerNav [data-docs-toc-item]")).map(
(el) => (el as HTMLElement).dataset.slug,
);
const toggle = document.querySelector("[data-docs-toc-toggle]") as HTMLButtonElement;
const drawer = document.getElementById("docsTocDrawer") as HTMLElement;
const backdrop = drawer.querySelector(".bv-modal-backdrop") as HTMLElement;
toggle.click();
await new Promise((resolve) => setTimeout(resolve, 0));
const drawerOpen = drawer.getAttribute("data-open");
const toggleExpandedOpen = toggle.getAttribute("aria-expanded");
backdrop.click();
await new Promise((resolve) => setTimeout(resolve, 0));
const drawerClosed = drawer.getAttribute("data-open");
window.location.hash = "#view=docs&doc=advanced";
window.dispatchEvent(new HashChangeEvent("hashchange"));
await new Promise((resolve) => setTimeout(resolve, 0));
await new Promise((resolve) => setTimeout(resolve, 0));
const advancedTitle = document.title;
const markdownHtml =
document.getElementById("markdown-container")?.innerHTML || "";
window.fetch = (async (input: RequestInfo | URL) => {
const url = String(input);
if (url.endsWith("content/docs/toc.json")) {
return new Response("broken", { status: 500 });
}
return realFetch(input);
}) as typeof fetch;
document.getElementById("docsTocList")!.innerHTML = "";
document.getElementById("markdown-container")!.innerHTML = "";
await initDocsView();
const tocErrorText = document.getElementById("docsTocList")?.textContent || "";
const markdownErrorText =
document.getElementById("markdown-container")?.textContent || "";
window.fetch = realFetch;
return {
initialTitle,
initialHash,
advancedTitle,
markdownHtml,
topLevelLinks,
drawerLinks,
drawerOpen,
drawerClosed,
toggleExpandedOpen,
fetchLog,
highlighted,
tocErrorText,
markdownErrorText,
};
});
expect(result.initialTitle).toBe("Intro | bitvid docs");
expect(result.initialHash).toContain("view=docs&doc=intro");
expect(result.advancedTitle).toBe("Advanced | bitvid docs");
expect(result.markdownHtml).toContain("Second doc");
expect(result.topLevelLinks).toContain("intro");
expect(result.topLevelLinks).toContain("advanced");
expect(result.drawerLinks).toContain("intro");
expect(result.drawerOpen).toBe("true");
expect(result.drawerClosed).toBe("false");
expect(result.toggleExpandedOpen).toBe("true");
expect(result.fetchLog.some((url: string) => url.endsWith("content/docs/toc.json"))).toBe(true);
expect(result.fetchLog.some((url: string) => url.endsWith("/content/docs/intro.md"))).toBe(true);
expect(result.fetchLog.some((url: string) => url.endsWith("/content/docs/advanced.md"))).toBe(true);
expect(result.highlighted.length).toBeGreaterThan(0);
expect(result.tocErrorText).toContain("Unable to load docs table of contents");
expect(result.markdownErrorText).toContain("Error loading content");
});
});
+219
View File
@@ -0,0 +1,219 @@
import { expect, test } from "./helpers/instrumentedTest";
test.describe("runtime utility module coverage", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/docs/kitchen-sink.html?__test__=1", {
waitUntil: "networkidle",
});
});
test("exercises search filter parsing and serialization", async ({ page }) => {
const result = await page.evaluate(async () => {
const { parseFilterQuery, serializeFiltersToQuery } = await import(
"/js/search/searchFilters.js"
);
const parsed = parseFilterQuery(
'author:abc123 tag:#music,nostr kind:30078 relay:wss://relay.example after:2024-01-02 before:1707000000 duration:>=1.5m has:magnet nsfw:safe "exact phrase" looseTerm',
);
const withErrors = parseFilterQuery(
"duration:oops has:bad unknown:value before:not-a-date",
);
const serialized = serializeFiltersToQuery(parsed.filters);
return {
parsed,
withErrors,
serialized,
};
});
expect(result.parsed.filters.authorPubkeys).toEqual(["abc123"]);
expect(result.parsed.filters.tags).toEqual(["music", "nostr"]);
expect(result.parsed.filters.kind).toBe(30078);
expect(result.parsed.filters.relay).toBe("wss://relay.example");
expect(result.parsed.filters.duration.minSeconds).toBe(90);
expect(result.parsed.filters.hasMagnet).toBe(true);
expect(result.parsed.filters.nsfw).toBe("false");
expect(result.parsed.text).toContain("exact phrase");
expect(result.parsed.text).toContain("looseTerm");
expect(result.withErrors.errors.length).toBeGreaterThanOrEqual(3);
expect(result.withErrors.errors.some((e: any) => /Duration/.test(e.message))).toBe(true);
expect(result.withErrors.errors.some((e: any) => /Has filter/.test(e.message))).toBe(true);
expect(result.withErrors.errors.some((e: any) => /Date value/.test(e.message))).toBe(true);
expect(result.serialized).toContain("author:abc123");
expect(result.serialized).toContain("tag:music");
expect(result.serialized).toContain("kind:30078");
expect(result.serialized).toContain("has:magnet");
});
test("exercises video note payload normalization and error mapping", async ({ page }) => {
const result = await page.evaluate(async () => {
const {
normalizeVideoNotePayload,
VIDEO_NOTE_ERROR_CODES,
getVideoNoteErrorMessage,
} = await import("/js/services/videoNotePayload.js");
const invalid = normalizeVideoNotePayload({
title: "",
url: "http://insecure.example/video.mp4",
});
const normalized = normalizeVideoNotePayload({
legacyFormData: {
title: " Coverage Video ",
url: "https://cdn.example/video.mp4 ",
magnet:
"magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567",
ws: "https://webseed.example/video.mp4",
xs: "https://cdn.example/video.torrent",
mode: "DEV",
isPrivate: true,
isNsfw: true,
isForKids: true,
storageProvider: "r2",
fileSha256:
"ABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCD",
originalFileSha256:
"00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF",
},
nip71: {
summary: " summary text ",
publishedAt: "2024-05-01T12:00:00Z",
duration: "120",
hashtags: ["nostr", " video "],
references: [" https://example.com/ref "],
textTracks: [
{
url: " https://example.com/captions.vtt ",
type: " subtitles ",
language: " en ",
},
],
segments: [{ start: "0", end: "30", title: " Intro " }],
participants: [
{
pubkey:
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
relay: " wss://relay.example ",
},
],
imeta: [
{
m: " VIDEO/MP4 ",
url: "https://cdn.example/video.mp4",
image: [" https://cdn.example/thumb.jpg "],
duration: "98",
},
],
},
});
return {
invalid,
normalized,
invalidUrlCode: VIDEO_NOTE_ERROR_CODES.INVALID_URL_PROTOCOL,
invalidUrlMessage: getVideoNoteErrorMessage(
VIDEO_NOTE_ERROR_CODES.INVALID_URL_PROTOCOL,
),
fallbackMessage: getVideoNoteErrorMessage("unknown"),
};
});
expect(result.invalid.errors).toContain(result.invalidUrlCode);
expect(result.invalid.errors).toContain("missing_title");
expect(result.invalidUrlMessage).toMatch(/HTTPS/i);
expect(result.fallbackMessage).toMatch(/Unable to submit video/i);
const legacy = result.normalized.payload.legacyFormData;
expect(legacy.title).toBe("Coverage Video");
expect(legacy.mode).toBe("dev");
expect(legacy.isPrivate).toBe(true);
expect(legacy.isNsfw).toBe(true);
expect(legacy.isForKids).toBe(false);
expect(legacy.storagePointer).toContain("r2:");
expect(legacy.fileSha256).toHaveLength(64);
expect(legacy.originalFileSha256).toHaveLength(64);
expect(legacy.magnet).toContain("magnet:?xt=urn:btih:");
expect(legacy.ws).toContain("https://webseed.example/video.mp4");
expect(legacy.xs).toContain("https://cdn.example/video.torrent");
const nip71 = result.normalized.payload.nip71;
expect(nip71.summary).toBe("summary text");
expect(nip71.duration).toBe(120);
expect(nip71.imeta[0].m).toBe("video/mp4");
expect(nip71.hashtags).toEqual(["nostr", "video"]);
expect(nip71.segments[0].start).toBe(0);
expect(nip71.participants[0].relay).toBe("wss://relay.example");
});
test("exercises attachment tag parsing and attachment descriptions", async ({
page,
}) => {
const result = await page.evaluate(async () => {
const {
ATTACHMENT_KIND,
parseAttachmentTags,
extractAttachmentsFromMessage,
formatAttachmentSize,
describeAttachment,
} = await import("/js/attachments/attachmentUtils.js");
const parsed = parseAttachmentTags([
["x", "A".repeat(64)],
["url", "https://cdn.example/file.bin"],
["name", "manual.pdf"],
["type", "application/pdf"],
["size", "2048"],
["k", "secret"],
]);
const noSource = parseAttachmentTags([["name", "nameless"]]);
const extracted = extractAttachmentsFromMessage({
message: {
kind: ATTACHMENT_KIND,
tags: [["x", "b".repeat(64)], ["url", "https://cdn.example/second.bin"]],
},
});
const skipped = extractAttachmentsFromMessage({
event: { kind: 1, tags: [["x", "c".repeat(64)]] },
});
return {
parsed,
noSource,
extractedLength: extracted.length,
extractedHash: extracted[0]?.x,
skippedLength: skipped.length,
sizeBytes: formatAttachmentSize(999),
sizeKilobytes: formatAttachmentSize(2048),
sizeMegabytes: formatAttachmentSize(1048576),
describedName: describeAttachment(parsed),
describedType: describeAttachment({ type: "image/png" }),
describedFallback: describeAttachment(null),
};
});
expect(result.parsed.x).toBe("a".repeat(64));
expect(result.parsed.url).toBe("https://cdn.example/file.bin");
expect(result.parsed.size).toBe(2048);
expect(result.parsed.encrypted).toBe(true);
expect(result.noSource).toBeNull();
expect(result.extractedLength).toBe(1);
expect(result.extractedHash).toBe("b".repeat(64));
expect(result.skippedLength).toBe(0);
expect(result.sizeBytes).toBe("999 B");
expect(result.sizeKilobytes).toBe("2.0 KB");
expect(result.sizeMegabytes).toBe("1.0 MB");
expect(result.describedName).toBe("Attachment: manual.pdf");
expect(result.describedType).toBe("Attachment (image/png)");
expect(result.describedFallback).toBe("Attachment");
});
});