127 lines
6.0 KiB
JavaScript
127 lines
6.0 KiB
JavaScript
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 newTaskJs = fs.readFileSync("web/static/js/newtask.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="files-scope-all"[^>]*>全部文件</);
|
|
assert.match(filesJs, /state\.filesScope = "task"/);
|
|
assert.match(filesJs, /state\.filesScope = "all"/);
|
|
});
|
|
|
|
test("task navigation groups chats by project with restrained icon sizes", () => {
|
|
const chatJs = fs.readFileSync("web/static/js/chat.js", "utf8");
|
|
assert.match(pageHtml, />项目与对话</);
|
|
assert.match(chatJs, /task-nav-recent/);
|
|
assert.match(chatJs, /task-project-head/);
|
|
assert.match(chatJs, /groupedTaskListHtml/);
|
|
assert.match(chatJs, /loadProjectTasks/);
|
|
assert.match(chatJs, /working_dir: projectName/);
|
|
assert.match(chatJs, /state\.taskHasMore = filtered &&/);
|
|
assert.match(chatJs, /task-project-more/);
|
|
assert.match(chatJs, /Math\.min\(3, tasks\.length\)/);
|
|
assert.match(chatJs, /task-recent-toggle/);
|
|
assert.match(chatJs, /revealTaskInProject/);
|
|
assert.match(chatJs, /scroller\.onscroll = maybeLoadMore/);
|
|
assert.match(chatJs, /task-project-create/);
|
|
assert.doesNotMatch(chatJs, /if \(!folder\.n_tasks\) continue/);
|
|
assert.match(chatJs, /renameProjectFolder/);
|
|
assert.match(chatJs, /removeProjectFolder/);
|
|
assert.match(chatJs, /openProjectFiles/);
|
|
assert.match(filesJs, /function createProjectFolder/);
|
|
assert.match(filesJs, /function removeProjectFolder/);
|
|
assert.match(chatJs, /folders-refresh-requested/);
|
|
assert.match(newTaskJs, /folders-refresh-requested/);
|
|
assert.match(filesJs, /setRightPanelTab\("files"\)/);
|
|
assert.match(filesJs, /recursive: entries\.length > 0/);
|
|
assert.match(pageHtml, /\.task-nav-icon \{ width: 15px; height: 15px;/);
|
|
assert.match(pageHtml, /\.task-project-head \.task-nav-icon \{ width: 16px; height: 16px;/);
|
|
assert.match(pageHtml, /\.task-projects \{ flex: 1; min-height: 0; overflow-y: auto;/);
|
|
assert.match(pageHtml, /\.task-nav-recent \{ flex: none;/);
|
|
});
|
|
|
|
test("file rows render restrained type-specific svg icons", () => {
|
|
assert.match(filesJs, /function fileIconKind/);
|
|
assert.match(filesJs, /if \(entry\.is_dir\) return "folder"/);
|
|
assert.match(filesJs, /word: new Set/);
|
|
assert.match(filesJs, /sheet: new Set/);
|
|
assert.match(filesJs, /slide: new Set/);
|
|
assert.match(filesJs, /if \(ext === "pdf"\) return "pdf"/);
|
|
assert.match(filesJs, /image: new Set/);
|
|
assert.match(filesJs, /archive: new Set/);
|
|
assert.match(filesJs, /code: new Set/);
|
|
assert.match(filesJs, /file-type-icon type-\$\{kind\}/);
|
|
assert.match(pageHtml, /\.file-type-icon svg \{ width: 16px; height: 16px;/);
|
|
assert.doesNotMatch(pageHtml, /\.ico-file::before/);
|
|
});
|
|
|
|
test("current directory supports search, sort, and modification time", () => {
|
|
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\)/);
|
|
});
|