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>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
// The project the browser tests open for the web browser and web server
|
||||
// (browser/fixtures/web-pages.fls), made here with the document's commands:
|
||||
// a computer "Browser" (192.168.0.10) with a web browser, a computer
|
||||
// "Server" (192.168.0.20) whose web server runs with the pages below — links,
|
||||
// a mark in the page, a page in a folder, a GET and a POST form, an image
|
||||
// there and one missing, a script and a handler that must not run —, and a
|
||||
// router (192.168.0.1) for its pages, all on one switch. Compared on each run;
|
||||
// written anew with FILIUS_WRITE_FIXTURES=1.
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import * as Y from 'yjs';
|
||||
import { installationFiles, type VirtualFileSystem } from '@mdg-filius/core';
|
||||
import { addDevice, connect, docArchive, emptyProject, fileEntry, readProject, ROOTS, setHostAddress, setInterfaceAddress, setSoftware, writeProject } from '@mdg-filius/doc';
|
||||
import { openProjectArchive } from '@mdg-filius/fls';
|
||||
|
||||
const fixture = join(dirname(fileURLToPath(import.meta.url)), '../browser/fixtures/web-pages.fls');
|
||||
|
||||
/** The server's pages besides those its installation brings (index.html replaced). */
|
||||
export const WEB_PAGES: Readonly<Record<string, string>> = {
|
||||
'/webserver/index.html':
|
||||
'<html><head><title>Seiten & Formulare</title><script>document.title = "Skript"</script></head>' +
|
||||
'<body onload="document.body.dataset.ran = \'yes\'"><h1 id="oben">Start</h1>' +
|
||||
'<img src="splashscreen-mini.png" alt="Bild"><img src="fehlt.png" alt="Fehlt">' +
|
||||
'<p><a href="zwei.html">Zwei</a> <a href="#unten">Unten</a> <a href="ordner/drei.html">Drei</a></p>' +
|
||||
'<form action="ziel.html" method="get"><input name="q" value="a b"><input type="submit" name="los" value="Los"></form>' +
|
||||
'<form action="ziel.html" method="post"><input name="name" value="Fil ius"><input type="checkbox" name="c" value="1" checked>' +
|
||||
'<input type="checkbox" name="d" value="1"><select name="s"><option>x</option><option selected value="y">Y</option></select>' +
|
||||
'<textarea name="t">ü&=</textarea><input type="submit" name="ok" value="Gut"></form>' +
|
||||
'<p id="unten" style="margin-top: 3000px">Unten</p></body></html>',
|
||||
'/webserver/zwei.html': '<html><head><title>Zwei</title></head><body><a href="index.html">Start</a></body></html>',
|
||||
'/webserver/ziel.html': '<html><head><title>Ziel</title></head><body>Ziel</body></html>',
|
||||
'/webserver/ordner/drei.html': '<html><head><title>Drei</title></head><body><a href="vier.html">Vier</a></body></html>',
|
||||
'/webserver/ordner/vier.html': '<html><head><title>Vier im Ordner</title></head><body>Vier</body></html>',
|
||||
};
|
||||
|
||||
function webPagesDoc(): Y.Doc {
|
||||
const doc = new Y.Doc();
|
||||
writeProject(doc, emptyProject());
|
||||
let ids = 0;
|
||||
const newId = () => `port-${++ids}`;
|
||||
addDevice(doc, { kind: 'pc', id: 'browser', name: 'Browser', x: 60, y: 60, newId }, 'me');
|
||||
addDevice(doc, { kind: 'pc', id: 'server', name: 'Server', x: 360, y: 60, newId }, 'me');
|
||||
addDevice(doc, { kind: 'router', id: 'router', name: 'Router', x: 210, y: 260, newId }, 'me');
|
||||
addDevice(doc, { kind: 'networkSwitch', id: 'switch', name: 'Switch', x: 210, y: 140, newId }, 'me');
|
||||
for (const [node, link] of [['browser', 'cable-1'], ['server', 'cable-2'], ['router', 'cable-3']] as const) connect(doc, { node }, { node: 'switch' }, link, 'me');
|
||||
setHostAddress(doc, 'browser', 'ipAddress', '192.168.0.10', 'me');
|
||||
setHostAddress(doc, 'server', 'ipAddress', '192.168.0.20', 'me');
|
||||
// The router's second card, unplugged, away from the computers' network (its own address would be 192.168.0.10, the browser's).
|
||||
const routerPorts = (doc.getMap<Y.Map<unknown>>(ROOTS.nodes).get('router')!.get('ports') as Y.Array<Y.Map<unknown>>).map((port) => port.get('id') as string);
|
||||
setInterfaceAddress(doc, 'router', routerPorts[0]!, 'ipAddress', '192.168.0.1', 'me');
|
||||
setInterfaceAddress(doc, 'router', routerPorts[1]!, 'ipAddress', '10.0.0.1', 'me');
|
||||
setSoftware(doc, 'browser', { install: ['webBrowser'], remove: [] }, 'de', 'me');
|
||||
setSoftware(doc, 'server', { install: ['webServer'], remove: [] }, 'de', 'me');
|
||||
const server = doc.getMap<Y.Map<unknown>>(ROOTS.nodes).get('server')!;
|
||||
(server.get('programs') as Y.Map<Y.Map<unknown>>).get('webServer')!.set('active', true);
|
||||
const files = server.get('files') as Y.Map<Y.Map<unknown>>;
|
||||
let order = files.size;
|
||||
files.set('/webserver/ordner', fileEntry(order++, { kind: 'directory' }));
|
||||
for (const [path, text] of Object.entries(WEB_PAGES)) {
|
||||
if (files.has(path)) (files.get(path)!.get('text') as Y.Text).delete(0, (files.get(path)!.get('text') as Y.Text).length);
|
||||
if (files.has(path)) (files.get(path)!.get('text') as Y.Text).insert(0, text);
|
||||
else files.set(path, fileEntry(order++, { kind: 'text', text }));
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
/** A computer's files, text as it is, the rest by kind. */
|
||||
function filesOf(files: VirtualFileSystem | undefined): Record<string, string> {
|
||||
return Object.fromEntries((files?.allEntries() ?? []).map((entry) => [entry.path, entry.content.kind === 'text' ? entry.content.text : entry.content.kind]));
|
||||
}
|
||||
|
||||
describe("the browser tests' project with web pages", () => {
|
||||
it('is made from the pages above', () => {
|
||||
const made = docArchive(webPagesDoc()).archive;
|
||||
if (process.env.FILIUS_WRITE_FIXTURES === '1' || !existsSync(fixture)) writeFileSync(fixture, made);
|
||||
let ids = 0;
|
||||
const open = (archive: Uint8Array) => openProjectArchive(archive, () => `id-${++ids}`);
|
||||
const [saved, fresh] = [open(new Uint8Array(readFileSync(fixture))), open(made)];
|
||||
const server = (project: typeof saved) => project.graph.nodes.find((node) => node.displayName === 'Server')!.id;
|
||||
expect(filesOf(saved.virtualFileSystems.get(server(saved)))).toEqual(filesOf(fresh.virtualFileSystems.get(server(fresh))));
|
||||
expect(filesOf(saved.virtualFileSystems.get(server(saved)))).toMatchObject(WEB_PAGES);
|
||||
expect(saved.activePrograms.get(server(saved))).toEqual(new Set(['webServer']));
|
||||
expect(saved.graph.nodes.map((node) => node.displayName).sort()).toEqual(['Browser', 'Router', 'Server', 'Switch']);
|
||||
expect(saved.graph.links).toHaveLength(3);
|
||||
// The installation's picture is there for the start page's image.
|
||||
expect(installationFiles('webServer', 'de').map((file) => file.path)).toContain('/webserver/splashscreen-mini.png');
|
||||
expect(readProject(webPagesDoc()).installedPrograms.get('browser')).toEqual(new Set(['webBrowser']));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user