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>
206 lines
11 KiB
TypeScript
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 });
|
|
});
|