// Two projects' networks joined by their modems (FILIUS_MODEM_PLAN K5), on a // real server F, three people in three browsers, each in a copy of FILIUS' // modem example. Mia's access point offers a connection and shows its code; // Ben's accessing modem joins it with the code as typed off the board. Each // modem says under it how it stands. Ben's notebook pings 192.168.0.11 — // the computer behind Mia's access point, for the one behind Ben's own is cut // off: his access point has no partner left on its port. Rita joins the same // code and reads that it is taken, until Ben stops. import { expect, test, type Browser, type Page } from '@playwright/test'; import { onDevice, serveEditor, showDesktop } from './editorPage.js'; import { startServerF, type ServerF } from './serverF.js'; let editor: { readonly base: string; readonly close: () => Promise }; let f: ServerF; test.beforeAll(async () => { editor = await serveEditor(); f = await startServerF(editor.base); }); test.afterAll(async () => { await f?.close(); await editor?.close(); }); const ACCESS_POINT = 'Modem (Zugangspunkt)'; const ACCESSING = 'Modem (Zugreifend)'; /** A person in their own browser, signed in, in a fresh copy of the modem example. */ async function person(browser: Browser, name: string): Promise<{ readonly page: Page; readonly node: (name: string) => string }> { const project = await f.project('java-examples/modem_beispiel.fls'); const context = await browser.newContext({ permissions: ['clipboard-read', 'clipboard-write'] }); await f.signIn(context, project.id, { name }); const page = await context.newPage(); await page.goto(`${editor.base}?project=${project.id}&server=${encodeURIComponent(f.base)}`); await page.waitForSelector('svg.filius-canvas'); return { page, node: project.node }; } const sheet = (page: Page) => page.getByRole('complementary', { name: 'Einstellungen' }); const note = (page: Page, id: string) => page.locator(`g.device[data-node="${id}"] text[data-note]`); async function choose(page: Page, id: string): Promise { const at = await onDevice(page, id); await page.mouse.click(at.x, at.y); await expect(sheet(page)).toBeVisible(); } async function start(page: Page): Promise { await page.getByRole('button', { name: 'Wechselt in den Aktionsmodus' }).click(); await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'simulation'); } test("joins two projects' networks through their modems: a code offered, a code typed, a ping across", async ({ browser }) => { test.setTimeout(120_000); const mia = await person(browser, 'Mia'); const ben = await person(browser, 'Ben'); const rita = await person(browser, 'Rita'); // Mia's access point offers: its sheet shows the code to pass on, and copies it. await choose(mia.page, mia.node(ACCESS_POINT)); await sheet(mia.page).getByRole('radio', { name: 'Mit einem anderen Projekt: Verbindung anbieten' }).check(); const code = (await sheet(mia.page).locator('output.modem-code').textContent())!; expect(code).toMatch(/^[A-HJ-NP-Z2-9]{3}-[A-HJ-NP-Z2-9]{3}$/); await sheet(mia.page).getByRole('button', { name: 'Code kopieren' }).click(); await expect(sheet(mia.page).getByRole('button', { name: 'Kopiert ✓' })).toBeVisible(); await expect(mia.page.getByRole('alert')).toHaveCount(0); expect(await mia.page.evaluate(() => navigator.clipboard.readText())).toBe(code); await mia.page.screenshot({ path: 'test-results/modem-offer.png' }); // Ben's accessing modem joins, the code typed as one reads it off the board. for (const person of [ben, rita]) { await choose(person.page, person.node(ACCESSING)); await sheet(person.page).getByRole('radio', { name: 'Mit einem anderen Projekt: mit Code verbinden' }).check(); const field = sheet(person.page).locator('input[name="modemCode"]'); await field.fill(code.toLowerCase().replace('-', ' ')); await field.press('Enter'); await expect(field).toHaveValue(code); } await ben.page.screenshot({ path: 'test-results/modem-join.png' }); await start(mia.page); await expect(note(mia.page, mia.node(ACCESS_POINT))).toHaveText('wartet auf Gegenstelle'); await expect(note(mia.page, mia.node(ACCESSING))).toHaveText('kein zweites Modem mit diesem Port'); await start(ben.page); await expect(note(ben.page, ben.node(ACCESSING))).toHaveText('verbunden'); await expect(note(mia.page, mia.node(ACCESS_POINT))).toHaveText('verbunden'); await expect(note(mia.page, mia.node(ACCESS_POINT))).toHaveAttribute('data-note', 'good'); await mia.page.screenshot({ path: 'test-results/modem-connected.png' }); // Ben's notebook pings the computer behind Mia's access point. const notebook = ben.node('Notebook 0.10'); await showDesktop(ben.page, notebook); const desktop = ben.page.locator(`section[data-desktop="${notebook}"]`); await desktop.locator('[data-program="commandPrompt"]').click(); const input = desktop.locator('[data-program-window="commandPrompt"] input[name="terminalInput"]'); await input.fill('ping 192.168.0.11'); await input.press('Enter'); await expect(desktop.locator('.terminal-text')).toContainText('4 Paket(e) gesendet, 4 Paket(e) empfangen', { timeout: 30_000 }); // Rita wants the same code: taken while Ben has it, hers once he stops. await start(rita.page); await expect(note(rita.page, rita.node(ACCESSING))).toHaveText('Code schon belegt'); await expect(note(rita.page, rita.node(ACCESSING))).toHaveAttribute('data-note', 'trouble'); await ben.page.getByRole('button', { name: 'Wechselt in den Entwurfsmodus' }).click(); await expect(note(rita.page, rita.node(ACCESSING))).toHaveText('verbunden'); await expect(note(mia.page, mia.node(ACCESS_POINT))).toHaveText('verbunden'); // In the design there is nothing to say. await expect(ben.page.locator('text[data-note]')).toHaveCount(0); });