v0.9.0 e5e4ded

web-visual-smoke.cjs RawHistoryFolder

1// Run against an isolated gateway. Supply PLAYWRIGHT_MODULE, VEV_BINARY and VEV_ENV.
2// The test creates sessions/panes; it never targets an existing named session.
3const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright');
4const { execFileSync } = require('node:child_process');
5const assert = require('node:assert/strict');
6
7(async () => {
8  if (!process.env.VEV_BINARY || !process.env.VEV_ENV) throw new Error('VEV_BINARY and isolated VEV_ENV are required');
9  const browser = await chromium.launch({
10    executablePath: process.env.CHROMIUM_PATH || undefined,
11    headless: true
12  });
13  try {
14    const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
15    const errors = [];
16    page.on('pageerror', error => errors.push(error.message));
17    const output = execFileSync(process.env.VEV_BINARY, ['--web-daemon'], { encoding: 'utf8' });
18    const token = output.match(/#token=([A-Za-z0-9_-]+)/)[1];
19    await page.goto(`http://127.0.0.1:8778/#token=${token}`);
20    const connected = () => page.waitForFunction(() => document.querySelector('#status').textContent.startsWith('Connected'));
21    const contains = text => page.waitForFunction(text => document.querySelector('.vev-terminal__accessible-output').textContent.includes(text), text);
22    await connected();
23    assert.equal(new URL(page.url()).hash, '');
24    await page.keyboard.type("printf '\\033[2J\\033[HDIMCHECK\\n'");
25    await page.keyboard.press('Enter');
26    await contains('DIMCHECK');
27    const markerColor = () => page.evaluate(() => {
28      const cell = [...document.querySelectorAll('.vev-terminal__cell')].find(cell => cell.textContent === 'D');
29      return cell && getComputedStyle(cell).color;
30    });
31    const normalColor = await markerColor();
32    assert.ok(normalColor);
33    await page.locator('#palette').click();
34    await contains('Commands');
35    assert.notEqual(await markerColor(), normalColor, 'palette backdrop uses theme dimming');
36    await page.keyboard.press('Escape');
37    await page.waitForFunction(() => !document.querySelector('.vev-terminal__accessible-output').textContent.includes('Commands'));
38    assert.equal(await markerColor(), normalColor, 'closing the palette restores the theme');
39    await page.locator('#palette').click();
40    await contains('Commands');
41    await page.keyboard.type('SPR');
42    await page.keyboard.press('Enter');
43    await contains('│');
44    await page.keyboard.type("printf '\\033[2J\\033[HRIGHT-PANE\\n'");
45    await page.keyboard.press('Enter');
46    await contains('RIGHT-PANE');
47    await page.keyboard.press('Alt+h');
48    await page.keyboard.type("printf '\\033[2J\\033[HLEFT-PANE\\n'");
49    await page.keyboard.press('Enter');
50    await contains('LEFT-PANE');
51    const bounds = await page.locator('#terminal').boundingBox();
52    await page.mouse.click(bounds.x + bounds.width * .75, bounds.y + 90);
53    await page.keyboard.type('MOUSE-CHECK');
54    await contains('MOUSE-CHECK');
55    await page.keyboard.press('Control+c');
56    await page.setViewportSize({ width: 960, height: 600 });
57    await page.waitForFunction(() => document.querySelectorAll('.vev-terminal__row').length < 40);
58    await page.locator('#terminal').evaluate(root => {
59      const clipboardData = new DataTransfer();
60      clipboardData.setData('text/plain', 'paste');
61      root.querySelector('textarea').dispatchEvent(new ClipboardEvent('paste', { clipboardData, bubbles: true, cancelable: true }));
62    });
63    await page.waitForFunction(() => {
64      const rows = [...document.querySelectorAll('.vev-terminal__row')];
65      const right = rows.map(row => {
66        const cells = [...row.children];
67        const divider = cells.findIndex(cell => cell.textContent === '│');
68        return cells.slice(divider + 1).map(cell => cell.textContent).join('').trimEnd();
69      }).join('');
70      return right.includes('paste');
71    });
72    await page.keyboard.press('Control+c');
73    await page.reload();
74    await connected();
75    assert.deepEqual(errors, []);
76    console.log('Web visual smoke passed: auth, palette, split, keyboard, mouse input, resize, paste, reload.');
77  } finally {
78    await browser.close();
79  }
80})().catch(error => { console.error(error); process.exitCode = 1; });

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close