auralynq-rag / web /lib /ui.test.mjs
MHamdan's picture
Deploy Auralynq RAG (Llama-3.3-70B via HF Inference Providers)
8c1b9fe verified
Raw
History Blame Contribute Delete
15.9 kB
// Structural UI guards for the premium redesign. The project's test harness is
// dependency-free (no DOM runtime), so these assert that each redesigned surface
// composes the required sections, copy and states at the source level — catching
// accidental regressions of the acceptance criteria without a browser.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
const read = (p) => readFileSync(join(root, p), "utf8");
let passed = 0;
const t = (name, fn) => {
fn();
passed++;
console.log(" ok", name);
};
const has = (src, needle, msg) =>
assert.ok(src.includes(needle), msg || `expected to find: ${needle}`);
// ---- landing composes all main sections ---------------------------------
t("landing page renders all main sections", () => {
const page = read("app/page.tsx");
for (const s of ["Hero", "SystemStatus", "Features", "HowItWorks", "Differentiators", "Stack", "CTA", "Footer"]) {
has(page, `<${s} `, `landing missing <${s}>`);
}
});
t("hero has value prop, dual CTAs and trust badges", () => {
const hero = read("components/landing/Hero.tsx");
has(hero, "Voice-native RAG for private documents");
has(hero, "/chat"); // launch CTA
has(hero, "github.com/MHHamdan/Auralynq"); // github CTA
for (const b of ["Local-first", "$0 default", "Open source", "Citations on every answer", "Voice in / out", "Provider-agnostic"]) {
has(hero, b, `hero missing trust badge: ${b}`);
}
});
t("system status surfaces the seven subsystems with health states", () => {
const s = read("components/landing/SystemStatus.tsx");
for (const g of ["API", "Corpus", "Vector DB", "Embeddings", "LLM", "ASR / TTS", "Tracing"]) {
has(s, g, `status missing group: ${g}`);
}
for (const h of ["healthy", "degraded", "offline"]) has(s, h);
});
t("capabilities render six benefit cards", () => {
const f = read("components/landing/Features.tsx");
for (const c of [
"Voice-native document chat",
"Grounded answers with citations",
"PathRAG graph reasoning",
"Hybrid retrieval & reranking",
"Agent trace & observability",
"Local-first provider routing",
]) {
has(f, c, `features missing card: ${c}`);
}
});
t("how-it-works renders the nine-step pipeline", () => {
const h = read("components/landing/HowItWorks.tsx");
for (const step of ["Upload", "Parse", "Chunk", "Index", "Retrieve", "Rerank", "Reason", "Cite", "Observe"]) {
has(h, step, `pipeline missing step: ${step}`);
}
});
t("differentiators proof section exists", () => {
const d = read("components/landing/Differentiators.tsx");
has(d, "What makes Auralynq different?");
has(d, "Evidence-gated abstention");
has(d, "Local-first by default");
});
// ---- chat shell ----------------------------------------------------------
t("chat uses app bar, composer and never-empty inspector overview", () => {
const page = read("app/chat/page.tsx");
has(page, "<AppBar");
has(page, "<Composer");
has(page, "<InspectorOverview");
has(page, '"overview"'); // default inspector tab
});
t("composer has mode selector, voice, upload and key hints", () => {
const c = read("components/chat/Composer.tsx");
for (const m of ["Ask corpus", "Summarize", "Inventory", "Web"]) has(c, m);
has(c, "VoiceRecorder");
has(c, "Upload a document");
has(c, "Shift");
});
t("voice recorder exposes explicit states", () => {
const v = read("components/VoiceRecorder.tsx");
for (const st of ["idle", "listening", "transcribing", "speaking", "failed"]) has(v, st);
});
t("inspector overview is useful before any query", () => {
const o = read("components/chat/InspectorOverview.tsx");
for (const s of ["Your corpus", "System status", "Try asking"]) has(o, s);
});
t("inspector overview shows a first-run checklist for an empty corpus", () => {
const o = read("components/chat/InspectorOverview.tsx");
has(o, "First run");
has(o, "!summary.indexed"); // only shown before any document is indexed
for (const step of [
"Upload a document",
"Ask a question",
"Click a citation",
"Open the Source Workspace",
"Try ModelFit",
"Run a benchmark",
]) {
has(o, step, `first-run checklist missing step: ${step}`);
}
});
// ---- inspector panels ----------------------------------------------------
t("evidence panel has coverage, breakdown, debug disclosure and empty state", () => {
const e = read("components/EvidencePaths.tsx");
has(e, "Evidence coverage");
has(e, "displaySource");
has(e, "Debug · raw source");
has(e, "Ask a question to see the evidence trail");
has(e, "whySelected");
});
t("trace panel has summary dashboard and Phoenix card", () => {
const tr = read("components/TracePanel.tsx");
has(tr, "Hallu. risk");
has(tr, "Open in Phoenix");
has(tr, "not connected"); // degraded hint
has(tr, "Run a query to see the trace");
});
t("eval panel has feedback widget and planned evals", () => {
const ev = read("components/EvalPanel.tsx");
has(ev, "Rate the last answer");
has(ev, "Groundedness");
has(ev, "Abstention correctness");
});
t("insufficient-evidence card uses soft warning copy", () => {
const ie = read("components/InsufficientEvidence.tsx");
has(ie, "Not enough evidence in your indexed documents");
has(ie, "held back instead of guessing");
has(ie, "border-warn"); // soft warning, not error red
});
t("corpus inventory renders the inventory fields", () => {
const ci = read("components/CorpusInventory.tsx");
for (const field of ["Documents", "Languages", "File types", "Last indexed", "Top topics"]) {
has(ci, field, `inventory missing field: ${field}`);
}
});
t("citations clean internal paths via displaySource", () => {
has(read("components/Citations.tsx"), "displaySource");
has(read("components/Message.tsx"), "CorpusInventory");
});
// ---- design system -------------------------------------------------------
t("design tokens define all three themes with status + text scale", () => {
const css = read("app/globals.css");
for (const th of ['[data-theme="dark"]', '[data-theme="light"]', '[data-theme="comfort"]']) has(css, th);
for (const tok of ["--c-text-2", "--c-text-3", "--c-ok", "--c-warn", "--c-bad", "--ring"]) has(css, tok);
has(css, ".focusable");
has(css, ".evidence-strong");
});
// ---- new: trace rail, algorithm selector, settings -----------------------
t("agent activity rail is always visible in chat inspector", () => {
const page = read("app/chat/page.tsx");
has(page, "AgentActivityRail");
has(page, "traceAlwaysVisible");
has(page, "agentActivity");
});
t("agent activity rail has all required states", () => {
const rail = read("components/chat/AgentActivityRail.tsx");
has(rail, "idle");
has(rail, "generating");
has(rail, "abstained");
has(rail, "corpus_empty");
has(rail, "system_route");
has(rail, "TraceMiniTimeline");
has(rail, "AlgorithmBadge");
has(rail, "RiskBadge");
has(rail, "Ready — ask a question to see agent activity");
});
t("algorithm selector renders strategy dropdown near composer", () => {
const sel = read("components/chat/AlgorithmSelector.tsx");
has(sel, "RAG Algorithm");
has(sel, "Available");
has(sel, "Experimental");
has(sel, "Planned");
has(sel, "auralynq_rag");
has(sel, "fetchRAGStrategies");
has(sel, "auralynq.rag_strategy.v1");
});
t("composer includes algorithm selector", () => {
const c = read("components/chat/Composer.tsx");
has(c, "AlgorithmSelector");
has(c, "ragStrategy");
has(c, "onRagStrategyChange");
});
t("settings panel has all configurable options", () => {
const sp = read("components/chat/SettingsPanel.tsx");
has(sp, "Font Size");
has(sp, "Density");
has(sp, "Inspector Width");
has(sp, "Trace Visibility");
has(sp, "New Chat Behavior");
has(sp, "chat_only");
has(sp, "clear_corpus");
has(sp, "--font-scale");
has(sp, "--inspector-width");
});
t("css defines font-scale and density CSS variables", () => {
const css = read("app/globals.css");
has(css, "--font-scale");
has(css, "--density-scale");
has(css, "--inspector-width");
has(css, "--chat-max-width");
});
t("new chat supports clear-corpus mode with confirmation", () => {
const page = read("app/chat/page.tsx");
has(page, "clear_corpus");
has(page, "clearConfirmOpen");
has(page, "executeClearCorpus");
has(page, "This will remove all indexed documents");
});
t("app bar shows entities count and settings button", () => {
const bar = read("components/chat/AppBar.tsx");
has(bar, "entities");
has(bar, "onToggleSettings");
has(bar, "Display settings");
});
t("eval panel shows last query metrics from backend", () => {
const ev = read("components/EvalPanel.tsx");
has(ev, "evalLast");
has(ev, "Last query metrics");
has(ev, "exportEvalRun");
has(ev, "Strategy comparison");
});
// ---- visual grounding & source view ----------------------------------------
t("source view tab added to chat inspector", () => {
const page = read("app/chat/page.tsx");
has(page, '"source"');
has(page, "SourceViewPanel");
has(page, "visualGrounding");
has(page, "activeCitation");
});
t("source view panel has page viewer, overlay and legend", () => {
const sv = read("components/SourceViewPanel.tsx");
has(sv, "PageViewer");
has(sv, "HighlightBox");
has(sv, "ClaimGroundingView");
has(sv, "normalized_bbox");
has(sv, "reindex_required");
has(sv, "visual_grounding_available");
has(sv, "Source View");
has(sv, "Reindex");
has(sv, "grounding_stage");
});
t("source view panel handles unavailable grounding gracefully", () => {
const sv = read("components/SourceViewPanel.tsx");
has(sv, "Visual grounding not available");
has(sv, "Ask a question to see source grounding");
});
t("source view panel has zoom controls", () => {
const sv = read("components/SourceViewPanel.tsx");
has(sv, "Zoom in");
has(sv, "Zoom out");
has(sv, "fit");
});
t("evidence panel has source view open button on citations", () => {
const ev = read("components/EvidencePaths.tsx");
has(ev, "onOpenSource");
has(ev, "onOpenSourceTab");
has(ev, "Open Source View");
has(ev, "hasVisualGrounding");
has(ev, "Visual grounding");
});
t("api has visual grounding types and fetchers", () => {
const api = read("lib/api.ts");
has(api, "VisualGrounding");
has(api, "VisualHighlight");
has(api, "ClaimGrounding");
has(api, "fetchDocumentPages");
has(api, "documentPageImageUrl");
has(api, "visual_grounding_available");
has(api, "grounding_stage");
has(api, "normalized_bbox");
});
t("answer result type includes visual grounding field", () => {
const api = read("lib/api.ts");
has(api, "visual_grounding?: VisualGrounding | null");
});
t("source view panel renders highlight boxes with citation colors", () => {
const sv = read("components/SourceViewPanel.tsx");
has(sv, "CITATION_COLORS");
has(sv, "colorFor");
has(sv, "borderFor");
has(sv, "color_index");
});
t("source view panel shows claim grounding with support status", () => {
const sv = read("components/SourceViewPanel.tsx");
has(sv, "supported");
has(sv, "partial");
has(sv, "unsupported");
has(sv, "SUPPORT_CLS");
has(sv, "SUPPORT_LABEL");
});
t("chat page clears visual grounding on new query", () => {
const page = read("app/chat/page.tsx");
has(page, "setVisualGrounding(null)");
has(page, "setActiveCitation(null)");
});
// ---- Source Workspace Modal structural tests ----------------------------
t("SourceWorkspaceModal component file exists and exports correctly", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "export function SourceWorkspaceModal");
has(sw, "export interface SourceWorkspaceModalProps");
has(sw, "grounding: VisualGrounding | null | undefined");
has(sw, "citations: Citation[]");
has(sw, "activeCitation: string | null");
has(sw, "onClose: () => void");
});
t("SourceWorkspaceModal renders three-panel layout with citation and evidence panels", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "CitationPanel");
has(sw, "EvidencePanel");
has(sw, "WorkspacePageViewer");
has(sw, "grid-cols-[260px_1fr_280px]");
has(sw, "Grounded Source Workspace");
});
t("SourceWorkspaceModal has zoom controls with fit-width preset", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "ZOOM_PRESETS");
has(sw, "ZOOM_LABELS");
has(sw, "fit");
has(sw, "setZoom");
});
t("SourceWorkspaceModal has page navigation controls", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "navigatePage");
has(sw, "hasPrev");
has(sw, "hasNext");
has(sw, "currentPageKey");
has(sw, "ArrowRight");
});
t("SourceWorkspaceModal supports full-screen mode toggle", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "fullscreen");
has(sw, "setFullscreen");
has(sw, "Show side panels");
has(sw, "Full-screen PDF view");
});
t("SourceWorkspaceModal handles unavailable grounding gracefully", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "visual_grounding_available");
has(sw, "Visual grounding not available");
has(sw, "Reindex");
});
t("SourceWorkspaceModal navigates to active citation page on open", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "activeCitation");
has(sw, "initialPageKey");
has(sw, "setCurrentPageKey");
});
t("SourceWorkspaceModal closes on Escape key", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "Escape");
has(sw, "onClose");
has(sw, "window.addEventListener");
});
t("WorkspacePageViewer places overlays with normalized bbox percentages", () => {
const sw = read("components/SourceWorkspaceModal.tsx");
has(sw, "normalized_bbox");
has(sw, "position: \"absolute\"");
has(sw, ".toFixed(3)}%");
});
t("chat page opens workspace modal when View source is clicked", () => {
const page = read("app/chat/page.tsx");
has(page, "showWorkspace");
has(page, "setShowWorkspace");
has(page, "SourceWorkspaceModal");
has(page, "onOpenWorkspace");
});
t("chat page resets workspace state on new query", () => {
const page = read("app/chat/page.tsx");
has(page, "setShowWorkspace(false)");
});
t("InlineSourceStrip has two buttons: preview and view source workspace", () => {
const page = read("app/chat/page.tsx");
has(page, "onOpenWorkspace");
has(page, "View source ↗");
has(page, "Preview");
});
t("SourceViewPanel has Expand button to open workspace", () => {
const sv = read("components/SourceViewPanel.tsx");
has(sv, "onOpenWorkspace");
has(sv, "Expand");
has(sv, "Open full Source Workspace");
});
t("AlgorithmSelector groups strategies by status", () => {
const sel = read("components/chat/AlgorithmSelector.tsx");
has(sel, "GROUP_CONFIG");
has(sel, "Available now");
has(sel, "Experimental");
has(sel, "Planned / requires setup");
has(sel, "StatusGroup");
});
t("AlgorithmSelector planned strategies are non-selectable", () => {
const sel = read("components/chat/AlgorithmSelector.tsx");
has(sel, "selectable: false");
has(sel, "cursor-not-allowed");
has(sel, "disabled={!selectable}");
});
t("api.ts exports PageLayoutBlock and PageLayoutResponse types", () => {
const api = read("lib/api.ts");
has(api, "export interface PageLayoutBlock");
has(api, "export interface PageLayoutResponse");
has(api, "export async function fetchPageLayout");
});
t("api.ts exports fetchPageLayout with correct URL pattern", () => {
const api = read("lib/api.ts");
has(api, "/pages/${page}/layout");
has(api, "encodeURIComponent(docId)");
});
console.log(`\n${passed} ui structure tests passed`);