oxedyne/daimond/dev/shot_agentpane.mjs
5.5 KiB, 1 run
created by r2519314175:139, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | // shot_agentpane.mjs — visual + behavioural check of the agents panel's new |
| 2 | // chips (parent Diamond, inherited tags, model) and its Diamonds-style search/filter. |
| 3 | // |
| 4 | // Drives the real UI: two tagged Diamonds, a small fan-out from each, then the search |
| 5 | // box and a Diamond-chip filter. Needs dev/serve.mjs (DAIMOND_PORT, default 8777) + |
| 6 | // dev/mockllm.mjs. |
| 7 | import { open, shot } from './harness.mjs'; |
| 8 | |
| 9 | const s = await open({ name: 'agentpane-shot', signIn: true, connect: true }); |
| 10 | const { page } = s; |
| 11 | const pause = (ms) => page.waitForTimeout(ms); |
| 12 | |
| 13 | async function newDiamond(name) { |
| 14 | await page.click('#new-diamond-btn', { force: true }); |
| 15 | await page.waitForSelector('.dlg-input', { timeout: 8000 }); |
| 16 | await page.fill('.dlg-input', name); |
| 17 | await page.click('.dlg-ok'); |
| 18 | await page.waitForSelector('#steer-input', { timeout: 10000 }); |
| 19 | await pause(300); |
| 20 | } |
| 21 | |
| 22 | async function addTags(tags) { |
| 23 | // The "# Tags" button on the Diamond command surface opens the tag editor. |
| 24 | await page.click('button.crystal-act:has-text("# Tags")', { force: true }); |
| 25 | await page.waitForSelector('.tag-input', { timeout: 6000 }); |
| 26 | for (const t of tags) { |
| 27 | await page.fill('.tag-input', t); |
| 28 | await page.click('button.crystal-act:has-text("+ Add")', { force: true }); |
| 29 | await pause(250); |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | async function steer(text) { |
| 34 | await page.fill('#steer-input', text); |
| 35 | await page.click('#steer-send'); |
| 36 | await pause(1200); |
| 37 | } |
| 38 | |
| 39 | // ── Diamond A: Backend audit, tagged, two workers ───────────────────────── |
| 40 | await newDiamond('Backend audit'); |
| 41 | await addTags(['rust', 'ledger']); |
| 42 | // Back to the steer surface by re-selecting the Diamond in the rail. |
| 43 | await page.click('.diamond-box:has-text("Backend audit")', { force: true }); |
| 44 | await page.waitForSelector('#steer-input', { timeout: 8000 }); |
| 45 | await steer('@tools spawn_agent {"name":"schema-check","task":"@text checked the schema"} ' |
| 46 | + ';; spawn_agent {"name":"ledger-audit","task":"@text audited the ledger"}'); |
| 47 | |
| 48 | // ── Diamond B: Frontend polish, tagged, two workers ─────────────────────── |
| 49 | await newDiamond('Frontend polish'); |
| 50 | await addTags(['css']); |
| 51 | await page.click('.diamond-box:has-text("Frontend polish")', { force: true }); |
| 52 | await page.waitForSelector('#steer-input', { timeout: 8000 }); |
| 53 | await steer('@tools spawn_agent {"name":"chip-render","task":"@text rendered the chips"} ' |
| 54 | + ';; spawn_agent {"name":"theme-sweep","task":"@text swept the themes"}'); |
| 55 | |
| 56 | // Let the workers finish, then force the panel to repaint with fresh tags. |
| 57 | await pause(2500); |
| 58 | await page.fill('#agent-search', ' '); |
| 59 | await page.fill('#agent-search', ''); |
| 60 | await pause(400); |
| 61 | |
| 62 | const summary = await page.evaluate(() => { |
| 63 | const cards = [...document.querySelectorAll('#agents-list .acard')]; |
| 64 | return { |
| 65 | count: cards.length, |
| 66 | tiles: cards.map(c => ({ |
| 67 | name: (c.querySelector('.an') || {}).textContent, |
| 68 | diamond: (c.querySelector('.diamond-chip') || {}).textContent, |
| 69 | tags: [...c.querySelectorAll('.tag-chip.tag-sm')].map(t => t.textContent), |
| 70 | model: (c.querySelector('.achip-model') || {}).textContent, |
| 71 | })), |
| 72 | }; |
| 73 | }); |
| 74 | console.log('TILES:', JSON.stringify(summary, null, 2)); |
| 75 | await shot(s, 'agentpane-all'); |
| 76 | |
| 77 | // ── Search filters by Diamond name ──────────────────────────────────────── |
| 78 | await page.fill('#agent-search', 'backend'); |
| 79 | await pause(400); |
| 80 | const afterSearch = await page.$$eval('#agents-list .acard', cs => cs.length); |
| 81 | const searchMiss = await page.$eval('#agents-list', el => el.textContent.includes('No agents match') ); |
| 82 | console.log('after search "backend": cards =', afterSearch); |
| 83 | await shot(s, 'agentpane-search-backend'); |
| 84 | await page.fill('#agent-search', ''); |
| 85 | await pause(300); |
| 86 | |
| 87 | // ── Clicking a Diamond chip filters to that Diamond, and shows a clear chip ── |
| 88 | await page.click('.acard .diamond-chip:has-text("Frontend polish")', { force: true }); |
| 89 | await pause(400); |
| 90 | const afterChip = await page.evaluate(() => ({ |
| 91 | cards: document.querySelectorAll('#agents-list .acard').length, |
| 92 | filterChip: (document.querySelector('#agent-filter .tag-chip') || {}).textContent || '(none)', |
| 93 | filterShown: document.getElementById('agent-filter').style.display !== 'none', |
| 94 | })); |
| 95 | console.log('after Diamond-chip filter:', JSON.stringify(afterChip)); |
| 96 | await shot(s, 'agentpane-filter-frontend'); |
| 97 | |
| 98 | // ── Clear the filter with the × ───────────────────────────────────────── |
| 99 | await page.click('#agent-filter .tag-x', { force: true }); |
| 100 | await pause(400); |
| 101 | const cleared = await page.$$eval('#agents-list .acard', cs => cs.length); |
| 102 | console.log('after clearing filter: cards =', cleared); |
| 103 | |
| 104 | // ── Verdicts ──────────────────────────────────────────────────────────── |
| 105 | // The gateway is not run in this harness, so /api calls 502 by design; those |
| 106 | // are not the pane's errors. Anything else is. |
| 107 | const realErrs = s.errs.filter(e => !/502|Bad Gateway|\/api\b/.test(e)); |
| 108 | const ok = summary.count === 4 |
| 109 | && summary.tiles.every(t => t.focus && t.model) |
| 110 | && summary.tiles.some(t => t.tags.length > 0) |
| 111 | && afterSearch === 2 && !searchMiss |
| 112 | && afterChip.cards === 2 && afterChip.filterShown |
| 113 | && cleared === 4 |
| 114 | && realErrs.length === 0; |
| 115 | console.log(ok ? '\n✅ PASS — chips, search and Diamond filter all work' : '\n❌ FAIL — see above'); |
| 116 | if (realErrs.length) console.log('PAGE ERRORS:', realErrs); |
| 117 | await s.close(); |
| 118 | process.exit(ok ? 0 : 1); |