import { test } from "node:test"; import assert from "node:assert/strict"; import React from "react"; import type { ActionableRow, ActionableSummary } from "../../lib/actionable/loadActionable"; // The module under test contains JSX (`headerAction` is an element). This // repo's tsconfig sets `jsx: "preserve"` for Next, which the unit runner's // esbuild can only turn into classic `React.createElement` calls — so the // module needs a global `React` when it is loaded outside Next. Next itself // uses the automatic runtime and never reaches this line. Kept here rather // than adding an import Next does not need to the module itself. (globalThis as unknown as { React: unknown }).React = React; const { channelWorkSections, sectionsFor } = await import("./sections"); // A summary whose every list is a DISTINCT array object, so "which list does // this section draw" is answerable by identity. The pairing used to be done by // hand at the render site, which is exactly the hand-exhaustiveness that let a // section quietly render the wrong rows. function distinctSummary(): ActionableSummary { const fresh = () => [] as ActionableRow[]; return { rows: fresh(), undownloaded: fresh(), metadataScan: fresh(), missingNeverFetched: fresh(), untranscribed: fresh(), incompleteTranscripts: fresh(), shortAudio: fresh(), cleanTranscribedAudio: fresh(), cleanExtraFormats: fresh(), digestWarnings: fresh(), }; } test("section ids are unique", () => { const ids = channelWorkSections().map((s) => s.id); assert.equal(new Set(ids).size, ids.length); }); test("every section draws a distinct list of the summary", () => { const summary = distinctSummary(); const drawn = channelWorkSections().map((s) => s.getRows(summary)); assert.equal(new Set(drawn).size, drawn.length); for (const list of drawn) { assert.ok( Object.values(summary).includes(list), "getRows must return a field of the summary, not a new array", ); } }); test("sectionsFor groups the sections by the page that renders them", () => { assert.deepEqual( sectionsFor("download").map((s) => s.id), ["undownloaded", "missing-never-fetched", "short-audio"], ); assert.deepEqual( sectionsFor("metadata-scan").map((s) => s.id), ["metadata-scan"], ); assert.deepEqual( sectionsFor("transcription").map((s) => s.id), ["untranscribed", "incomplete-transcripts"], ); assert.deepEqual( sectionsFor("digest").map((s) => s.id), ["digest-warnings"], ); assert.deepEqual( sectionsFor(null).map((s) => s.id), ["clean-transcribed-audio", "clean-extra-formats"], ); }); test("sectionsFor puts work before attention", () => { for (const op of [ "download", "metadata-scan", "transcription", "digest", null, ] as const) { const roles = sectionsFor(op).map((s) => s.role); assert.deepEqual(roles, [...roles].sort((a, b) => (a === b ? 0 : a === "work" ? -1 : 1))); } }); test("an operation with no sections of its own gets none", () => { // Every runner/sweep page calls this with its own operation id; the ones // with no work sections (diarization, attribution-*) must get an // empty list rather than throwing, which is what makes the cast at the // /operations/[id] call site safe. assert.deepEqual( sectionsFor("diarization" as never), [], ); });