Files
maestro/scripts/check-execution-conversations.mjs
T
oss-sync 57702b4f34
CI / build-and-test (push) Canceled after 0s
sync: update from private repo (2336949a)
2026-09-17 11:29:31 +00:00

114 lines
7.5 KiB
JavaScript

import assert from 'node:assert/strict';
import { once } from 'node:events';
import { createServer } from 'node:http';
import { unlink, writeFile } from 'node:fs/promises';
import { createRequire } from 'node:module';
import { dirname, join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const root = dirname(dirname(fileURLToPath(import.meta.url)));
const ui = join(root, 'ui');
process.env.BROWSERSLIST_IGNORE_OLD_DATA = 'true';
process.chdir(ui);
const require = createRequire(join(ui, 'package.json'));
const { createServer: createViteServer } = await import(pathToFileURL(require.resolve('vite')).href);
const { chromium } = require('playwright');
const fixture = `/tmp/execution-conversations-${process.pid}.js`;
let vite, server, browser;
try {
await writeFile(fixture, `
import React from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import '${ui}/src/index.css';
import i18n from '${ui}/src/i18n/index.ts';
import { TaskExecutionsProvider } from '${ui}/src/hooks/useTaskExecutions.ts';
import { ExecutionsTab } from '${ui}/src/components/detail/tabs/ExecutionsTab.tsx';
import { useVisibleDetailTabs } from '${ui}/src/components/detail/detailTabs.ts';
import { SpaceChatRail } from '${ui}/src/components/spaces/SpaceChatRail.tsx';
function App() {
const [kind, setKind] = React.useState('delegate');
const tabs = useVisibleDetailTabs(1).filter(t => ['delegates','subtasks'].includes(t.id));
return React.createElement('div', {style:{display:'flex',width:'100%',minWidth:0}},
React.createElement('main', {style:{flex:1,minWidth:0,padding:12}}, React.createElement(ExecutionsTab,{key:kind,taskId:1,kind})),
React.createElement(SpaceChatRail,{items:tabs,activeId:kind==='delegate'?'delegates':'subtasks',paneOpen:true,onSelect:id=>setKind(id==='delegates'?'delegate':'subtask'),ariaLabel:'Executions',renderLabel:t=>i18n.t(t.labelKey,{ns:'detail'}),appearClass:()=>''}));
}
window.setLanguage = async lang => {await i18n.changeLanguage(lang); render();};
const tree = createRoot(document.getElementById('root'));
const client = new QueryClient({defaultOptions:{queries:{retry:false}}});
function render(){ tree.render(React.createElement(QueryClientProvider,{client},React.createElement(TaskExecutionsProvider,{taskId:1},React.createElement(App)))); }
render();
`);
vite = await createViteServer({ root: ui, appType: 'custom', logLevel: 'error', server: { middlewareMode: true, hmr: false, fs: { allow: [root, '/tmp'] } } });
const executions = [
...Array.from({ length: 80 }, (_, i) => ({ id: `delegate:j:d${i}`, kind: 'delegate', jobId: 'j', rootJobId: 'j', title: `Research ${i + 1}`, status: 'success', createdAt: '2026-09-08T00:00:00Z' })),
{ id: 'subtask:k', kind: 'subtask', jobId: 'k', rootJobId: 'j', title: 'Implementation', status: 'succeeded', createdAt: '2026-09-08T00:01:00Z' },
];
const conversationRequests = [];
server = createServer(async (req, res) => {
if (req.url.startsWith('/api/')) {
let payload = { available: false, active: false };
if (req.url.endsWith('/executions')) payload = { executions, truncated: false };
if (req.url.endsWith('/conversation')) {
conversationRequests.push(req.url);
payload = { source: 'history', truncated: false, messages: [
{ role: 'user', content: 'Inspect the example files.' },
{ role: 'assistant', content: '**Readable result**', thinking: 'Recorded thinking details', llm: { durationMs: 1800, promptTokens: 400, completionTokens: 60 }, toolCalls: [{ id: 'call1', name: 'Read', arguments: '{"path":"sample.txt"}' }] },
{ role: 'tool', toolCallId: 'call1', content: 'Recorded tool result', isError: true, durationMs: 1234, cacheHit: false },
] };
}
res.setHeader('content-type', 'application/json'); res.end(JSON.stringify(payload)); return;
}
if (req.url === '/') {
res.setHeader('content-type', 'text/html');
res.end(await vite.transformIndexHtml('/', `<!doctype html><html><head><meta charset="utf-8"></head><body><div id="root"></div><script type="module" src="/@fs${fixture}"></script></body></html>`)); return;
}
vite.middlewares(req, res);
});
server.listen(0, '127.0.0.1'); await once(server, 'listening');
browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-dev-shm-usage'] });
const page = await browser.newPage({ viewport: { width: 600, height: 700 } });
await page.goto(`http://127.0.0.1:${server.address().port}/`);
await page.getByText('Readable result', { exact: true }).waitFor();
assert.equal(conversationRequests.length, 1, 'only the selected conversation is fetched');
for (const lang of ['ja', 'en']) {
await page.evaluate(lang => window.setLanguage(lang), lang);
for (const width of [320, 600, 900]) {
await page.setViewportSize({ width, height: 700 });
await page.evaluate(() => document.fonts.ready);
for (const kind of ['delegates', 'subtasks']) {
await page.getByTestId(`space-chat-tab-${kind}`).click();
await page.getByText('Readable result', { exact: true }).waitFor();
const panel = page.getByTestId(kind === 'delegates' ? 'delegate-conversations' : 'subtask-conversations');
assert.equal(await panel.getByRole('tablist').getAttribute('aria-orientation'), 'vertical');
if (kind === 'delegates') {
const tab = page.getByRole('tab', { name: /^Research 1 / });
await tab.click(); await tab.focus(); await page.keyboard.press('End');
assert.equal(await page.getByRole('tab', { name: /^Research 80 / }).getAttribute('aria-selected'), 'true');
await page.getByRole('searchbox').fill('Research 79');
assert.equal(await panel.getByRole('tab').count(), 1);
await page.getByRole('tab', { name: /^Research 79 / }).click();
await page.getByRole('searchbox').fill('');
assert.equal(await page.getByRole('tab', { name: /^Research 79 / }).getAttribute('aria-selected'), 'true');
}
await page.getByText('Readable result', { exact: true }).waitFor();
const thinking = page.getByRole('button', { name: lang === 'ja' ? '思考' : 'Thinking', exact: true });
await thinking.click();
await page.getByText('Recorded thinking details', { exact: true }).waitFor();
assert.equal(await page.getByText('1.8s', { exact: true }).count(), 1);
assert.equal(await page.getByText('Recorded tool result', { exact: true }).count(), 0);
await page.getByRole('button', { name: /Read.*sample/ }).click();
await page.getByText('Recorded tool result', { exact: true }).waitFor();
await page.getByRole('button', { name: lang === 'ja' ? '実行一覧を隠す' : 'Hide execution list' }).click();
assert.equal(await panel.getByRole('tablist').count(), 0);
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), `${lang}/${width}/${kind}: page overflow`);
await page.getByRole('button', { name: lang === 'ja' ? '実行一覧を表示' : 'Show execution list' }).click();
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), `${lang}/${width}/${kind}: list overflow`);
}
}
}
if (process.env.SCREENSHOT_PATH) await page.screenshot({ path: process.env.SCREENSHOT_PATH });
console.log('execution conversations: 12 language/width/kind cases, 80-run search, keyboard, thinking, tool failure and collapse passed');
} finally {
await browser?.close(); await vite?.close(); server?.close(); await unlink(fixture).catch(() => {});
}