Files
mdg-filius/packages/editor/browser/web.spec.ts
T
julian.kiedaischandClaude Opus 5.5 aa54e5400e feat(editor): F4.6d the web browser and web server on a computer's desktop
The oracle BrowserWindowRun uses Java's browser and web server windows as a
person does; the room keeps a browser window per computer and sends a
program window's view, the start names the person's language. The editor
shows the page in a sandboxed frame, links and forms go to the room, the web
server has its button, virtual hosts and lines. Better than Java: relative
images and links count from the page's folder, a mark stays in the page, the
error page from Java's template, the virtual hosts' file with complete rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 16:09:05 +00:00

206 lines
11 KiB
TypeScript

// The web browser and the web server on a computer's desktop in Chromium
// (F4.6d), on browser/fixtures/web-pages.fls (test/webPagesFixture.test.ts):
// Java's address line and title, the page in a sandbox that runs no script
// and loads nothing but the images that came, links and forms handed to the
// room as Java's window hands them to its program, the server's button,
// virtual hosts and lines, the routers' pages in the language FILIUS runs in.
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { expect, test, type Page } from '@playwright/test';
import { drawing, onDevice, serveEditor } from './editorPage.js';
let server: { readonly base: string; readonly close: () => Promise<void> };
test.beforeAll(async () => {
server = await serveEditor();
});
test.afterAll(async () => {
await server.close();
});
const FIXTURE = join(dirname(fileURLToPath(import.meta.url)), 'fixtures/web-pages.fls');
async function openPages(page: Page, query = ''): Promise<void> {
await page.goto(`${server.base}?example=new${query}`);
await page.waitForSelector('svg.filius-canvas');
await page.locator('input[type="file"]').setInputFiles(FIXTURE);
await expect.poll(async () => (await drawing(page).catch(() => ({ nodes: [] }))).nodes.length).toBe(4);
}
async function start(page: Page, name = 'Wechselt in den Aktionsmodus'): Promise<void> {
await page.getByRole('button', { name }).click();
await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'simulation');
}
/** Opens a computer's desktop with a tap and a program's window on it. */
async function openProgram(page: Page, name: string, program: string) {
const id = (await drawing(page)).nodes.find((node) => node.name === name)!.id;
const at = await onDevice(page, id);
await page.mouse.click(at.x, at.y);
const desktop = page.locator(`section[data-desktop="${id}"]`);
await desktop.locator(`[data-program="${program}"]`).click();
const window = desktop.locator(`[data-program-window="${program}"]`);
return { desktop, window, title: window.locator('.program-titlebar h3') };
}
test("browses as Java's browser does: the address line, the page's title, links and forms to the server, the page in a sandbox", async ({ page }) => {
test.setTimeout(120_000);
await openPages(page);
await start(page);
const { desktop, window, title } = await openProgram(page, 'Browser', 'webBrowser');
const address = window.getByRole('textbox', { name: 'Adresse' });
const frame = window.frameLocator('iframe[title="Seite"]');
// Java's start: the program's name, "http://", its browser's logo.
await expect(title).toHaveText('Webbrowser');
await expect(address).toHaveValue('http://');
await expect(window.locator('.browser-start img')).toBeVisible();
await address.fill('192.168.0.20');
await address.press('Enter');
await expect(address).toHaveValue('http://192.168.0.20/');
await expect(title).toHaveText('Seiten & Formulare', { timeout: 30_000 });
await expect(frame.locator('h1')).toHaveText('Start');
// The image that came shows from its data, the missing one does not load; no script ran, nor a handler.
await expect(frame.locator('img[alt="Bild"]')).toHaveAttribute('src', /^data:image\/png;base64,iVBOR/, { timeout: 30_000 });
await expect(frame.locator('img[alt="Fehlt"]')).not.toHaveAttribute('src', /./);
await expect(frame.locator('script')).toHaveCount(0);
expect(await frame.locator('body').getAttribute('data-ran')).toBeNull();
expect(await frame.locator('body').getAttribute('onload')).toBeNull();
await expect(frame.locator('meta[http-equiv="Content-Security-Policy"]')).toHaveAttribute('content', /default-src 'none'; img-src data:/);
await expect(window.locator('iframe')).toHaveAttribute('sandbox', 'allow-same-origin allow-forms');
// A mark in the page stays in it; a link goes to the server.
await frame.getByRole('link', { name: 'Unten' }).click();
await expect(frame.locator('#unten')).toBeInViewport();
await expect(address).toHaveValue('http://192.168.0.20/');
await frame.getByRole('link', { name: 'Drei' }).click();
await expect(title).toHaveText('Drei', { timeout: 30_000 });
await expect(address).toHaveValue('http://192.168.0.20/ordner/drei.html');
// A link of a page in a folder goes beside it (Java's from the server's root).
await frame.getByRole('link', { name: 'Vier' }).click();
await expect(title).toHaveText('Vier im Ordner', { timeout: 30_000 });
await expect(address).toHaveValue('http://192.168.0.20/ordner/vier.html');
// The forms: the POST one sends its fields as Java's window sends them.
await address.fill('http://192.168.0.20/');
await window.getByRole('button', { name: 'Start' }).click();
await expect(title).toHaveText('Seiten & Formulare', { timeout: 30_000 });
await frame.getByRole('button', { name: 'Gut' }).click();
await expect(title).toHaveText('Ziel', { timeout: 30_000 });
await expect(address).toHaveValue('http://192.168.0.20/ziel.html');
await address.fill('192.168.0.20');
await address.press('Enter');
await expect(title).toHaveText('Seiten & Formulare', { timeout: 30_000 });
// The GET one's data stays in the address, which Java's browser does not send (nor show).
await frame.getByRole('button', { name: 'Los' }).click();
await expect(title).toHaveText('Ziel', { timeout: 30_000 });
await expect(address).toHaveValue('http://192.168.0.20/ziel.html');
// An error: the browser's page for its status (Java's never finds its template).
await address.fill('192.168.0.20/nichts.html');
await address.press('Enter');
await expect(title).toHaveText('Fehler Not Found (404)', { timeout: 30_000 });
await expect(frame.locator('h1')).toHaveText('Not Found');
await expect(frame.locator('p')).toHaveText('Es ist ein Fehler aufgetreten (HTTP-Status: 404)');
// No address: the line goes back to "http://", the page stays.
await address.fill('kein host!');
await address.press('Enter');
await expect(address).toHaveValue('http://');
await expect(title).toHaveText('Fehler Not Found (404)');
// The server got the forms as Java's browser sends them.
await desktop.getByRole('button', { name: 'Schließen' }).click();
const web = await openProgram(page, 'Server', 'webServer');
const log = web.window.getByLabel('Meldungen');
await expect(log).toContainText('>>POST /ziel.html HTTP/1.1');
await expect(log).toContainText('name=Fil+ius&c=1&s=y&t=%C3%BC%26%3D&ok=Gut');
await expect(log).toContainText('>>GET /ziel.html HTTP/1.1');
await expect(log).not.toContainText('q=a+b');
await expect(log).toContainText('Verbindung zu 192.168.0.10:');
});
test("starts and stops the web server, and edits its virtual hosts, as Java's window does", async ({ page }) => {
test.setTimeout(120_000);
await openPages(page);
await start(page);
const { desktop, window, title } = await openProgram(page, 'Server', 'webServer');
await expect(title).toHaveText('Webserver');
const button = window.getByRole('button', { name: 'Anhalten' });
await expect(button).toBeVisible();
const box = window.getByRole('checkbox', { name: 'Verwende virtuelle Hosts' });
await expect(box).not.toBeChecked();
await expect(window.getByRole('table')).toHaveCount(0);
await button.click();
await expect(window.getByRole('button', { name: 'Starten' })).toBeVisible();
await expect(window.getByLabel('Meldungen')).toContainText('Annahme von Verbindungsanfragen beendet');
await expect(window.getByLabel('Meldungen')).toContainText('Server-Socket geschlossen');
// The virtual hosts' table: five rows, a cell written when it is left.
// The box shows what the server holds: ticked once the room answers.
await box.click();
await expect(box).toBeChecked();
const table = window.getByRole('table', { name: 'Virtuelle Hosts:' });
await expect(table.getByRole('columnheader')).toHaveText(['Hostname', 'Unterverzeichnis']);
await expect(table.getByRole('textbox')).toHaveCount(10);
await table.getByRole('textbox', { name: 'Hostname 1' }).fill('www.filius.de');
await table.getByRole('textbox', { name: 'Unterverzeichnis 1' }).fill('ordner');
await table.getByRole('textbox', { name: 'Unterverzeichnis 1' }).press('Enter');
// Escape goes back to what the server holds: what Enter wrote.
await table.getByRole('textbox', { name: 'Unterverzeichnis 1' }).press('Escape');
await expect(table.getByRole('textbox', { name: 'Unterverzeichnis 1' })).toHaveValue('ordner');
await table.getByRole('textbox', { name: 'Hostname 2' }).fill('weg');
await table.getByRole('textbox', { name: 'Hostname 2' }).press('Escape');
await expect(table.getByRole('textbox', { name: 'Hostname 2' })).toHaveValue('');
// Left and opened again, the window shows what the server holds.
await desktop.getByRole('button', { name: 'Startbildschirm' }).click();
await desktop.locator('[data-program="webServer"]').click();
await expect(table.getByRole('textbox', { name: 'Hostname 1' })).toHaveValue('www.filius.de');
await expect(table.getByRole('textbox', { name: 'Unterverzeichnis 1' })).toHaveValue('ordner');
await window.getByRole('button', { name: 'Starten' }).click();
await expect(window.getByLabel('Meldungen')).toContainText('Annahme von Verbindungsanfragen gestartet');
// Asked for by its address, which no row names, the server gives the root's pages.
await desktop.getByRole('button', { name: 'Schließen' }).click();
const browser = await openProgram(page, 'Browser', 'webBrowser');
const address = browser.window.getByRole('textbox', { name: 'Adresse' });
await address.fill('192.168.0.20');
await address.press('Enter');
await expect(browser.title).toHaveText('Seiten & Formulare', { timeout: 30_000 });
});
test("shows the server it cannot reach as Java's browser does", async ({ page }) => {
test.setTimeout(120_000);
await openPages(page);
await start(page);
const web = await openProgram(page, 'Server', 'webServer');
await web.window.getByRole('button', { name: 'Anhalten' }).click();
await web.desktop.getByRole('button', { name: 'Schließen' }).click();
const { window, title } = await openProgram(page, 'Browser', 'webBrowser');
const address = window.getByRole('textbox', { name: 'Adresse' });
await address.fill('192.168.0.20');
await address.press('Enter');
await expect(title).toHaveText('http://192.168.0.20/');
await expect(title).toHaveText('Fehler', { timeout: 60_000 });
await expect(window.frameLocator('iframe').locator('h1')).toHaveText('Server konnte nicht erreicht werden!');
});
test("shows a router's pages in English when FILIUS speaks English", async ({ page }) => {
test.setTimeout(120_000);
await openPages(page, '&lang=en');
await start(page, 'Change to Simulation Mode');
const { window, title } = await openProgram(page, 'Browser', 'webBrowser');
await expect(title).toHaveText('Webbrowser');
const address = window.getByRole('textbox', { name: 'Address' });
await address.fill('192.168.0.1');
await address.press('Enter');
await expect(title).toHaveText('Router Administration', { timeout: 30_000 });
await window.frameLocator('iframe').getByRole('link', { name: 'Show current routing/forwarding table' }).click();
await expect(title).toHaveText('Routing/Forwarding Table', { timeout: 30_000 });
await expect(address).toHaveValue('http://192.168.0.1/routes');
await address.fill('192.168.0.20/nichts.html');
await address.press('Enter');
await expect(title).toHaveText('Error Not Found (404)', { timeout: 30_000 });
});