Files

69 lines
3.7 KiB
JavaScript

const assert = require('node:assert/strict');
const { chromium, expect } = require('@playwright/test');
(async () => {
const base = new URL(process.argv[2]);
const sessionId = process.argv[3];
assert.equal(base.hostname, '127.0.0.1');
assert.match(sessionId, /^s-[a-f0-9]{32}$/);
const browser = await chromium.launch({
executablePath: process.env.CHROMIUM_EXECUTABLE,
args: ['--no-sandbox', '--disable-dev-shm-usage'],
});
try {
const page = await browser.newPage();
const errors = [];
page.on('pageerror', error => errors.push(error.message));
await page.route('**/*', route => new URL(route.request().url()).origin === base.origin ? route.continue() : route.abort());
await page.addInitScript(() => {
const NativeWebSocket = window.WebSocket;
window.fixtureSockets = [];
window.WebSocket = class extends NativeWebSocket {
constructor(...args) { super(...args); window.fixtureSockets.push(this); }
};
});
const text = () => page.evaluate(() => {
const buffer = window.fixtureTerminal?.buffer.active;
return buffer ? Array.from({ length: buffer.length }, (_, i) => buffer.getLine(i)?.translateToString()).join('\n') : '';
});
for (const width of [1440, 390]) {
await page.setViewportSize({ width, height: 844 });
await page.goto(base.href);
await page.getByRole('button', { name: 'Open terminal', exact: true }).click();
await expect.poll(text).toContain('D09-LINE-0000');
await expect.poll(text).toContain('D09-LINE-0099');
await expect(page.getByRole('combobox', { name: 'Terminal session' })).toHaveValue(sessionId);
await page.locator('.xterm-helper-textarea').focus();
// No contiguous expected marker in the echoed command: prove execution.
await page.keyboard.type(`printf 'NATIVE-%s-INPUT\\n' '${width}'`);
await page.keyboard.press('Enter');
await expect.poll(text).toContain(`NATIVE-${width}-INPUT`);
const before = await page.evaluate(() => window.fixtureSockets.length);
await page.evaluate(() => window.fixtureSockets.at(-1).close());
await expect.poll(() => page.evaluate(() => window.fixtureSockets.length), { timeout: 8000 }).toBe(before + 1);
await expect.poll(() => page.evaluate(() => window.fixtureSockets.at(-1).readyState)).toBe(1);
// A retained old buffer must not make a failed reconnect look successful.
await page.locator('.xterm-helper-textarea').focus();
await page.keyboard.type(`printf 'REJOIN-%s-DONE\\n' '${width}'`);
await page.keyboard.press('Enter');
await expect.poll(text).toContain(`REJOIN-${width}-DONE`);
assert.equal((await text()).split('D09-LINE-0000').length - 1, 1);
await page.evaluate(() => window.fixtureTerminal.scrollToTop());
await expect(page.locator('.xterm-rows')).toContainText('D09-LINE-0000');
await page.screenshot({ path: `/out/actual-terminal-history-${width}.png` });
await page.getByRole('button', { name: 'Close', exact: true }).click();
const sessions = await page.evaluate(async () => {
const response = await fetch('/api/terminal/sessions');
if (!response.ok) throw new Error(`session inventory failed: ${response.status}`);
return response.json();
});
assert.equal(sessions.length, 1, 'browser reconnect must not create another session');
assert.equal(sessions[0].id, sessionId);
}
assert.deepEqual(errors, []);
console.log('PASS real CLIPopup/xterm -> actual authenticated handler -> private tmux: typed command, history, same-session WebSocket reconnect and visible1440/390 history; synthetic owner bootstrap only');
} finally {
await browser.close();
}
})().catch(error => { console.error(error); process.exitCode = 1; });