feat(editor): F4.5c the DHCP server and the port forwarding in the device sheet
Measured first: SheetRun now types into FILIUS' DHCP dialog and keeps rows
in its port forwarding dialog. Java's DHCP server keeps its bounds as typed
(".0.0.1" too), never takes an empty gateway or DNS server, hands out the
device's gateway and DNS server unless its own (a home router always its
LAN address, where "manual settings" do nothing), drops a second static
assignment of a MAC address silently and lets "_" through its MAC pattern;
the port forwarding dialog keeps any row, its firmware forwards only the
valid ones. Here a value passes its rule or is not taken, with a word why.
- doc: DHCP server addresses and flags, static assignments (a MAC address
once, ignoring case), port forwarding rows (TCP or UDP, musterPort, the
LAN address); the sheet shows what the server hands out and which rows
Java's firmware forwards (the core's parsePortForwardingRow, exported)
- editor: Java's dialogs as pages of the sheet with a back button, the
three row tables on one RowsEditor (the forwarding table rebuilt on it);
a sheet per device, so an open page closes when another device is chosen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
// beside the surface on a wide screen, below it on a phone.
|
||||
|
||||
import { expect, test, type Page } from '@playwright/test';
|
||||
import type { HostSheet, RouterSheet } from '@mdg-filius/doc';
|
||||
import type { HomeRouterSheet, HostSheet, RouterSheet } from '@mdg-filius/doc';
|
||||
import { drawing, onDevice, openEditor, serveEditor, sheetOf } from './editorPage.js';
|
||||
|
||||
let server: { readonly base: string; readonly close: () => Promise<void> };
|
||||
@@ -193,7 +193,7 @@ test("edits a router's forwarding table: a new row once its four cells pass, a c
|
||||
// Java's own rows: two addresses, two networks (the same twice: both cards are new), localhost.
|
||||
await expect(rows).toHaveCount(5);
|
||||
await sheet(page).getByRole('button', { name: 'Neuer Eintrag' }).click();
|
||||
await expect(sheet(page).getByText('Die Zeile wird übernommen, sobald alle vier Felder gültig sind.')).toBeVisible();
|
||||
await expect(sheet(page).getByText('Die Zeile wird übernommen, sobald alle Felder gültig sind.')).toBeVisible();
|
||||
await expect(field(page, 'route-destinationNetwork')).toHaveAttribute('aria-invalid', 'false');
|
||||
for (const [column, text] of [
|
||||
['destinationNetwork', '10.0.0.0'],
|
||||
@@ -206,7 +206,9 @@ test("edits a router's forwarding table: a new row once its four cells pass, a c
|
||||
expect(((await sheetOf(page, id)) as RouterSheet).routes).toEqual([]);
|
||||
await field(page, 'route-interfaceIPAddress').fill('192.168.0.10');
|
||||
await field(page, 'route-interfaceIPAddress').press('Enter');
|
||||
await expect.poll(async () => ((await sheetOf(page, id)) as RouterSheet).routes).toEqual([{ destinationNetwork: '10.0.0.0', subnetMask: '255.0.0.0', gateway: '192.168.0.1', interfaceIPAddress: '192.168.0.10' }]);
|
||||
await expect
|
||||
.poll(async () => ((await sheetOf(page, id)) as RouterSheet).routes)
|
||||
.toEqual([{ destinationNetwork: '10.0.0.0', subnetMask: '255.0.0.0', gateway: '192.168.0.1', interfaceIPAddress: '192.168.0.10' }]);
|
||||
await expect(sheet(page).locator('tr[data-manual="0"]')).toHaveAttribute('aria-selected', 'true');
|
||||
await field(page, 'route-gateway').fill('192.168.0.2');
|
||||
await field(page, 'route-gateway').press('Enter');
|
||||
@@ -250,6 +252,102 @@ test("shows a home router's LAN and WAN tabs, DHCP closing the WAN card's addres
|
||||
await expect(tabs.nth(1)).toHaveText('LAN: 192.168.7.1');
|
||||
});
|
||||
|
||||
test("sets up a PC's DHCP server on its own page: base settings as Java determines them, static assignments, back to the fields", async ({ page }) => {
|
||||
await openEditor(page, server.base, 'new');
|
||||
await page.locator('[data-palette="pc"]').click();
|
||||
const id = (await drawing(page)).nodes[0]!.id;
|
||||
await sheet(page).getByRole('button', { name: 'DHCP-Server einrichten' }).click();
|
||||
await expect(sheet(page).getByRole('heading')).toHaveText('DHCP-Server einrichten');
|
||||
await expect(field(page, 'ipAddress')).toBeHidden();
|
||||
await expect(field(page, 'dhcpGateway')).toHaveValue('0.0.0.0');
|
||||
await expect(field(page, 'dhcpGateway')).toHaveAttribute('readonly', '');
|
||||
await expect(field(page, 'dhcpSubnetMask')).toHaveValue('255.255.255.0');
|
||||
await field(page, 'lowerBound').fill('192.168.0.300');
|
||||
await field(page, 'lowerBound').press('Enter');
|
||||
await expect(sheet(page).getByRole('status')).toHaveText('Nicht übernommen – es gilt weiterhin 0.0.0.0.');
|
||||
await field(page, 'lowerBound').fill('192.168.000.100');
|
||||
await field(page, 'lowerBound').press('Enter');
|
||||
await expect(field(page, 'lowerBound')).toHaveValue('192.168.0.100');
|
||||
await sheet(page).getByLabel('Manuelle Einstellungen').check();
|
||||
await expect(field(page, 'dhcpGateway')).not.toHaveAttribute('readonly', '');
|
||||
await field(page, 'dhcpGateway').fill('192.168.0.1');
|
||||
await field(page, 'dhcpGateway').press('Enter');
|
||||
await sheet(page).getByLabel('DHCP aktivieren').check();
|
||||
await expect
|
||||
.poll(async () => ((await sheetOf(page, id)) as HostSheet).dhcpServer)
|
||||
.toMatchObject({ isActive: true, useOwnSettings: true, gateway: '192.168.0.1', lowerBoundIPAddress: '192.168.0.100' });
|
||||
|
||||
await sheet(page).getByRole('tab', { name: 'Statische Adresszuweisung' }).click();
|
||||
await sheet(page).getByRole('button', { name: 'Hinzufügen' }).click();
|
||||
await field(page, 'static-macAddress').fill('aa:bb:cc:dd:ee:01');
|
||||
await field(page, 'static-macAddress').press('Enter');
|
||||
await field(page, 'static-ipAddress').fill('192.168.0.50');
|
||||
await field(page, 'static-ipAddress').press('Enter');
|
||||
await expect(sheet(page).locator('table[data-rows="static"] tr[data-manual="0"] td').first()).toHaveText('AA:BB:CC:DD:EE:01');
|
||||
await sheet(page).getByRole('button', { name: 'Hinzufügen' }).click();
|
||||
await field(page, 'static-macAddress').fill('AA:BB:CC:DD:EE:01');
|
||||
await field(page, 'static-macAddress').press('Enter');
|
||||
await field(page, 'static-ipAddress').fill('192.168.0.51');
|
||||
await field(page, 'static-ipAddress').press('Enter');
|
||||
await expect(sheet(page).getByRole('alert')).toHaveText('Diese MAC-Adresse hat schon eine feste Adresse.');
|
||||
expect(((await sheetOf(page, id)) as HostSheet).dhcpServer.staticAssignments).toHaveLength(1);
|
||||
await sheet(page).locator('table[data-rows="static"] tr[data-manual="0"]').click();
|
||||
await sheet(page).getByRole('button', { name: 'Entfernen' }).click();
|
||||
await expect.poll(async () => ((await sheetOf(page, id)) as HostSheet).dhcpServer.staticAssignments).toEqual([]);
|
||||
|
||||
// Another device starts at its fields; back at the first, its page is closed too.
|
||||
await page.locator('[data-palette="pc"]').click();
|
||||
await expect(sheet(page).getByRole('heading')).toHaveText('Rechner');
|
||||
await expect(field(page, 'ipAddress')).toBeVisible();
|
||||
await choose(page, id);
|
||||
await expect(sheet(page)).toHaveAttribute('data-device', id);
|
||||
await sheet(page).getByRole('button', { name: 'DHCP-Server einrichten' }).click();
|
||||
await sheet(page).getByRole('button', { name: 'Zurück' }).click();
|
||||
await expect(sheet(page).getByRole('heading')).toHaveText('Rechner');
|
||||
await expect(field(page, 'ipAddress')).toBeVisible();
|
||||
await sheet(page).getByLabel('DHCP zur Konfiguration verwenden').check();
|
||||
await expect(sheet(page).getByRole('button', { name: 'DHCP-Server einrichten' })).toBeDisabled();
|
||||
});
|
||||
|
||||
test("keeps a home router's port forwarding on its own page, and shows its DHCP server with the LAN address as gateway", async ({ page }) => {
|
||||
await openEditor(page, server.base, 'new');
|
||||
await page.locator('[data-palette="gateway"]').click();
|
||||
const id = (await drawing(page)).nodes[0]!.id;
|
||||
await sheet(page).getByRole('button', { name: 'Portfreigaben' }).click();
|
||||
await expect(sheet(page).getByRole('heading')).toHaveText('Portfreigaben');
|
||||
await sheet(page).getByRole('button', { name: 'Neuer Eintrag' }).click();
|
||||
await sheet(page).locator('select[name="forwarding-protocolValue"]').selectOption('UDP');
|
||||
for (const [column, text] of [
|
||||
['publicPortValue', '53'],
|
||||
['lanIPAddress', '192.168.0.20'],
|
||||
['lanPortValue', '5353'],
|
||||
] as const) {
|
||||
await field(page, `forwarding-${column}`).fill(text);
|
||||
await field(page, `forwarding-${column}`).press('Enter');
|
||||
}
|
||||
await expect
|
||||
.poll(async () => ((await sheetOf(page, id)) as HomeRouterSheet).portForwarding)
|
||||
.toEqual([{ protocolValue: 'UDP', publicPortValue: '53', lanIPAddress: '192.168.0.20', lanPortValue: '5353', forwarded: true }]);
|
||||
await field(page, 'forwarding-lanPortValue').fill('05353');
|
||||
await field(page, 'forwarding-lanPortValue').press('Enter');
|
||||
await expect(sheet(page).getByRole('status')).toHaveText('Nicht übernommen – es gilt weiterhin 5353.');
|
||||
await sheet(page).locator('select[name="forwarding-protocolValue"]').selectOption('TCP');
|
||||
await expect.poll(async () => ((await sheetOf(page, id)) as HomeRouterSheet).portForwarding[0]!.protocolValue).toBe('TCP');
|
||||
await sheet(page).getByRole('button', { name: 'Eintrag löschen' }).click();
|
||||
await expect.poll(async () => ((await sheetOf(page, id)) as HomeRouterSheet).portForwarding).toEqual([]);
|
||||
await sheet(page).getByRole('button', { name: 'Zurück' }).click();
|
||||
|
||||
await sheet(page).getByRole('tab', { name: 'LAN: 192.168.0.10' }).click();
|
||||
await sheet(page).getByRole('button', { name: 'DHCP-Server einrichten' }).click();
|
||||
await expect(field(page, 'dhcpGateway')).toHaveValue('192.168.0.10');
|
||||
await expect(field(page, 'dhcpGateway')).toHaveAttribute('readonly', '');
|
||||
await expect(field(page, 'dhcpDnsServer')).not.toHaveAttribute('readonly', '');
|
||||
await expect(sheet(page).getByLabel('Manuelle Einstellungen')).toHaveCount(0);
|
||||
await sheet(page).getByRole('button', { name: 'Zurück' }).click();
|
||||
// Back on the tab it was opened from, as Java's panel stays behind its dialog.
|
||||
await expect(sheet(page).getByRole('tab', { name: 'LAN: 192.168.0.10' })).toHaveAttribute('aria-selected', 'true');
|
||||
});
|
||||
|
||||
test.describe('on a phone', () => {
|
||||
test.use({ viewport: { width: 390, height: 844 }, hasTouch: true });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user