import assert from "node:assert/strict"; import fs from "node:fs"; import test from "node:test"; const filesJs = fs.readFileSync("web/static/js/files.js", "utf8"); const layoutJs = fs.readFileSync("web/static/js/layout.js", "utf8"); const jobsJs = fs.readFileSync("web/static/js/software_jobs.js", "utf8"); const previewJs = fs.readFileSync("web/static/js/preview.js", "utf8"); const pageHtml = fs.readFileSync("web/static/dev.html", "utf8"); test("file menu exposes an explicit confirmed replacement action", () => { assert.match(filesJs, /label: "替换文件"/); assert.match(filesJs, /title: "替换文件"/); assert.match(filesJs, /okText: "确认替换"/); assert.match(filesJs, /danger: true/); assert.match(filesJs, /conflict: "overwrite"/); assert.match(filesJs, /targetName: name/); }); test("ordinary uploads default to rename conflict policy", () => { assert.match(filesJs, /fd\.append\("conflict", opts\.conflict \|\| "rename"\)/); assert.match(filesJs, /同名文件已保留为/); }); test("file workspace exposes explicit task and all-files contexts", () => { assert.match(pageHtml, /id="files-scope-task"[^>]*>当前对话]*>全部文件 { assert.match(pageHtml, /id="file-search"/); assert.match(pageHtml, /id="file-sort"/); assert.match(pageHtml, /value="mtime-desc"/); assert.match(pageHtml, /value="size-desc"/); assert.match(filesJs, /visibleEntries/); assert.match(filesJs, /fileMtime\(e\.mtime\)/); }); test("software jobs share the embed-safe right workspace", () => { assert.match(pageHtml, /data-right-panel-tab="files"/); assert.match(pageHtml, /data-right-panel-tab="jobs"/); assert.match(pageHtml, /id="right-jobs-panel"/); assert.doesNotMatch(pageHtml, /id="software-job-backdrop"/); assert.doesNotMatch(pageHtml, /id="software-job-panel"/); assert.match(layoutJs, /setRightPanelTab/); assert.match(jobsJs, /unreadTerminal/); assert.match(jobsJs, /作业列表加载失败,请点击刷新重试/); }); test("current task software jobs render in a dedicated result rail", () => { assert.match(pageHtml, /id="software-result-rail"/); assert.match(pageHtml, /id="mobile-software-results"/); assert.match(jobsJs, /task_id: taskJobsTaskId/); assert.match(jobsJs, /renderTaskResultRail/); assert.match(jobsJs, /task-selection-changed/); assert.match(jobsJs, /openSoftwarePreviewGallery/); }); test("software preview gallery uses authenticated fetch and hides artifact download", () => { assert.match(previewJs, /export async function openSoftwarePreviewGallery/); assert.match(previewJs, /"Authorization": "Bearer " \+ state\.token/); assert.match(previewJs, /\$\("fp-download"\)\.hidden = true/); assert.match(pageHtml, /software-preview-gallery/); }); test("mobile navigation distinguishes list, files, and software jobs", () => { assert.match(pageHtml, /id="mv-tab-left"[^>]*>\s*列表/); assert.match(pageHtml, /id="mv-tab-right"[^>]*data-right-tab="files"/); assert.match(pageHtml, /id="mv-tab-jobs"[^>]*data-right-tab="jobs"/); }); test("right workspace widens the legacy default without overriding custom widths", () => { assert.match(layoutJs, /right: \{ min: 220, max: 560, def: 360 \}/); assert.match(layoutJs, /!migrated && \(!storedRight \|\| storedRight === "320"\)/); assert.match(layoutJs, /localStorage\.setItem\(LS_RIGHT_WIDTH_360_MIGRATED, "1"\)/); assert.match(pageHtml, /--right-grid-width: var\(--right-pane-width, 360px\)/); });