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:
@@ -196,7 +196,7 @@ describe('a simulation room', () => {
|
||||
expect(reason({ node: 'nobody', app: 'terminal', action: 'enter', args: { line: 'x' } })).toMatchObject({ code: 'unknownNode' });
|
||||
expect(reason({ node: node('Switch 1').id, app: 'terminal', action: 'enter', args: { line: 'x' } })).toMatchObject({ code: 'unknownApp' });
|
||||
expect(reason({ node: notebook, app: 'terminal', action: 'paint' })).toMatchObject({ code: 'unknownAction' });
|
||||
expect(reason({ node: notebook, app: 'webBrowser', action: 'open' })).toMatchObject({ code: 'unknownAction' });
|
||||
expect(reason({ node: notebook, app: 'emailClient', action: 'send' })).toMatchObject({ code: 'unknownAction' });
|
||||
expect(reason({ node: notebook, app: 'terminal', action: 'enter', args: { line: 7 } })).toMatchObject({ code: 'badArgs' });
|
||||
expect(reason({ node: notebook, app: 'terminal', action: 'interrupt' })).toEqual({ v: 1, kind: 'ack', reqId: 'r' });
|
||||
send({ kind: 'nonsense', reqId: 'q' });
|
||||
@@ -371,3 +371,99 @@ describe('a simulation room', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("a simulation room's browser and web server windows", () => {
|
||||
const views = (got: readonly HostMessage[], node: Id, app: string) => ofKind(got, 'view').filter((each) => each.node === node && each.app === app).map((each) => each.view);
|
||||
|
||||
it("sends the browser's view and the web server's whole when they change, and none of the browser's lines", () => {
|
||||
const { node, send, run, got } = setUp();
|
||||
const notebook = node('Notebook 1.10').id;
|
||||
const server = node('Webserver 5.10').id;
|
||||
send({ kind: 'sub', topics: [`app:${notebook}/webBrowser`, `app:${server}/webServer`] });
|
||||
// Before a start: neither has anything.
|
||||
expect([views(got, notebook, 'webBrowser'), views(got, server, 'webServer')]).toEqual([[null], [null]]);
|
||||
send({ kind: 'start' });
|
||||
run(TICK_MILLISECONDS);
|
||||
expect(views(got, notebook, 'webBrowser')).toEqual([null, { address: 'http://', loading: null, arrived: false, page: { kind: 'start' } }]);
|
||||
expect(views(got, server, 'webServer')).toEqual([null, { active: true, useVirtualHosts: false, virtualHosts: [['', ''], ['', ''], ['', ''], ['', ''], ['', '']] }]);
|
||||
send({ kind: 'input', node: notebook, app: 'webBrowser', action: 'open', args: { address: '141.99.5.10' }, reqId: 'open' });
|
||||
expect(ofKind(got, 'ack').map((each) => each.reqId)).toEqual(['open']);
|
||||
expect(views(got, notebook, 'webBrowser').at(-1)).toEqual({ address: 'http://141.99.5.10/', loading: 'http://141.99.5.10/', arrived: false, page: { kind: 'start' } });
|
||||
run(20_000);
|
||||
const page = views(got, notebook, 'webBrowser').at(-1) as { arrived: boolean; page: { kind: string; html: string; images: Record<string, unknown> } };
|
||||
expect(page.arrived).toBe(true);
|
||||
expect(page.page.kind).toBe('html');
|
||||
expect(Object.keys(page.page.images).length).toBeGreaterThan(0);
|
||||
// The browser's lines are its view; the server's go on as lines.
|
||||
expect(ofKind(got, 'app').some((each) => each.app === 'webBrowser')).toBe(false);
|
||||
expect(ofKind(got, 'app').filter((each) => each.app === 'webServer').flatMap((each) => each.lines as { kind: string }[]).some((line) => line.kind === 'received')).toBe(true);
|
||||
// Nothing new: no view sent again.
|
||||
const before = got.length;
|
||||
run(1_000);
|
||||
expect(ofKind(got.slice(before), 'view')).toEqual([]);
|
||||
// The server's button, check box and table.
|
||||
send({ kind: 'input', node: server, app: 'webServer', action: 'setActive', args: { active: false }, reqId: 's1' });
|
||||
send({ kind: 'input', node: server, app: 'webServer', action: 'useVirtualHosts', args: { use: true }, reqId: 's2' });
|
||||
send({ kind: 'input', node: server, app: 'webServer', action: 'setVirtualHost', args: { row: 4, column: 1, value: 'ordner' }, reqId: 's3' });
|
||||
expect(views(got, server, 'webServer').at(-1)).toEqual({ active: false, useVirtualHosts: true, virtualHosts: [['', ''], ['', ''], ['', ''], ['', ''], ['', 'ordner']] });
|
||||
expect(views(got, server, 'webServer')).toHaveLength(5);
|
||||
// A stop: the browser keeps its page (Java's window does), the server has none; a client subscribing again gets both.
|
||||
send({ kind: 'stop' });
|
||||
expect(views(got, server, 'webServer').at(-1)).toBeNull();
|
||||
const again = got.length;
|
||||
send({ kind: 'sub', topics: [`app:${notebook}/webBrowser`] });
|
||||
expect(views(got.slice(again), notebook, 'webBrowser')).toEqual([page]);
|
||||
});
|
||||
|
||||
it('follows the links of a page, and writes the routers’ pages in the language of the start', () => {
|
||||
const { node, send, run, got, room } = setUp();
|
||||
const notebook = node('Notebook 1.10').id;
|
||||
const gateway = docSnapshot(exampleDoc()).deviceConfigurations.get(notebook)!.defaultGateway;
|
||||
send({ kind: 'sub', topics: [`app:${notebook}/webBrowser`] });
|
||||
const page = () => (views(got, notebook, 'webBrowser').at(-1) as { address: string; page: { html?: string } });
|
||||
send({ kind: 'start' });
|
||||
run(TICK_MILLISECONDS);
|
||||
send({ kind: 'input', node: notebook, app: 'webBrowser', action: 'open', args: { address: gateway }, reqId: 'r' });
|
||||
run(20_000);
|
||||
expect(page().page.html).toContain('Administration des Vermittlungsrechners');
|
||||
send({ kind: 'input', node: notebook, app: 'webBrowser', action: 'follow', args: { href: 'routes' }, reqId: 'f' });
|
||||
run(20_000);
|
||||
expect([page().address, page().page.html]).toEqual([`http://${gateway}/routes`, expect.stringContaining('Routing-/Weiterleitungs-Tabelle')]);
|
||||
room.stop();
|
||||
send({ kind: 'start', language: 'en' });
|
||||
run(TICK_MILLISECONDS);
|
||||
send({ kind: 'input', node: notebook, app: 'webBrowser', action: 'open', args: { address: gateway }, reqId: 'r' });
|
||||
run(20_000);
|
||||
expect(page().page.html).toContain('Router Administration');
|
||||
});
|
||||
|
||||
it('names a wrong action in the windows', () => {
|
||||
const { node, send, run, got } = setUp();
|
||||
const notebook = node('Notebook 1.10').id;
|
||||
const server = node('Webserver 5.10').id;
|
||||
const dns = node('DNS-Server 2.10').id;
|
||||
send({ kind: 'start' });
|
||||
run(TICK_MILLISECONDS);
|
||||
const cases: [string, string, string, Record<string, unknown>, string][] = [
|
||||
[notebook, 'webBrowser', 'open', {}, 'badArgs'],
|
||||
[notebook, 'webBrowser', 'follow', { href: 7 }, 'badArgs'],
|
||||
[notebook, 'webBrowser', 'follow', { href: 'a', post: 1 }, 'badArgs'],
|
||||
[notebook, 'webBrowser', 'back', {}, 'unknownAction'],
|
||||
[dns, 'webBrowser', 'open', { address: 'x' }, 'unknownApp'],
|
||||
[server, 'webServer', 'setActive', { active: 'yes' }, 'badArgs'],
|
||||
[server, 'webServer', 'useVirtualHosts', { use: 1 }, 'badArgs'],
|
||||
[server, 'webServer', 'setVirtualHost', { row: 5, column: 0, value: 'a' }, 'badArgs'],
|
||||
[server, 'webServer', 'setVirtualHost', { row: -1, column: 0, value: 'a' }, 'badArgs'],
|
||||
[server, 'webServer', 'setVirtualHost', { row: 1.5, column: 0, value: 'a' }, 'badArgs'],
|
||||
[server, 'webServer', 'setVirtualHost', { row: 0, column: 2, value: 'a' }, 'badArgs'],
|
||||
[server, 'webServer', 'setVirtualHost', { row: 0, column: 1, value: 2 }, 'badArgs'],
|
||||
[server, 'webServer', 'restart', {}, 'unknownAction'],
|
||||
[notebook, 'webServer', 'setActive', { active: true }, 'unknownApp'],
|
||||
[server, 'emailServer', 'setActive', { active: true }, 'unknownAction'],
|
||||
];
|
||||
for (const [target, app, action, args, code] of cases) {
|
||||
send({ kind: 'input', node: target, app, action, args, reqId: `${app}.${action}.${JSON.stringify(args)}` });
|
||||
expect(ofKind(got, 'error').at(-1), `${app} ${action} ${JSON.stringify(args)}`).toMatchObject({ code, reqId: `${app}.${action}.${JSON.stringify(args)}` });
|
||||
}
|
||||
expect(ofKind(got, 'ack')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user