feat: make display setup actionable

This commit is contained in:
ekklesia
2026-09-29 20:49:51 -05:00
parent 7999582460
commit 36c1a72363
5 changed files with 654 additions and 299 deletions
+44 -14
View File
@@ -18,6 +18,21 @@ import { createFakeHost, errorResponse, FAKE_SETTINGS, jsonResponse } from "./su
let container: HTMLElement;
let root: Root;
const DISPLAY_SETUP_URL = "https://dashboard.example.test/display/signage";
const copiedText: string[] = [];
function dashboard(context: Parameters<typeof SignageDashboard>[0]["context"]): React.ReactElement {
return (
<SignageDashboard
context={context}
copyText={(value) => {
copiedText.push(value);
return Promise.resolve();
}}
displaySetupUrl={DISPLAY_SETUP_URL}
/>
);
}
async function render(element: React.ReactElement): Promise<void> {
await act(async () => {
@@ -131,6 +146,7 @@ const TIMELINE = {
};
beforeEach(() => {
copiedText.length = 0;
container = document.createElement("div");
document.body.append(container);
root = createRoot(container);
@@ -168,7 +184,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
expect(container.querySelector("[data-testid='signage-current']")?.textContent).toContain(
"welcome",
@@ -197,7 +213,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
// The current slide is still legible; the buttons that would be refused are simply not offered.
expect(container.querySelector("[data-testid='signage-current']")).not.toBeNull();
@@ -220,7 +236,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
expect(container.textContent).toContain("Active: Sunday morning");
await click(
[...container.querySelectorAll("[role='tab']")].find(
@@ -262,7 +278,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
await click(
[...container.querySelectorAll("[role='tab']")].find(
(candidate) => candidate.textContent === "Timelines",
@@ -293,7 +309,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
await click(
[...container.querySelectorAll("[role='tab']")].find(
(tab) => tab.textContent === "Timelines",
@@ -357,7 +373,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
await click(
[...container.querySelectorAll("[role='tab']")].find(
(tab) => tab.textContent === "Timelines",
@@ -407,7 +423,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
await click(
[...container.querySelectorAll("[role='tab']")].find(
(tab) => tab.textContent === "Providers",
@@ -430,7 +446,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
await click(
[...container.querySelectorAll("[role='tab']")].find(
(tab) => tab.textContent === "Providers",
@@ -476,13 +492,15 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
await click(
[...container.querySelectorAll("[role='tab']")].find((tab) => tab.textContent === "Displays"),
);
expect(container.textContent).toContain("Online");
expect(container.textContent).toContain("12 cached");
const display = container.querySelector("[data-testid='signage-display-viewer-1']");
expect(display?.closest(".signage-channel")?.textContent).toContain("Default");
// A sanitized code, never a stack or a message a browser happened to have.
expect(container.textContent).toContain("SLIDE_DECODE_FAILED");
});
@@ -521,15 +539,27 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
await click(
[...container.querySelectorAll("[role='tab']")].find(
(candidate) => candidate.textContent === "Displays",
),
);
expect(container.textContent).toContain("/display/signage");
expect(container.textContent).toContain("Provider audience key");
const setupLink = [...container.querySelectorAll("a")].find(
(candidate) => candidate.textContent?.trim() === "Open display setup",
);
expect(setupLink?.href).toBe(DISPLAY_SETUP_URL);
expect(setupLink?.target).toBe("_blank");
expect(setupLink?.rel).toContain("noopener");
await click(buttonNamed("Copy URL"));
expect(copiedText).toEqual([DISPLAY_SETUP_URL]);
expect(container.textContent).toContain("Display setup URL copied.");
expect(container.textContent).toContain("Channel groups and displays");
expect(container.textContent).toContain("Audience channels");
expect(container.textContent).toContain("Channel name");
expect(container.textContent).toContain("Audience key");
expect(container.textContent).toContain("Advanced: lowercase ID sent to content providers");
await click(buttonNamed("Delete group"));
expect(deleted).toEqual([]);
expect(container.textContent).toContain("Displays assigned here will move");
@@ -564,7 +594,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
await click(
[...container.querySelectorAll("[role='tab']")].find((tab) => tab.textContent === "Images"),
);
@@ -615,7 +645,7 @@ describe("Signage dashboard", () => {
],
});
await render(<SignageDashboard context={host.context} />);
await render(dashboard(host.context));
expect(container.textContent).toContain("Signage could not load this");
expect(container.textContent).toContain("The operation could not be completed.");