import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; const [app, index] = await Promise.all([ readFile(new URL('./app.js', import.meta.url), 'utf8'), readFile(new URL('./index.html', import.meta.url), 'utf8') ]); test('workbench exposes one XLS/XLSX roster attachment without turning it into persistent state', () => { assert.match(index, /id="rosterAttachment"[^>]+type="file"/); assert.match(index, /accept="\.xls,\.xlsx,application\/vnd\.ms-excel,application\/vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet"/); assert.doesNotMatch(index, /id="rosterAttachment"[^>]+multiple/); assert.match(index, /id="rosterAttachmentStatus"[^>]+role="status"/); assert.doesNotMatch(app, /sessionStorage\.(?:setItem|getItem)\([^\n]*(?:attachment|base64|file)/i); assert.doesNotMatch(app, /console\.(?:log|info|warn|error)\([^\n]*(?:attachment|base64|file)/i); }); test('new and awaiting roster tasks send a single in-memory base64 JSON attachment', () => { assert.match(app, /async function encodeRosterAttachment\(file\)/); assert.match(app, /reader\.readAsDataURL\(file\)/); assert.match(app, /content_base64:/); assert.match(app, /attachments: rosterAttachment \? \[rosterAttachment\] : undefined/); assert.match(app, /async function sendTaskAttachment/); assert.match(app, /message: ''/); assert.match(app, /attachments: \[attachment\]/); assert.match(app, /taskAttachmentDrafts/); }); test('awaiting_attachment has a dedicated attachment-only continuation UI', () => { assert.match(app, /function isAwaitingAttachment\(task\)/); assert.match(app, /status === 'awaiting_attachment'/); assert.match(app, /kind === 'awaiting_attachment'/); assert.match(app, /renderTaskAttachmentForm\(task\)/); assert.match(index, /option value="awaiting_attachment">等待名单附件<\/option>/); }); test('parser routing visibly locks Program-only routes and keeps emergency wording accurate', () => { assert.match(app, /route\.mode_locked/); assert.match(app, /route\.allowed_modes/); assert.match(app, /Program-only/); assert.match(app, /名单导入仍保持 Program/); assert.match(index, /可配置业务全部切回 AI/); });