Files
LWLT-AIBOT/LianSyn-platform/app-operations-dashboard.test.mjs
2026-09-02 11:52:04 +08:00

62 lines
3.3 KiB
JavaScript

import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import test from 'node:test';
const [app, index, styles, taskService] = await Promise.all([
readFile(new URL('./app.js', import.meta.url), 'utf8'),
readFile(new URL('./index.html', import.meta.url), 'utf8'),
readFile(new URL('./styles.css', import.meta.url), 'utf8'),
readFile(new URL('../control-plane/src/task-service.ts', import.meta.url), 'utf8')
]);
test('dashboard summary cards share one visual treatment and expose an accessible selected state', () => {
assert.match(app, /label: '操作次数'[^\n]+tone: 'total'/);
assert.doesNotMatch(app, /tone: 'primary'/);
assert.match(app, /card\.setAttribute\('aria-pressed', String\(isSelected\)\)/);
assert.match(styles, /\.operations-dashboard-summary\s*{[\s\S]*?grid-template-columns: repeat\(5, minmax\(0, 1fr\)\)/);
assert.match(styles, /\.operations-dashboard-stat\.is-active\s*{[\s\S]*?background: var\(--dashboard-accent-soft\);[\s\S]*?transform: none;/);
assert.doesNotMatch(styles, /operations-dashboard-stat-primary/);
});
test('waiting-for-input work is folded into in-progress instead of exposing a follow-up state', () => {
assert.doesNotMatch(index, /<option value="attention">/);
assert.doesNotMatch(app, /label: '待跟进'/);
assert.match(app, /outcomeKind === 'attention'\) return '进行中'/);
assert.match(app, /Number\(summary\.active \|\| 0\) \+ Number\(summary\.attention \|\| 0\)/);
const activeSql = taskService.slice(
taskService.indexOf('const OPERATIONS_ACTIVE_SQL'),
taskService.indexOf('const OPERATIONS_COMPLETED_STATUSES')
);
assert.doesNotMatch(activeSql, /OPERATIONS_ATTENTION_SQL/);
});
test('dashboard route has a focused leadership header and phone-safe navigation shell', () => {
assert.match(index, /content="width=device-width, initial-scale=1, viewport-fit=cover"/);
assert.match(index, /class="operations-dashboard-page-head"[\s\S]*?<h1>平台运行看板<\/h1>/);
assert.match(index, /class="operations-dashboard-back-link" href="\/">返回操作台<\/a>/);
assert.match(
styles,
/body\.operations-dashboard-page-active \.top-nav,[\s\S]*?#statusDetailsPopover\s*{[\s\S]*?display: none !important;/
);
assert.match(
styles,
/@media \(max-width: 920px\)[\s\S]*?body\.operations-dashboard-page-active \.app-shell\s*{[\s\S]*?width: 100%;/
);
});
test('phone dashboard turns metrics and task rows into touch-friendly cards', () => {
assert.match(
styles,
/\.operations-dashboard-summary\s*{[\s\S]*?display: flex;[\s\S]*?overflow-x: auto;[\s\S]*?scroll-snap-type: x proximity;/
);
assert.match(styles, /\.operations-dashboard-business-row,[\s\S]*?min-height: 48px;/);
assert.match(styles, /\.operations-dashboard-table thead\s*{\s*display: none;/);
assert.match(styles, /content: attr\(data-label\);/);
assert.match(styles, /\.operations-dashboard-detail\s*{[\s\S]*?env\(safe-area-inset-top\)/);
for (const label of ['操作时间', '员工名称', '任务类型', '原始输入', '最终输出', '任务结果']) {
assert.match(app, new RegExp(`dataset\\.label = '${label}'`));
}
assert.match(app, /document\.body\.classList\.add\('operations-dashboard-detail-open'\)/);
assert.match(app, /document\.body\.classList\.remove\('operations-dashboard-detail-open'\)/);
});