feat(editor): F4.6b the command line on a computer's desktop
A new oracle, TerminalWindowRun, types into Java's own command line window: the window writes each line after its prompt, takes a blank line, cls, reset and exit itself, keeps the lines for the arrow keys, and keeps its text, history and working directory over a stop and a start. The room now holds a command line window per computer for everyone (TerminalWindow), kept over stops; the protocol carries its lines and the one way to read them (terminalScreen); the core goes on in the working directory of the run before (continueIn). The desktop opens programs in Java's frame, and the command line works with keyboard and finger: grey on black, the prompt hidden while a command runs, history, chips for the commands, Ctrl+C. The room's failure is now tested in the browser. Fixed: the room handed the engine the wall clock with fractions of a millisecond, and a ping hung after its first timeout. The engine and the room count whole milliseconds, as Java's clock does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -237,6 +237,121 @@ test.describe('on a phone', () => {
|
||||
const home = (await window.getByRole('button', { name: 'Startbildschirm' }).boundingBox())!;
|
||||
expect(home.height).toBeGreaterThanOrEqual(44);
|
||||
});
|
||||
|
||||
test("offers the command line's keys as buttons for a finger, and types without zooming", async ({ page }) => {
|
||||
await openEditor(page, server.base, 'internetwork_komplett.fls');
|
||||
await simulationButton(page).tap();
|
||||
await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'simulation');
|
||||
await page.getByRole('button', { name: 'Alles zeigen' }).tap();
|
||||
const id = await deviceNamed(page, 'Notebook 1.10');
|
||||
const at = await onDevice(page, id);
|
||||
await page.touchscreen.tap(at.x, at.y);
|
||||
await desktop(page, id).locator('[data-program="commandPrompt"]').tap();
|
||||
const window = desktop(page, id).locator('[data-program-window="commandPrompt"]');
|
||||
for (const name of ['ipconfig', 'Vorheriger Befehl', 'Befehl abbrechen (Strg+C)']) {
|
||||
const box = (await window.getByRole('button', { name, exact: true }).boundingBox())!;
|
||||
expect(box.height, name).toBeGreaterThanOrEqual(44);
|
||||
}
|
||||
await expect(window.locator('input[name="terminalInput"]')).toHaveCSS('font-size', '16px');
|
||||
await window.getByRole('button', { name: 'ipconfig', exact: true }).tap();
|
||||
await window.locator('input[name="terminalInput"]').press('Enter');
|
||||
await expect(window.locator('.terminal-text')).toContainText('IP Adresse . . . : 141.99.1.10');
|
||||
});
|
||||
});
|
||||
|
||||
/** Opens a computer's command line on its desktop. */
|
||||
async function commandLine(page: Page, name: string) {
|
||||
const id = await deviceNamed(page, name);
|
||||
await tap(page, id);
|
||||
await desktop(page, id).locator('[data-program="commandPrompt"]').click();
|
||||
const window = desktop(page, id).locator('[data-program-window="commandPrompt"]');
|
||||
await expect(window.getByRole('heading')).toHaveText('Befehlszeile');
|
||||
return { id, window, input: window.locator('input[name="terminalInput"]'), text: window.locator('.terminal-text'), prompt: window.locator('.terminal-prompt') };
|
||||
}
|
||||
|
||||
test("types into a computer's command line as into Java's: the start screen, a line after its prompt, the history, a ping across the routers", async ({ page }) => {
|
||||
test.setTimeout(60_000);
|
||||
await openEditor(page, server.base, 'internetwork_komplett.fls');
|
||||
await start(page);
|
||||
const { window, input, text, prompt } = await commandLine(page, 'Notebook 1.10');
|
||||
await expect(text).toContainText("Verwende den Befehl 'help'");
|
||||
await expect(prompt).toHaveText('/>');
|
||||
await expect(input).toBeFocused();
|
||||
await input.fill('ipconfig');
|
||||
await input.press('Enter');
|
||||
await expect(text).toContainText('\n/> ipconfig\nIP Adresse . . . : 141.99.1.10');
|
||||
await expect(input).toHaveValue('');
|
||||
await input.press('ArrowUp');
|
||||
await expect(input).toHaveValue('ipconfig');
|
||||
await input.press('ArrowDown');
|
||||
await expect(input).toHaveValue('');
|
||||
// A chip puts its command into the line.
|
||||
await window.getByRole('button', { name: 'ping', exact: true }).click();
|
||||
await expect(input).toHaveValue('ping ');
|
||||
await input.pressSequentially('141.99.5.10');
|
||||
await input.press('Enter');
|
||||
// While the ping runs, Java hides the prompt.
|
||||
await expect(prompt).toHaveCSS('visibility', 'hidden');
|
||||
await expect(text).toContainText('4 Paket(e) gesendet, 4 Paket(e) empfangen', { timeout: 20_000 });
|
||||
await expect(prompt).toHaveCSS('visibility', 'visible');
|
||||
await window.getByRole('button', { name: 'Vorheriger Befehl' }).click();
|
||||
await expect(input).toHaveValue('ping 141.99.5.10');
|
||||
});
|
||||
|
||||
test('ends a command with Ctrl+C, from the keyboard and from its button', async ({ page }) => {
|
||||
test.setTimeout(60_000);
|
||||
await openEditor(page, server.base, 'internetwork_komplett.fls');
|
||||
await start(page);
|
||||
const { window, input, text, prompt } = await commandLine(page, 'Notebook 1.10');
|
||||
await input.fill('ping 141.99.9.9');
|
||||
await input.press('Enter');
|
||||
await expect(prompt).toHaveCSS('visibility', 'hidden');
|
||||
await input.press('Control+c');
|
||||
await expect(prompt).toHaveCSS('visibility', 'visible', { timeout: 20_000 });
|
||||
await expect(text).toContainText('Paketstatistik');
|
||||
await input.fill('ping 141.99.9.9');
|
||||
await input.press('Enter');
|
||||
await expect(prompt).toHaveCSS('visibility', 'hidden');
|
||||
await window.getByRole('button', { name: 'Befehl abbrechen (Strg+C)' }).click();
|
||||
await expect(prompt).toHaveCSS('visibility', 'visible', { timeout: 20_000 });
|
||||
});
|
||||
|
||||
test('keeps the command line over leaving it, over a return to the design, and exit closes it', async ({ page }) => {
|
||||
await openEditor(page, server.base, 'internetwork_komplett.fls');
|
||||
await start(page);
|
||||
const { id, window, input, text } = await commandLine(page, 'Notebook 1.10');
|
||||
await input.fill('pwd');
|
||||
await input.press('Enter');
|
||||
await expect(text).toContainText('\n/> pwd\n/');
|
||||
await desktop(page, id).getByRole('button', { name: 'Startbildschirm' }).click();
|
||||
await expect(window).toHaveCount(0);
|
||||
await desktop(page, id).locator('[data-program="commandPrompt"]').click();
|
||||
await expect(text).toContainText('\n/> pwd\n/');
|
||||
await designButton(page).click();
|
||||
await start(page);
|
||||
await expect(text).toContainText('\n/> pwd\n/');
|
||||
await input.fill('exit');
|
||||
await input.press('Enter');
|
||||
await expect(window).toHaveCount(0);
|
||||
await expect(desktop(page, id).locator('.desktop-icon').first()).toBeVisible();
|
||||
await desktop(page, id).locator('[data-program="commandPrompt"]').click();
|
||||
await expect(text).toContainText('\n/> exit\n');
|
||||
// An exit that was there when the window opened does not close it again: it takes the next line.
|
||||
await input.fill('ls');
|
||||
await input.press('Enter');
|
||||
await expect(text).toContainText('\n/> exit\n\n/> ls\n');
|
||||
await expect(window).toBeVisible();
|
||||
});
|
||||
|
||||
test('says so when the simulation fails, and goes back to the design', async ({ page }) => {
|
||||
// FILIUS' modem example: the core has no modems yet (remote links); a ping to the other side fails it.
|
||||
await openEditor(page, server.base, 'modem_beispiel.fls');
|
||||
await start(page);
|
||||
const { input } = await commandLine(page, 'Notebook 0.10');
|
||||
await input.fill('ping 192.168.0.11');
|
||||
await input.press('Enter');
|
||||
await expect(page.getByRole('alert')).toHaveText('Die Simulation ist auf einen Fehler gestoßen und wurde beendet.', { timeout: 20_000 });
|
||||
await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'design');
|
||||
});
|
||||
|
||||
test('speaks English when asked', async ({ page }) => {
|
||||
|
||||
Reference in New Issue
Block a user