feat(editor): the simulation's properties in a sheet of their own, with a restart for everyone
Who started the simulation, "Pakete verwerfen" and the dropped packets move from the toolbar into a properties sheet beside the surface. New in it: restart the simulation, after asking. The room learns a restart message: stopped as by stop, every capture, dropped frame and program window emptied, started anew and named after who restarted; refused for readers and frozen projects, logged, and waiting for asked blobs like a start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -472,6 +472,14 @@ describe("a simulation room's browser and web server windows", () => {
|
||||
send({ kind: 'input', node: notebook, app: 'webBrowser', action: 'open', args: { address: gateway }, reqId: 'r' });
|
||||
run(20_000);
|
||||
expect(page().page.html).toContain('Router Administration');
|
||||
// Started anew: in the language of the restart, German without one.
|
||||
for (const [restart, title] of [[{ kind: 'restart' }, 'Administration des Vermittlungsrechners'], [{ kind: 'restart', language: 'en' }, 'Router Administration']] as const) {
|
||||
send(restart);
|
||||
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(title);
|
||||
}
|
||||
});
|
||||
|
||||
it('names a wrong action in the windows', () => {
|
||||
@@ -595,3 +603,78 @@ describe("a simulation room's e-mail windows", () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('a simulation room started anew', () => {
|
||||
/** A window sent from its beginning, and a view. */
|
||||
const anew = (got: readonly HostMessage[], node: Id, app: string) => ofKind(got, 'app').filter((each) => each.node === node && each.app === app && each.from === 0);
|
||||
const viewsOf = (got: readonly HostMessage[], node: Id, app: string) => ofKind(got, 'view').filter((each) => each.node === node && each.app === app);
|
||||
|
||||
it('empties every window and list for every client, names who did it, keeps what the devices wrote, and numbers anew', () => {
|
||||
const { room, got, send, node, run } = setUp();
|
||||
const benGot: HostMessage[] = [];
|
||||
const ben = room.connect((message) => benGot.push(message), 'Ben');
|
||||
const notebook = node('Notebook 1.10');
|
||||
const server = node('Webserver 5.10').id;
|
||||
const port = notebook.ports[0]!.id;
|
||||
const topics = ['activity', `trace:${port}`, 'dropped', `app:${notebook.id}/terminal`, `app:${notebook.id}/webBrowser`, `app:${server}/webServer`];
|
||||
send({ kind: 'sub', topics });
|
||||
room.receive(ben, { v: 1, kind: 'sub', topics });
|
||||
send({ kind: 'start' });
|
||||
run(TICK_MILLISECONDS);
|
||||
const fresh = { terminal: anew(got, notebook.id, 'terminal').at(-1)!, browser: viewsOf(got, notebook.id, 'webBrowser').at(-1)! };
|
||||
const enter = (line: string) => send({ kind: 'input', node: notebook.id, app: 'terminal', action: 'enter', args: { line }, reqId: line });
|
||||
enter('touch bleibt.txt');
|
||||
enter('ping 141.99.5.10');
|
||||
send({ kind: 'input', node: notebook.id, app: 'webBrowser', action: 'open', args: { address: '141.99.5.10' }, reqId: 'open' });
|
||||
run(20_000);
|
||||
send({ kind: 'drop', on: true });
|
||||
enter('ping 141.99.5.10');
|
||||
run(5_000);
|
||||
send({ kind: 'drop', on: false });
|
||||
expect(ofKind(got, 'dropped').flatMap((each) => each.units).length).toBeGreaterThan(0);
|
||||
expect(ofKind(got, 'app').filter((each) => each.app === 'webServer').flatMap((each) => each.lines).length).toBeGreaterThan(0);
|
||||
|
||||
const before = { mia: got.length, ben: benGot.length };
|
||||
room.receive(ben, { v: 1, kind: 'restart' });
|
||||
for (const [messages, from] of [[got, before.mia], [benGot, before.ben]] as const) {
|
||||
const after = messages.slice(from);
|
||||
expect(ofKind(after, 'state').map((each) => [each.mode, each.startedBy])).toEqual([
|
||||
['design', undefined],
|
||||
['simulation', 'Ben'],
|
||||
]);
|
||||
expect(ofKind(after, 'trace').at(-1)).toMatchObject({ portId: port, reset: true, frames: [] });
|
||||
expect(ofKind(after, 'dropped').at(-1)).toMatchObject({ reset: true, units: [] });
|
||||
// The command line as at the first start, the browser on its start page, the server's log empty.
|
||||
expect(anew(after, notebook.id, 'terminal').at(-1)!.lines).toEqual(fresh.terminal.lines);
|
||||
expect(viewsOf(after, notebook.id, 'webBrowser').at(-1)!.view).toEqual(fresh.browser.view);
|
||||
expect(anew(after, server, 'webServer').at(-1)!.lines).toEqual([]);
|
||||
}
|
||||
// What the devices wrote is still there; a ping is numbered from 1, and the notebook's cable lights up with its first frame.
|
||||
const restarted = got.length;
|
||||
const cable = readProject(exampleDoc()).graph.links.find((link) => link.sourcePortId === port || link.targetPortId === port)!.id;
|
||||
enter('ls');
|
||||
enter('ping 141.99.5.10');
|
||||
for (let waited = 0; !ofKind(got.slice(restarted), 'trace').some((each) => each.frames.length > 0) && waited < 5_000; waited += TICK_MILLISECONDS) run(TICK_MILLISECONDS);
|
||||
expect(ofKind(got.slice(restarted), 'activity').at(-1)!.links).toContain(cable);
|
||||
run(20_000);
|
||||
expect(ofKind(got.slice(restarted), 'app').flatMap((each) => each.lines as { text?: string }[]).some((line) => (line.text ?? '').includes('bleibt.txt'))).toBe(true);
|
||||
const frames = ofKind(got.slice(restarted), 'trace').flatMap((each) => each.frames);
|
||||
expect(frames.length).toBeGreaterThan(8);
|
||||
expect(frames.map((frame) => frame.number)).toEqual(frames.map((_, index) => index + 1));
|
||||
});
|
||||
|
||||
it('starts from the design emptied, where a start keeps the last run', () => {
|
||||
const { got, send, node, run } = setUp();
|
||||
const notebook = node('Notebook 1.10');
|
||||
const port = notebook.ports[0]!.id;
|
||||
send({ kind: 'start' });
|
||||
send({ kind: 'input', node: notebook.id, app: 'terminal', action: 'enter', args: { line: 'ping 141.99.5.10' }, reqId: 'r1' });
|
||||
run(5_000);
|
||||
send({ kind: 'stop' });
|
||||
send({ kind: 'restart' });
|
||||
expect(ofKind(got, 'state').at(-1)!.mode).toBe('simulation');
|
||||
send({ kind: 'sub', topics: [`trace:${port}`, `app:${notebook.id}/terminal`] });
|
||||
expect(ofKind(got, 'trace').at(-1)).toMatchObject({ reset: true, frames: [] });
|
||||
expect(ofKind(got, 'app').at(-1)!.lines.some((line) => /ping/.test(JSON.stringify(line)))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user