|
1 | | -import { test, expect } from '@playwright/test'; |
2 | | -import { existsSync } from 'node:fs'; |
| 1 | +import { test, expect } from 'playwright/test'; |
| 2 | +import { existsSync, readFileSync } from 'node:fs'; |
3 | 3 |
|
4 | 4 | const chromeCandidates = [ |
5 | 5 | process.env.CHROME_BIN, |
@@ -70,3 +70,43 @@ test('gui can import card and send direct message', async ({ page }) => { |
70 | 70 | return !!msgs && msgs.textContent.includes(message); |
71 | 71 | }, message); |
72 | 72 | }); |
| 73 | + |
| 74 | +// No daemon: run the real renderers in a browser with inert startup IO. |
| 75 | +test('agent names preserve labels, update peer names and escape HTML', async ({ page }) => { |
| 76 | + await page.route('**/*', route => route.abort()); |
| 77 | + const html = readFileSync(new URL('../../src/gui/x0x-gui.html', import.meta.url), 'utf8'); |
| 78 | + const script = html.match(/<script>([\s\S]*?)<\/script>/)[1]; |
| 79 | + // Keep every real function; omit only the startup calls and interval timers. |
| 80 | + await page.setContent('<table><tbody id="h-agents"></tbody></table><div id="rows"></div><div id="detail"></div>'); |
| 81 | + await page.addScriptTag({ content: script.split('// Apply theme on startup')[0] }); |
| 82 | + const agentId = 'a1'.repeat(32); |
| 83 | + async function render(label, selfName) { |
| 84 | + await page.evaluate(async ({ agentId, label, selfName }) => { |
| 85 | + const contact = { agent_id: agentId, label, machines: [], trust_level: 'Known' }; |
| 86 | + const discovered = { agent_id: agentId, self_name: selfName }; |
| 87 | + S.set('contacts', [contact]); |
| 88 | + api = async path => path === '/agents/discovered' ? {agents:[discovered]} : null; |
| 89 | + refreshAgentIdentity = async () => {}; |
| 90 | + refreshUpgradeBanner = async () => {}; |
| 91 | + await pollDash(); |
| 92 | + document.getElementById('rows').innerHTML = renderAgentRow({...contact, discovered}); |
| 93 | + renderAgentDetail(document.getElementById('detail'), {agentId, contact, disc:discovered}); |
| 94 | + }, {agentId, label, selfName}); |
| 95 | + } |
| 96 | + for (const [label, peer, expected] of [ |
| 97 | + ['', 'Remote name', 'Remote name'], |
| 98 | + ['My label', 'Remote name', 'My label'], |
| 99 | + ['', 'Updated name', 'Updated name'], |
| 100 | + ['', '', agentId.slice(0,10)+'…'], |
| 101 | + ['', '<img src=x onerror="window.nameInjected=true">', '<img src=x onerror="window.nameInjected=true">'], |
| 102 | + ['<svg onload="window.nameInjected=true">', 'Peer', '<svg onload="window.nameInjected=true">'], |
| 103 | + ]) { |
| 104 | + await render(label, peer); |
| 105 | + for (const selector of ['#h-agents', '#rows', '#detail']) { |
| 106 | + await expect(page.locator(selector)).toContainText(expected); |
| 107 | + await expect(page.locator(selector)).toContainText(agentId.slice(0,10)); |
| 108 | + await expect(page.locator(selector+' img, '+selector+' svg')).toHaveCount(0); |
| 109 | + } |
| 110 | + expect(await page.evaluate(() => window.nameInjected)).toBeUndefined(); |
| 111 | + } |
| 112 | +}); |
0 commit comments