Files
purewhiter--mobilegym/tests/runExplorerFilters.test.ts
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

176 lines
5.5 KiB
TypeScript

import fs from 'node:fs';
import path from 'node:path';
import vm from 'node:vm';
import { describe, expect, it } from 'vitest';
const runExplorerPath = path.resolve(__dirname, '../public/run_explorer.html');
const runExplorerHtml = fs.readFileSync(runExplorerPath, 'utf8');
function extractFunctionSource(name: string): string {
const signature = `function ${name}(`;
const start = runExplorerHtml.indexOf(signature);
if (start === -1) {
throw new Error(`Could not find function ${name} in run_explorer.html`);
}
const bodyStart = runExplorerHtml.indexOf('{', start);
if (bodyStart === -1) {
throw new Error(`Could not find opening brace for ${name}`);
}
let depth = 0;
for (let i = bodyStart; i < runExplorerHtml.length; i++) {
const char = runExplorerHtml[i];
if (char === '{') depth++;
if (char === '}') depth--;
if (depth === 0) {
return runExplorerHtml.slice(start, i + 1);
}
}
throw new Error(`Could not find closing brace for ${name}`);
}
type TaskStub = {
task_id: string;
task_name: string;
dirName: string;
status: string;
tags: string[];
};
type FilterState = {
success?: boolean;
failed?: boolean;
error?: boolean;
sideEffect?: boolean;
overdue?: boolean;
falseComplete?: boolean;
};
type RenderOptions = {
repeatN?: number;
grouped?: boolean;
};
function renderWithFilters(tasks: TaskStub[], filters: FilterState, options: RenderOptions = {}): string {
const wrap = {
innerHTML: '',
querySelectorAll: () => [] as Array<{ onclick?: () => void }>,
};
const inputs = {
fSuccess: { checked: filters.success ?? false },
fFailed: { checked: filters.failed ?? false },
fError: { checked: filters.error ?? false },
fSideEffect: { checked: filters.sideEffect ?? false },
fOverdue: { checked: filters.overdue ?? false },
fFalseComplete: { checked: filters.falseComplete ?? false },
};
const chips = Object.values(inputs).map(input => ({
querySelector: () => input,
classList: { toggle: () => {} },
}));
const elements = {
taskList: wrap,
taskFilter: { value: '' },
...inputs,
};
const taskGroups = new Map<string, TaskStub[]>();
if (options.grouped) {
taskGroups.set('group.demo', tasks);
}
const context = {
state: {
runDirName: 'demo-run',
summary: {},
meta: options.repeatN ? { repeat_n: options.repeatN } : {},
taskGroups,
tasks,
selectedTask: null,
},
el: (id: keyof typeof elements) => elements[id],
document: {
querySelectorAll: (selector: string) => (selector === '.chip' ? chips : []),
},
taskPassesTaxonomyFilters: () => true,
getTaskStatusForTrial: (task: TaskStub) => task.status,
getTaskIndicators: (task: TaskStub) => task.tags,
getResultForTask: () => ({
execution: { steps: 3, runtime_s: 1.2 },
}),
INDICATOR_LABELS: {
sideEffect: { icon: '🔧', text: '副作用' },
overdue: { icon: '⏱', text: '超步数' },
falseComplete: { icon: '🚫', text: '错误完成' },
},
console,
};
vm.createContext(context);
new vm.Script(`${extractFunctionSource('renderTaskList')}\nthis.__fn = renderTaskList;`).runInContext(context);
(context.__fn as () => void)();
return wrap.innerHTML;
}
describe('run_explorer indicator filter semantics', () => {
it('does not filter by issue tags when no issue tag is selected', () => {
const html = renderWithFilters(
[
{ task_id: 't.side', task_name: '带副作用失败', dirName: 'dir-side', status: 'failed', tags: ['sideEffect'] },
],
{ failed: true }
);
expect(html).toContain('带副作用失败');
});
it('filters positively when an issue tag is selected', () => {
const html = renderWithFilters(
[
{ task_id: 't.side', task_name: '带副作用失败', dirName: 'dir-side', status: 'failed', tags: ['sideEffect'] },
{ task_id: 't.plain', task_name: '普通失败', dirName: 'dir-plain', status: 'failed', tags: [] },
],
{ failed: true, sideEffect: true }
);
expect(html).toContain('带副作用失败');
expect(html).not.toContain('普通失败');
});
it('matches any selected issue tag instead of requiring all tags', () => {
const html = renderWithFilters(
[
{ task_id: 't.side', task_name: '副作用失败', dirName: 'dir-side', status: 'failed', tags: ['sideEffect'] },
{ task_id: 't.overdue', task_name: '超步数失败', dirName: 'dir-overdue', status: 'failed', tags: ['overdue'] },
{ task_id: 't.plain', task_name: '普通失败', dirName: 'dir-plain', status: 'failed', tags: [] },
],
{ failed: true, sideEffect: true, overdue: true }
);
expect(html).toContain('副作用失败');
expect(html).toContain('超步数失败');
expect(html).not.toContain('普通失败');
});
it('shows grouped trials that match any selected issue tag', () => {
const html = renderWithFilters(
[
{ task_id: 'group.demo', task_name: '同组任务', dirName: 'dir-side', status: 'failed', tags: ['sideEffect'] },
{ task_id: 'group.demo', task_name: '同组任务', dirName: 'dir-overdue', status: 'failed', tags: ['overdue'] },
{ task_id: 'group.demo', task_name: '同组任务', dirName: 'dir-plain', status: 'failed', tags: [] },
],
{ failed: true, sideEffect: true, overdue: true },
{ grouped: true, repeatN: 2 }
);
expect(html).toContain('data-dir="dir-side"');
expect(html).toContain('data-dir="dir-overdue"');
expect(html).not.toContain('data-dir="dir-plain"');
});
});