// The device sheet in Chromium (Playwright): it opens with the device that // is chosen, shows Java's fields, takes a field when it is left, marks and // keeps a text its rule refuses, moves a notebook into a wireless network — // beside the surface on a wide screen, below it on a phone. import { expect, test, type Page } from '@playwright/test'; import type { HostSheet } from '@mdg-filius/doc'; import { drawing, onDevice, openEditor, serveEditor, sheetOf } from './editorPage.js'; let server: { readonly base: string; readonly close: () => Promise }; test.beforeAll(async () => { server = await serveEditor(); }); test.afterAll(async () => { await server.close(); }); async function deviceNamed(page: Page, name: string): Promise { return (await drawing(page)).nodes.find((node) => node.name === name)!.id; } async function choose(page: Page, id: string): Promise { const at = await onDevice(page, id); await page.mouse.click(at.x, at.y); } const sheet = (page: Page) => page.getByRole('complementary', { name: 'Einstellungen' }); const field = (page: Page, name: string) => sheet(page).locator(`input[name="${name}"]`); test("opens with the chosen device, shows Java's fields beside the surface, and closes letting go of it", async ({ page }) => { await openEditor(page, server.base, 'zwei_rechnernetze_komplett.fls'); await expect(sheet(page)).toHaveCount(0); const id = await deviceNamed(page, 'Notebook 1.10'); await choose(page, id); await expect(sheet(page)).toBeVisible(); await expect(sheet(page).getByRole('heading')).toHaveText('Notebook'); const values = (await sheetOf(page, id)) as HostSheet; await expect(field(page, 'name')).toHaveValue('Notebook 1.10'); await expect(field(page, 'macAddress')).toHaveValue(values.macAddress); await expect(field(page, 'ipAddress')).toHaveValue(values.ipAddress); await expect(field(page, 'subnetMask')).toHaveValue(values.subnetMask); await expect(field(page, 'defaultGateway')).toHaveValue(values.defaultGateway); await expect(sheet(page).getByLabel('Kabelgebunden (LAN)')).toBeChecked(); await expect(sheet(page).getByLabel('DHCP zur Konfiguration verwenden')).not.toBeChecked(); const surface = (await page.locator('svg.filius-canvas').boundingBox())!; const aside = (await sheet(page).boundingBox())!; expect(aside.x).toBeGreaterThanOrEqual(surface.x + surface.width - 1); expect(aside.width).toBe(340); await sheet(page).getByRole('button', { name: 'Schließen' }).click(); await expect(sheet(page)).toHaveCount(0); await expect(page.locator(`g.device[data-node="${id}"]`)).not.toHaveAttribute('data-selected', 'true'); }); test('marks a refused address while it is typed, keeps it with what still holds, and takes a mended one', async ({ page }) => { await openEditor(page, server.base, 'new'); await page.locator('[data-palette="pc"]').click(); await expect(sheet(page)).toBeVisible(); const id = (await drawing(page)).nodes[0]!.id; const ip = field(page, 'ipAddress'); await ip.fill('10.0.0.256'); await expect(ip).toHaveAttribute('aria-invalid', 'true'); await field(page, 'subnetMask').focus(); await expect(sheet(page).getByRole('status')).toHaveText('Nicht übernommen – es gilt weiterhin 192.168.0.10.'); await expect(ip).toHaveValue('10.0.0.256'); expect(((await sheetOf(page, id)) as HostSheet).ipAddress).toBe('192.168.0.10'); await ip.fill('010.0.0.5'); await expect(ip).toHaveAttribute('aria-invalid', 'false'); await expect(sheet(page).getByRole('status')).toHaveCount(0); await ip.press('Enter'); await expect(ip).toHaveValue('10.0.0.5'); expect(((await sheetOf(page, id)) as HostSheet).ipAddress).toBe('10.0.0.5'); // Escape gives a text up; the empty gateway is taken, the empty address is not. await ip.fill('1.2.3'); await ip.press('Escape'); await expect(ip).toHaveValue('10.0.0.5'); await expect(sheet(page)).toBeVisible(); await field(page, 'defaultGateway').fill(''); await field(page, 'defaultGateway').press('Enter'); await ip.fill(''); await ip.press('Enter'); await expect(sheet(page).getByRole('status')).toHaveText('Nicht übernommen – es gilt weiterhin 10.0.0.5.'); }); test('takes a change back as one step, and shows the address as the name', async ({ page }) => { await openEditor(page, server.base, 'new'); await page.locator('[data-palette="pc"]').click(); const id = (await drawing(page)).nodes[0]!.id; await field(page, 'ipAddress').fill('10.0.0.9'); await field(page, 'ipAddress').press('Enter'); await sheet(page).getByLabel('IP-Adresse als Name verwenden').check(); await expect.poll(async () => (await drawing(page)).nodes[0]!.label).toBe('10.0.0.9'); await expect(field(page, 'name')).toBeDisabled(); await expect(field(page, 'name')).toHaveValue('10.0.0.9'); await page.getByRole('button', { name: 'Rückgängig' }).click(); await expect.poll(async () => (await drawing(page)).nodes[0]!.label).toBe('Rechner'); await expect(field(page, 'name')).toBeEnabled(); await page.getByRole('button', { name: 'Rückgängig' }).click(); await expect(field(page, 'ipAddress')).toHaveValue('192.168.0.10'); expect(((await sheetOf(page, id)) as HostSheet).ipAddress).toBe('192.168.0.10'); }); test('takes the text of one device when another is chosen, and shows the other', async ({ page }) => { await openEditor(page, server.base, 'zwei_rechnernetze_komplett.fls'); const first = await deviceNamed(page, 'Notebook 1.10'); const second = await deviceNamed(page, 'Notebook 1.11'); await choose(page, first); await field(page, 'dnsServer').fill('192.168.1.53'); await choose(page, second); await expect(sheet(page)).toHaveAttribute('data-device', second); expect(((await sheetOf(page, first)) as HostSheet).dnsServer).toBe('192.168.1.53'); await expect(field(page, 'name')).toHaveValue('Notebook 1.11'); await expect(field(page, 'dnsServer')).toHaveValue(((await sheetOf(page, second)) as HostSheet).dnsServer); }); test('offers no text of one device to the next, a refused one neither', async ({ page }) => { await openEditor(page, server.base, 'zwei_rechnernetze_komplett.fls'); const first = await deviceNamed(page, 'Notebook 1.10'); const second = await deviceNamed(page, 'Notebook 1.11'); await choose(page, first); await field(page, 'ipAddress').fill('300.0.0.1'); await choose(page, second); await expect(sheet(page)).toHaveAttribute('data-device', second); await expect(field(page, 'ipAddress')).toHaveValue(((await sheetOf(page, second)) as HostSheet).ipAddress); await expect(field(page, 'ipAddress')).toHaveAttribute('aria-invalid', 'false'); await expect(sheet(page).getByRole('status')).toHaveCount(0); }); test('closes the address fields while DHCP configures the card, as Java does', async ({ page }) => { await openEditor(page, server.base, 'new'); await page.locator('[data-palette="pc"]').click(); const fields = ['ipAddress', 'subnetMask', 'defaultGateway', 'dnsServer']; await sheet(page).getByLabel('DHCP zur Konfiguration verwenden').check(); for (const name of fields) await expect(field(page, name)).toBeDisabled(); await expect(field(page, 'name')).toBeEnabled(); await sheet(page).getByLabel('DHCP zur Konfiguration verwenden').uncheck(); for (const name of fields) await expect(field(page, name)).toBeEnabled(); }); test("joins a notebook to a switch's wireless network, drawn as wireless", async ({ page }) => { await openEditor(page, server.base, 'new'); await page.locator('[data-palette="networkSwitch"]').click(); const hub = (await drawing(page)).nodes[0]!.id; await field(page, 'ssid').fill('Schule'); await field(page, 'ssid').press('Enter'); await sheet(page).getByLabel('als Wolke darstellen').check(); await expect.poll(async () => (await drawing(page)).nodes[0]!.cloud).toBe(true); await page.locator('[data-palette="notebook"]').click(); await expect(sheet(page).getByRole('heading')).toHaveText('Notebook'); await expect(sheet(page).locator('select[name="ssid"]')).toBeDisabled(); await sheet(page).getByLabel('Drahtlos (WLAN)').check(); await sheet(page).locator('select[name="ssid"]').selectOption('Schule'); await expect.poll(async () => (await drawing(page)).links.map((link) => [link.targetNode, link.wireless])).toEqual([[hub, true]]); await sheet(page).getByLabel('Kabelgebunden (LAN)').check(); await expect.poll(async () => (await drawing(page)).links).toEqual([]); }); test.describe('on a phone', () => { test.use({ viewport: { width: 390, height: 844 }, hasTouch: true }); test('opens below the surface with a tap, the device still in sight', async ({ page }) => { await openEditor(page, server.base, 'zwei_rechnernetze_komplett.fls'); const id = await deviceNamed(page, 'Notebook 1.10'); const at = await onDevice(page, id); await page.touchscreen.tap(at.x, at.y); await expect(sheet(page)).toBeVisible(); const surface = (await page.locator('svg.filius-canvas').boundingBox())!; const aside = (await sheet(page).boundingBox())!; expect(aside.y).toBeGreaterThanOrEqual(surface.y + surface.height - 1); expect(aside.height).toBeLessThanOrEqual(844 / 2); expect(surface.height).toBeGreaterThan(200); const fieldBox = (await field(page, 'ipAddress').boundingBox())!; expect(fieldBox.height).toBeGreaterThanOrEqual(44); }); }); test('speaks English when asked', async ({ page }) => { await openEditor(page, server.base, 'new', '&lang=en'); await page.locator('[data-palette="networkSwitch"]').click(); const aside = page.getByRole('complementary', { name: 'Settings' }); await expect(aside.getByText('Wi-Fi Name (SSID)')).toBeVisible(); await expect(aside.getByText('Retention Time SAT Entries (Seconds)')).toBeVisible(); await expect(aside.getByLabel('Display as Cloud')).not.toBeChecked(); });