62 lines
3.3 KiB
JavaScript
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'\)/);
|
|
});
|