102 lines
5.2 KiB
JavaScript
102 lines
5.2 KiB
JavaScript
// Optional integration test: requires Playwright + Chromium and the project's test extra.
|
|
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-browser-'));
|
|
const port = process.env.NEST_BROWSER_TEST_PORT || '18765';
|
|
const origin = `http://127.0.0.1:${port}`;
|
|
const server = spawn(process.env.NEST_TEST_PYTHON || 'python3',
|
|
['-B', '-m', 'tests.browser_session_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 {
|
|
const response = await fetch(`${origin}/health`);
|
|
if (response.ok) { ready = true; break; }
|
|
} catch {}
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
}
|
|
assert.ok(ready, `Fixture server did not start: ${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 connected = () => page.waitForFunction(() => document.querySelector('#session-status').textContent === 'Connected');
|
|
const output = (value) => page.waitForFunction((text) => document.querySelector('#session-terminal').textContent.includes(text), value);
|
|
await page.goto(`${origin}/dashboard#vm/vm-test`);
|
|
await page.locator('#token-input').fill('browser-test-token');
|
|
await page.locator('#auth-btn').click();
|
|
await page.locator('#detail-actions [data-action="launch-session"]').click();
|
|
await connected();
|
|
const firstUrl = page.url();
|
|
assert.match(firstUrl, /#vm\/vm-test\/sessions\/session-/);
|
|
await page.keyboard.type('echo alpha');
|
|
await page.keyboard.press('Enter');
|
|
await output('result:echo alpha');
|
|
|
|
await page.reload();
|
|
await connected();
|
|
await output('result:echo alpha');
|
|
assert.equal(await page.locator('.session-item').count(), 1);
|
|
|
|
await page.locator('#detail-actions [data-action="launch-session"]').click();
|
|
await page.waitForURL((url) => url.href !== firstUrl);
|
|
await connected();
|
|
assert.equal(await page.locator('.session-item').count(), 2);
|
|
assert.ok(!(await page.locator('#session-terminal').textContent()).includes('result:echo alpha'));
|
|
await page.keyboard.type('echo beta');
|
|
await page.keyboard.press('Enter');
|
|
await output('result:echo beta');
|
|
await page.locator('.session-item').filter({ hasText: 'Session 1' }).click();
|
|
await connected();
|
|
await output('result:echo alpha');
|
|
assert.ok(!(await page.locator('#session-terminal').textContent()).includes('result:echo beta'));
|
|
|
|
await page.goto(`${origin}/dashboard#vm/vm-other/sessions`);
|
|
await page.locator('#vm-sessions-panel').waitFor({ state: 'visible' });
|
|
await page.waitForFunction(() => document.querySelector('#vm-session-count').textContent === '0');
|
|
assert.equal(await page.locator('.session-item').count(), 0);
|
|
assert.ok(await page.locator('#detail-actions [data-action="launch-session"]').isDisabled());
|
|
assert.ok(!(await page.locator('#session-terminal').textContent()).includes('alpha'));
|
|
|
|
await page.goto(firstUrl);
|
|
await connected();
|
|
await output('result:echo alpha');
|
|
await page.locator('#session-end').click();
|
|
await page.locator('#confirm-ok').click();
|
|
await page.waitForFunction(() => document.querySelector('#session-status').textContent.startsWith('closed'));
|
|
await page.reload();
|
|
await output('result:echo alpha');
|
|
assert.ok(await page.locator('#session-end').isDisabled());
|
|
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.waitForTimeout(300);
|
|
const fits = await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth);
|
|
assert.ok(fits, 'mobile layout overflows the viewport');
|
|
assert.deepEqual(errors, []);
|
|
if (process.env.NEST_BROWSER_SCREENSHOT) await page.screenshot({ path: process.env.NEST_BROWSER_SCREENSHOT, fullPage: true });
|
|
console.log('Browser sessions passed: launch, terminal input, reload/reconnect, multiple sessions, VM isolation, closed history, and mobile layout.');
|
|
} catch (error) {
|
|
if (page) console.error('Browser state:', await page.locator('#sessions-error').textContent(), await page.locator('#session-status').textContent());
|
|
console.error(logs);
|
|
throw error;
|
|
} finally {
|
|
await browser?.close();
|
|
server.kill('SIGTERM');
|
|
if (server.exitCode === null) await new Promise((resolve) => server.once('exit', resolve));
|
|
await rm(temporary, { recursive: true, force: true });
|
|
}
|
|
})().catch((error) => { console.error(error); process.exitCode = 1; });
|