Files
pi-gui/test/quick-pane-ui.test.js
T

161 lines
6.1 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
test("Quick Pane polls snapshots, renders answers, and gates handoff", async () => {
const source = await readFile(
new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url),
"utf8",
);
assert.match(source, /get_session_runtime_snapshot/);
assert.match(source, /setInterval\(\(\) => void refresh\(\), 800\)/);
assert.match(source, /<ReactMarkdown remarkPlugins=\{\[remarkGfm\]\}>/);
assert.match(source, /className="quick-pane-notice escalation"/);
assert.match(
source,
/<button type="button" onClick=\{continueFull\}>\s*Continue\s*<\/button>/,
);
});
test("main Settings uses shared catalog selectors with provider derived from models", async () => {
const [pane, app, catalog, persistence] = await Promise.all([
readFile(
new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url),
"utf8",
),
readFile(new URL("../ui/src/App.tsx", import.meta.url), "utf8"),
readFile(new URL("../ui/src/model-catalog.ts", import.meta.url), "utf8"),
readFile(
new URL("../ui/src/workspace/persistence.ts", import.meta.url),
"utf8",
),
]);
assert.match(
catalog,
/invoke<\{ models\?: Model\[\] \}>\("get_model_catalog"\)/,
);
assert.match(app, /getModelCatalog\(\)/);
assert.match(app, /Quick model[\s\S]*?<select/);
assert.match(app, /Follow-up model[\s\S]*?<select/);
assert.doesNotMatch(app, /Quick provider\s*<input/);
assert.doesNotMatch(app, /Follow-up provider\s*<input/);
assert.match(app, /provider: identity\.provider/);
assert.match(app, /followUpProvider: identity\.provider/);
assert.match(app, /Save Quick Pane settings/);
assert.match(pane, /getModelCatalog\(\)/);
assert.match(pane, /set_thinking_level/);
assert.doesNotMatch(pane, /showSettings/);
assert.doesNotMatch(pane, /Save Quick Pane settings/);
assert.match(pane, /isStreaming === false/);
assert.match(pane, /current === submitted\.request \? "" : current/);
assert.match(pane, /readOnly=\{runActive\}/);
assert.match(pane, /window\.requestAnimationFrame\(focusInput\)/);
assert.match(pane, /\.slice\(0, 5\)/);
assert.match(pane, /aria-label="Answer history"/);
assert.doesNotMatch(pane, /localStorage|sessionStorage|save_ui_state/);
assert.match(persistence, /quickPane: current\.quickPane/);
});
test("Quick Pane keeps slash-containing model IDs atomic and reloads catalog on focus", async () => {
const [pane, catalog] = await Promise.all([
readFile(
new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url),
"utf8",
),
readFile(new URL("../ui/src/model-catalog.ts", import.meta.url), "utf8"),
]);
assert.match(pane, /useState<ModelIdentity>/);
assert.match(pane, /provider: selectedModel\.provider/);
assert.match(pane, /modelId: selectedModel\.modelId/);
assert.doesNotMatch(pane, /selectedModel\.split/);
assert.match(pane, /onFocusChanged[\s\S]*?void loadModelCatalog\(\)/);
assert.match(
catalog,
/JSON\.stringify\(\[identity\.provider, identity\.modelId\]\)/,
);
assert.match(catalog, /JSON\.parse\(value\)/);
});
test("Quick Pane routes extension responses through existing bridge command", async () => {
const source = await readFile(
new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url),
"utf8",
);
assert.match(
source,
/<ExtensionDialog extension=\{extension\} onRespond=\{respond\} \/>/,
);
assert.match(source, /invoke\("respond_to_extension"/);
assert.match(source, /requestId: extension\.id/);
});
test("Quick Pane uses native dimensions and request-first compact layout", async () => {
const [source, stylesheet, configSource, nativeSource, capabilitySource] =
await Promise.all([
readFile(
new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url),
"utf8",
),
readFile(
new URL("../ui/src/quick-pane/quick-pane.css", import.meta.url),
"utf8",
),
readFile(
new URL("../ui/src-tauri/tauri.conf.json", import.meta.url),
"utf8",
),
readFile(new URL("../ui/src-tauri/src/lib.rs", import.meta.url), "utf8"),
readFile(
new URL("../ui/src-tauri/capabilities/default.json", import.meta.url),
"utf8",
),
]);
const config = JSON.parse(configSource);
const capability = JSON.parse(capabilitySource);
const quickPane = config.app.windows.find(
(window) => window.label === "quick-pane",
);
assert.deepEqual(
{
width: quickPane.width,
height: quickPane.height,
transparent: quickPane.transparent,
},
{ width: 640, height: 440, transparent: true },
);
assert.ok(capability.windows.includes("quick-pane"));
assert.match(nativeSource, /WindowEvent::CloseRequested/);
assert.match(nativeSource, /api\.prevent_close\(\)/);
assert.match(
nativeSource,
/WebviewWindowBuilder::from_config\(app, config\)/,
);
assert.match(nativeSource, /get_or_create_quick_pane/);
assert.doesNotMatch(source, /onCloseRequested/);
assert.match(source, /className="quick-pane-request"/);
assert.match(source, /className="quick-pane-exchange"/);
assert.match(source, /className="quick-pane-composer"/);
assert.match(source, /className="quick-pane-send"/);
assert.match(source, /ref=\{inputRef\}/);
assert.match(source, /focusInput\(\)/);
assert.doesNotMatch(source, /if \(!event\.payload\)[\s\S]*?hide\(/);
assert.match(
source,
/event\.key === "Enter" &&\s*!event\.shiftKey &&\s*!event\.nativeEvent\.isComposing/,
);
assert.match(source, /event\.preventDefault\(\);\s*void run\(\);/);
assert.match(
stylesheet,
/\.quick-pane-exchange[\s\S]*?flex: 1 1 auto[\s\S]*?overflow-y: auto/,
);
assert.match(
stylesheet,
/\.quick-pane-composer[\s\S]*?height: 120px[\s\S]*?min-height: 110px[\s\S]*?max-height: 130px/,
);
assert.match(stylesheet, /\.quick-pane-send[\s\S]*?position: absolute/);
assert.match(stylesheet, /@media \(max-width: 400px\)/);
assert.match(stylesheet, /#f0a347/);
assert.doesNotMatch(stylesheet, /#8aa9d6|#46494f|#23252a/);
});