// Optional browser integration: real API/job persistence, simulated guest commands. const assert = require('node:assert/strict'); const { spawn } = require('node:child_process'); const { mkdtemp, rm } = require('node:fs/promises'); const os = require('node:os'); const path = require('node:path'); const { chromium } = require('playwright'); (async () => { const temporary = await mkdtemp(path.join(os.tmpdir(), 'nest-tool-browser-')); const port = process.env.NEST_BROWSER_TEST_PORT || '18766'; const origin = `http://127.0.0.1:${port}`; const server = spawn(process.env.NEST_TEST_PYTHON || 'python3', ['-B', '-m', 'tests.browser_tool_server', '--base', temporary, '--port', port], { cwd: path.resolve(__dirname, '..'), stdio: ['ignore', 'pipe', 'pipe'] }); let logs = ''; server.stdout.on('data', (data) => { logs = (logs + data).slice(-16000); }); server.stderr.on('data', (data) => { logs = (logs + data).slice(-16000); }); let browser; let page; try { let ready = false; for (let attempt = 0; attempt < 100; attempt++) { if (server.exitCode !== null) throw new Error(`Fixture server exited: ${logs}`); try { if ((await fetch(`${origin}/health`)).ok) { ready = true; break; } } catch {} await new Promise((resolve) => setTimeout(resolve, 100)); } assert.ok(ready, logs); browser = await chromium.launch({ headless: true }); page = await browser.newPage({ viewport: { width: 1440, height: 1100 } }); const errors = []; page.on('pageerror', (error) => errors.push(error.message)); const installed = (key) => page.waitForFunction((value) => document.querySelector(`[data-install-tool="${value}"]`)?.textContent === 'Installed', key); await page.goto(`${origin}/dashboard#create`); await page.locator('#token-input').fill('browser-test-token'); await page.locator('#auth-btn').click(); await page.locator('[data-create-tool="python-3.12"]').check(); await page.locator('[data-create-tool="git"]').check(); await page.locator('[data-create-tool="nodejs"]').check(); assert.equal(await page.locator('[data-create-tool]').count(), 12); assert.match(await page.locator('#create-summary-details').textContent(), /Git, Node.js, Python 3.12/); const layout = await page.evaluate(() => ({ parent: document.querySelector('.create-tools-section').parentElement.className, children: document.querySelector('#create-form').children.length, })); assert.equal(layout.parent, 'create-fields'); assert.equal(layout.children, 2); await page.locator('#create-name').fill('tool-workspace'); await page.locator('#create-submit').click(); await page.waitForURL((url) => /#vm\/vm-[a-f0-9]+\/tools$/.test(url.hash)); const createdUrl = page.url(); await page.locator('#tools-job').waitFor({ state: 'visible' }); // A reload can occur while the server-owned worker is still installing. await page.reload(); await page.waitForFunction(() => document.querySelector('#tools-job-title').textContent === 'Complete'); await installed('curl'); assert.equal(await page.locator('.tool-installed').count(), 4); // Includes image-provided curl. assert.ok(await page.locator('[data-install-tool="python-3.12"]').isDisabled()); await page.locator('#tools-job-items details').first().locator('summary').click(); assert.match(await page.locator('#tools-job-items').textContent(), /Verified and installed git/); await page.locator('[data-install-tool="php"]').click(); await page.waitForFunction(() => document.querySelector('#tools-job-title').textContent === 'Failed'); await page.locator('#tools-job-items details[data-output="php"] summary').click(); assert.match(await page.locator('.tool-output').textContent(), /