A tap on a device in the simulation now only chooses it; its three-dot button (or a long press) opens what Java's click opened: the desktop, and newly a switch's SAT table and a home router's NAT table, beside each card's data exchange. The desktop, the tables and the data exchange are no windows in the window any more but the simulation's sheet (SimulationSheet), after the dashboard's AppSheet: below the toolbar to the bottom, a head with icon, title, who else has it open, see-through and close. One at a time — it covers the surface. It stays over a return to the design with the program open on a desktop; a sheet whose device was removed closes. See-through fades the body (0.55) and keeps the head; the choice is kept in the browser. Java opens these as movable windows, several at once, with a click; named in CHANGES.md. Tests open through the menu (showFromMenu, showDesktop in editorPage.ts). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
110 lines
6.4 KiB
TypeScript
110 lines
6.4 KiB
TypeScript
// The viewers of a running simulation and "Pakete verwerfen" in Chromium
|
|
// (F4.6g5), on FILIUS' example nat_with_mapped_port: a tap on a home router
|
|
// shows its NAT table (the port forwarding's entry, then a ping's), a tap on
|
|
// a switch its table (the addresses a ping taught it), each reset; and
|
|
// "Pakete verwerfen" held while a ping runs: the ping loses answers, the
|
|
// list shows the dropped frames in Java's words.
|
|
|
|
import { expect, test, type Locator, type Page } from '@playwright/test';
|
|
import { drawing, openEditor, serveEditor, showDesktop, showFromMenu } from './editorPage.js';
|
|
|
|
let server: { readonly base: string; readonly close: () => Promise<void> };
|
|
|
|
test.beforeAll(async () => {
|
|
server = await serveEditor();
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
await server.close();
|
|
});
|
|
|
|
async function install(desktop: Locator, program: string) {
|
|
await desktop.locator('[data-program="installation"]').click();
|
|
const installation = desktop.locator('[data-program-window="installation"]');
|
|
await installation.locator('[data-list="available"] [role="option"]').filter({ hasText: program }).click();
|
|
await installation.getByRole('button', { name: 'Installieren' }).click();
|
|
await installation.getByRole('button', { name: 'Änderungen annehmen' }).click();
|
|
}
|
|
|
|
test('shows the tables of a home router and a switch, and drops while “Pakete verwerfen” is held', async ({ page }) => {
|
|
test.setTimeout(120_000);
|
|
await openEditor(page, server.base, 'nat_with_mapped_port.fls');
|
|
const nodes = (await drawing(page)).nodes;
|
|
const idOf = (name: string) => nodes.find((node) => node.name === name)!.id;
|
|
await page.getByRole('button', { name: 'Wechselt in den Aktionsmodus' }).click();
|
|
await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'simulation');
|
|
|
|
// The home router with the port forwarding: its entry, with Java's columns.
|
|
const gateway = idOf('Heimrouter Vincent');
|
|
await showFromMenu(page, gateway, 'NAT-Tabelle anzeigen');
|
|
const nat = page.locator(`section[data-viewer="${gateway}"]`);
|
|
await expect(nat.locator('h2')).toHaveText('NAT-Tabelle Heimrouter Vincent');
|
|
await expect(nat.locator('th')).toHaveText(['Protokoll', 'Portfreigabe', 'WAN-Adresse', 'LAN-Adresse', 'LAN-Port', 'Letzte Aktualisierung']);
|
|
await expect(nat.locator('tbody tr')).toHaveCount(1);
|
|
await expect(nat.locator('tbody tr').first().locator('td')).toHaveText(['TCP', '80', '*', '192.168.0.10', '80', '-']);
|
|
await nat.locator('.simulation-sheet__close').click();
|
|
|
|
// The web server behind it pings out.
|
|
const web = idOf('Webserver - 192.168.0.10');
|
|
await showDesktop(page, web);
|
|
const desktop = page.locator(`section[data-desktop="${web}"]`);
|
|
await install(desktop, 'Befehlszeile');
|
|
await desktop.locator('[data-program="commandPrompt"]').click();
|
|
const input = desktop.locator('input[name="terminalInput"]');
|
|
await input.fill('ping 141.99.4.10');
|
|
await input.press('Enter');
|
|
await expect(desktop.locator('.terminal-text')).toContainText('4 Paket(e) empfangen', { timeout: 30_000 });
|
|
await desktop.locator('.simulation-sheet__close').click();
|
|
|
|
await showFromMenu(page, gateway, 'NAT-Tabelle anzeigen');
|
|
await expect(nat.locator('tbody tr')).toHaveCount(2);
|
|
await expect(nat.locator('tbody tr').nth(1).locator('td').nth(0)).toHaveText('ICMP');
|
|
await expect(nat.locator('tbody tr').nth(1).locator('td').nth(5)).toHaveText(/^\d\d:\d\d:\d\d$/);
|
|
await nat.getByRole('button', { name: 'Tabelle zurücksetzen' }).click();
|
|
await expect(nat.locator('tbody tr')).toHaveCount(1);
|
|
await nat.locator('.simulation-sheet__close').click();
|
|
|
|
// The switch the web server hangs on: the addresses the ping taught it.
|
|
const link = (await drawing(page)).links.find((each) => each.sourceNode === web || each.targetNode === web)!;
|
|
const lanSwitch = link.sourceNode === web ? link.targetNode : link.sourceNode;
|
|
await showFromMenu(page, lanSwitch, 'SAT Tabelle anzeigen');
|
|
const sat = page.locator(`section[data-viewer="${lanSwitch}"]`);
|
|
await expect(sat.locator('th')).toHaveText(['MAC', 'Port', 'Letzte Aktualisierung']);
|
|
await expect(sat.locator('tbody tr').first().locator('td')).toHaveText([/^[0-9A-F:]{17}$/, /^Port \d+$/, /^\d\d:\d\d:\d\d$/]);
|
|
await sat.getByRole('button', { name: 'Tabelle löschen' }).click();
|
|
await expect(sat.locator('tbody tr')).toHaveCount(0);
|
|
await sat.locator('.simulation-sheet__close').click();
|
|
|
|
// "Pakete verwerfen" held while a ping runs: answers lost, the frames listed.
|
|
await showDesktop(page, web);
|
|
await desktop.locator('[data-program="commandPrompt"]').click();
|
|
await input.fill('ping 141.99.4.10');
|
|
await input.press('Enter');
|
|
const hold = page.getByRole('button', { name: 'Pakete verwerfen' });
|
|
const box = (await hold.boundingBox())!;
|
|
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
|
await page.mouse.down();
|
|
await expect(hold).toHaveAttribute('aria-pressed', 'true');
|
|
await page.waitForTimeout(2_500);
|
|
await page.mouse.up();
|
|
await expect(hold).toHaveAttribute('aria-pressed', 'false');
|
|
// The second ping's statistics: an answer lost.
|
|
await expect(desktop.locator('.terminal-text')).toContainText(/Paketstatistik ---[^]*Paketstatistik ---\n4 Paket\(e\) gesendet, [0-3] Paket\(e\) empfangen/, { timeout: 30_000 });
|
|
await expect(desktop.locator('.terminal-text')).toContainText('-- Timeout!');
|
|
const toggle = page.getByRole('button', { name: /^Verworfen: [1-9]/ });
|
|
await toggle.click();
|
|
await expect(page.getByLabel('Verworfene Pakete')).toContainText(/\d\d:\d\d:\d\d\.\d\d\d : \[src=[0-9A-F:]+, dest=[0-9A-F:]+, type=0x8/);
|
|
});
|
|
|
|
test('keeps “Pakete verwerfen” in reach on a phone', async ({ page }) => {
|
|
await page.setViewportSize({ width: 360, height: 740 });
|
|
await openEditor(page, server.base, 'nat_with_mapped_port.fls');
|
|
await page.getByRole('button', { name: 'Wechselt in den Aktionsmodus' }).click();
|
|
await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'simulation');
|
|
await expect(page.getByRole('button', { name: 'Pakete verwerfen' })).toBeInViewport({ ratio: 1 });
|
|
await expect(page.getByRole('button', { name: /^Verworfen: / })).toBeInViewport({ ratio: 1 });
|
|
// Every part of the toolbar within the screen (the dev page's own fields above the editor are wider; they are not the editor's).
|
|
const rights = await page.locator('.editor-toolbar > *').evaluateAll((parts) => parts.map((part) => part.getBoundingClientRect().right));
|
|
expect(Math.max(...rights)).toBeLessThanOrEqual(360);
|
|
});
|