65 lines
2.8 KiB
TypeScript
65 lines
2.8 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
import { GlobalRegistrator } from "@happy-dom/global-registrator";
|
|
import type { Song } from "../src/data/types";
|
|
|
|
beforeAll(() => GlobalRegistrator.register());
|
|
afterAll(() => GlobalRegistrator.unregister());
|
|
|
|
const base: Song = {
|
|
id: "x", title: "X", sagaId: "troy", track: 1,
|
|
storyTime: { yearsAfterTroy: 0, label: "t" }, odyssey: null,
|
|
parts: [{ name: "Verse 1", singers: ["A"], lines: ["l"], motifIds: [] }],
|
|
facts: ["f"], videos: [{ title: "v1", url: "https://example.com/1" }, { title: "v2", url: "https://example.com/2" }],
|
|
};
|
|
|
|
describe("credits", () => {
|
|
test("FAN_NOTICE has required phrases", async () => {
|
|
const { FAN_NOTICE } = await import("../src/shared/credits");
|
|
for (const p of ["unofficial", "not affiliated", "Jorge Rivera-Herrans"]) expect(FAN_NOTICE).toContain(p);
|
|
});
|
|
test("mountCredits puts the notice in document.body", async () => {
|
|
const { FAN_NOTICE, mountCredits } = await import("../src/shared/credits");
|
|
mountCredits();
|
|
expect(document.body.textContent).toContain(FAN_NOTICE);
|
|
});
|
|
test("mountCredits fills a [data-fan-credits] target", async () => {
|
|
const { FAN_NOTICE, mountCredits } = await import("../src/shared/credits");
|
|
document.body.innerHTML = '<footer data-fan-credits></footer>';
|
|
const el = mountCredits();
|
|
expect(el.hasAttribute("data-fan-credits")).toBe(true);
|
|
expect(document.querySelectorAll("[data-fan-credits]")).toHaveLength(1);
|
|
expect(el.textContent).toContain(FAN_NOTICE);
|
|
});
|
|
});
|
|
|
|
describe("createAudioSlot", () => {
|
|
test("createAudioSlot empty state", async () => {
|
|
const { createAudioSlot } = await import("../src/shared/audio-slot");
|
|
const el = createAudioSlot(base);
|
|
expect(el.classList.contains("audio-slot")).toBe(true);
|
|
expect(el.classList.contains("audio-slot--empty")).toBe(true);
|
|
expect(el.querySelector("audio")).toBeNull();
|
|
});
|
|
test("with audio and startSec renders player and seek button", async () => {
|
|
const { createAudioSlot } = await import("../src/shared/audio-slot");
|
|
const song: Song = { ...base, audio: "/a.mp3", parts: [{ ...base.parts[0]!, startSec: 30 }] };
|
|
const el = createAudioSlot(song);
|
|
expect(el.classList.contains("audio-slot--empty")).toBe(false);
|
|
expect(el.querySelectorAll("audio[src]")).toHaveLength(1);
|
|
expect(el.querySelectorAll('button[data-start="30"]')).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe("createVideoCards", () => {
|
|
test("one safe link per video", async () => {
|
|
const { createVideoCards } = await import("../src/shared/video-cards");
|
|
const el = createVideoCards(base);
|
|
const links = el.querySelectorAll("a.video-card");
|
|
expect(links).toHaveLength(2);
|
|
for (const a of links) {
|
|
expect(a.getAttribute("rel")).toContain("noopener");
|
|
expect(a.getAttribute("target")).toBe("_blank");
|
|
}
|
|
});
|
|
});
|