kaikai_test/test/temporary-query.test.js
2026-05-14 18:53:53 +08:00

72 lines
3.9 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
const server = await readFile(new URL("../src/server.js", import.meta.url), "utf8");
const html = await readFile(new URL("../public/index.html", import.meta.url), "utf8");
const app = await readFile(new URL("../public/app.js", import.meta.url), "utf8");
const ocr = await readFile(new URL("../src/ocr.js", import.meta.url), "utf8");
test("temporary query API collects without appending to history", () => {
assert.match(server, /url\.pathname === "\/api\/query-once"/);
const block = server.match(/if \(url\.pathname === "\/api\/query-once"[\s\S]*?return sendJson\(response, 200, \{ items \}\);\s*\}/)?.[0] || "";
assert.match(block, /collectTemporaryQueryItems\(/);
assert.doesNotMatch(block, /appendCollection\(/);
});
test("temporary query UI has isolated input, action, and CSV export", () => {
assert.match(html, /id="temporary-query-panel"/);
assert.match(html, /id="temporary-file-input"[^>]*type="file"/);
assert.match(html, /id="temporary-query-text"/);
assert.match(html, /id="temporary-query-button"[^>]*>一键查询<\/button>/);
assert.match(html, /id="temporary-export-button"[^>]*>导出临时 CSV<\/button>/);
assert.match(app, /temporaryQueryButton\s*=\s*document\.querySelector\("#temporary-query-button"\)/);
assert.match(app, /temporaryFileInput\.addEventListener\("change"/);
assert.match(app, /postJson\("\/api\/query-once"/);
assert.match(app, /downloadTemporaryCsv\(/);
});
test("temporary query renders platform results as each request finishes", () => {
assert.match(app, /runTemporaryQueryProgressively\(names, platforms\)/);
assert.match(app, /temporaryQueryTasks\(names, platforms\)/);
assert.match(app, /postJson\("\/api\/query-once", \{\s*names: \[task\.name\],\s*platforms: \[task\.platform\]/);
assert.match(app, /mergeTemporaryQueryResult\(task\.name, payload\.items\?.\[0\]/);
assert.match(app, /renderTemporaryResults\(temporaryQueryItems\)/);
assert.match(app, /clientMapLimit\(tasks, 6/);
});
test("temporary query supports dropping Excel-style files into the list box", () => {
assert.match(html, /accept="\.txt,\.csv,\.xlsx,text\/plain,text\/csv/);
assert.match(app, /temporaryQueryText\.addEventListener\("drop"/);
assert.match(app, /loadTemporaryImportFile\(/);
assert.match(app, /extractXlsxText\(/);
assert.match(app, /normalizeTemporaryListText\(/);
});
test("temporary query supports OCR import for screenshot images", () => {
assert.match(server, /recognizeImageText/);
assert.match(server, /url\.pathname === "\/api\/temporary-ocr"/);
assert.match(server, /readImageUploadBody\(request\)/);
assert.match(app, /postJson\("\/api\/temporary-ocr"/);
assert.match(app, /readFileAsDataUrl\(/);
assert.doesNotMatch(app, /截图需要 OCR/);
assert.match(ocr, /windows-ocr\.ps1/);
assert.match(ocr, /mkdtemp/);
});
test("temporary query runs with bounded concurrency and per-program failures", () => {
assert.match(server, /async function collectTemporaryQueryItems/);
assert.match(server, /const history = await readHistory\(\)/);
assert.match(server, /mapLimit\(names, concurrency/);
assert.match(server, /const concurrency = Math\.min\(Math\.max\(Number\(body\.concurrency \|\| 3\), 1\), 5\)/);
assert.match(server, /const manualUrls = sanitizeUrls\(body\.urls \|\| \{\}\)/);
assert.match(server, /urls: \{ \.\.\.historyProgramUrls\(history, name\), \.\.\.manualUrls \}/);
assert.match(server, /freshSearchPlatforms: body\.freshSearch \? platforms\.filter\(\(platform\) => !manualUrls\[platform\]\) : \[\]/);
assert.match(server, /delayMs: 0/);
assert.match(server, /quickSearch: body\.quickSearch !== false/);
assert.match(server, /parallelPlatforms: true/);
assert.match(server, /temporaryErrorCollection/);
assert.match(server, /async function mapLimit/);
assert.match(server, /function historyProgramUrls/);
});