A tap on a device in the simulation now only chooses it; its three-dot button (or a long press) opens what Java's click opened: the desktop, and newly a switch's SAT table and a home router's NAT table, beside each card's data exchange. The desktop, the tables and the data exchange are no windows in the window any more but the simulation's sheet (SimulationSheet), after the dashboard's AppSheet: below the toolbar to the bottom, a head with icon, title, who else has it open, see-through and close. One at a time — it covers the surface. It stays over a return to the design with the program open on a desktop; a sheet whose device was removed closes. See-through fades the body (0.55) and keeps the head; the choice is kept in the browser. Java opens these as movable windows, several at once, with a click; named in CHANGES.md. Tests open through the menu (showFromMenu, showDesktop in editorPage.ts). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
146 lines
8.9 KiB
TypeScript
146 lines
8.9 KiB
TypeScript
// The file explorer and the image viewer on a computer's desktop in
|
||
// Chromium (F4.6f3), on browser/fixtures/web-pages.fls
|
||
// (test/webPagesFixture.test.ts): installed while the simulation runs; a
|
||
// folder made, an entry copied and pasted through Java's right-click menu,
|
||
// renamed and deleted through the buttons (after Java's questions), what the
|
||
// command line makes shown at once, a local file imported; a picture
|
||
// imported and opened in the viewer, a text file refused there.
|
||
|
||
import { dirname, join } from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
import { expect, test, type Page } from '@playwright/test';
|
||
import { drawing, onDevice, serveEditor, showDesktop } 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 here = dirname(fileURLToPath(import.meta.url));
|
||
const FIXTURE = join(here, 'fixtures/web-pages.fls');
|
||
const PICTURE = join(here, '../src/assets/desktop/ordner.png');
|
||
|
||
async function openPages(page: Page): Promise<void> {
|
||
await page.goto(`${server.base}?example=new`);
|
||
await page.waitForSelector('svg.filius-canvas');
|
||
await page.locator('input[type="file"]').first().setInputFiles(FIXTURE);
|
||
await expect.poll(async () => (await drawing(page).catch(() => ({ nodes: [] }))).nodes.length).toBe(4);
|
||
await page.getByRole('button', { name: 'Wechselt in den Aktionsmodus' }).click();
|
||
await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'simulation');
|
||
}
|
||
|
||
test('makes, copies, pastes, renames, deletes and imports as Java’s explorer does, and shows a picture in the viewer', async ({ page }) => {
|
||
test.setTimeout(120_000);
|
||
await openPages(page);
|
||
const id = (await drawing(page)).nodes.find((node) => node.name === 'Server')!.id;
|
||
const desktop = await showDesktop(page, id);
|
||
await desktop.locator('[data-program="installation"]').click();
|
||
const installation = desktop.locator('[data-program-window="installation"]');
|
||
const available = installation.locator('[data-list="available"] [role="option"]');
|
||
for (const program of ['Datei-Explorer', 'Bildbetrachter', 'Befehlszeile']) await available.filter({ hasText: program }).click();
|
||
await installation.getByRole('button', { name: 'Installieren' }).click();
|
||
await installation.getByRole('button', { name: 'Änderungen annehmen' }).click();
|
||
|
||
await desktop.locator('[data-program="fileExplorer"]').click();
|
||
const window = desktop.locator('[data-program-window="fileExplorer"]');
|
||
await expect(window.locator('.program-titlebar h3')).toHaveText('Datei-Explorer');
|
||
const bar = window.getByRole('toolbar', { name: 'Aktionen' });
|
||
await expect(bar.getByRole('button')).toHaveText(['Importieren', 'Neuer Ordner', 'Löschen', 'Ausschneiden', 'Kopieren', 'Einfügen', 'Umbenennen']);
|
||
await expect(bar.getByRole('button', { name: 'Löschen' })).toBeDisabled();
|
||
await expect(bar.getByRole('button', { name: 'Einfügen' })).toBeDisabled();
|
||
const tree = window.getByRole('list', { name: 'Ordner und Dateien' }).getByRole('button');
|
||
const entries = window.getByRole('list', { name: 'Inhalt des Ordners' }).getByRole('button');
|
||
await expect(entries).toHaveText(['webserver']);
|
||
await expect(tree.first()).toHaveText('/');
|
||
|
||
// A new folder, named in Java's question.
|
||
await bar.getByRole('button', { name: 'Neuer Ordner' }).click();
|
||
const naming = window.getByRole('dialog', { name: 'Neuer Ordner' });
|
||
await naming.getByRole('textbox', { name: 'Neuer Name:' }).fill('ordner');
|
||
await naming.getByRole('button', { name: 'OK' }).click();
|
||
await expect(entries).toHaveText(['webserver', 'ordner']);
|
||
|
||
// Java's right-click menu: on an entry, then on the free space with something copied.
|
||
await entries.filter({ hasText: 'webserver' }).click({ button: 'right' });
|
||
await expect(window.getByRole('menuitem')).toHaveText(['Löschen', 'Ausschneiden', 'Kopieren', 'Umbenennen']);
|
||
await window.getByRole('menuitem', { name: 'Kopieren' }).click();
|
||
await window.locator('.file-explorer-tree [data-path="/ordner"]').click();
|
||
await expect(entries).toHaveCount(0);
|
||
await window.getByRole('list', { name: 'Inhalt des Ordners' }).click({ button: 'right', position: { x: 20, y: 20 } });
|
||
await expect(window.getByRole('menuitem')).toHaveText(['Neuer Ordner', 'Einfügen']);
|
||
await window.getByRole('menuitem', { name: 'Einfügen' }).click();
|
||
await expect(entries).toHaveText(['webserver']);
|
||
// A folder of the list opens with a double click; into the copied folder itself: Java's message.
|
||
await entries.filter({ hasText: 'webserver' }).dblclick();
|
||
await expect(window.locator('.file-explorer-tree [data-path="/ordner/webserver"]')).toHaveAttribute('aria-current', 'true');
|
||
await window.locator('.file-explorer-tree [data-path="/webserver"]').click();
|
||
await bar.getByRole('button', { name: 'Einfügen' }).click();
|
||
await expect(window.getByRole('alert')).toHaveText('Unzulässiger Zielordner!');
|
||
|
||
// Renamed through the button: the old name first, a name there already is said.
|
||
await window.locator('.file-explorer-tree [data-path="/"]').click();
|
||
await entries.filter({ hasText: 'ordner' }).click();
|
||
await bar.getByRole('button', { name: 'Umbenennen' }).click();
|
||
const renaming = window.getByRole('dialog', { name: 'Umbenennen' });
|
||
await expect(renaming.getByRole('textbox', { name: 'Neuer Name:' })).toHaveValue('ordner');
|
||
await renaming.getByRole('textbox', { name: 'Neuer Name:' }).fill('webserver');
|
||
await renaming.getByRole('button', { name: 'OK' }).click();
|
||
await expect(renaming.getByRole('alert')).toHaveText('Den Namen „webserver“ gibt es in diesem Ordner schon.');
|
||
await renaming.getByRole('textbox', { name: 'Neuer Name:' }).fill('Kopie');
|
||
await renaming.getByRole('button', { name: 'OK' }).click();
|
||
await expect(entries).toHaveText(['webserver', 'Kopie']);
|
||
|
||
// Deleted after Java's "Sicher?": no, then yes.
|
||
await bar.getByRole('button', { name: 'Löschen' }).click();
|
||
await window.getByRole('alertdialog', { name: 'Sicher?' }).getByRole('button', { name: 'Nein' }).click();
|
||
await expect(entries).toHaveText(['webserver', 'Kopie']);
|
||
await bar.getByRole('button', { name: 'Löschen' }).click();
|
||
await window.getByRole('alertdialog', { name: 'Sicher?' }).getByRole('button', { name: 'Ja' }).click();
|
||
await expect(entries).toHaveText(['webserver']);
|
||
|
||
// What the command line makes is there at once.
|
||
await desktop.locator('.desktop-home').click();
|
||
await desktop.locator('[data-program="commandPrompt"]').click();
|
||
const input = desktop.locator('input[name="terminalInput"]');
|
||
await input.fill('mkdir extern');
|
||
await input.press('Enter');
|
||
await desktop.locator('.desktop-home').click();
|
||
await desktop.locator('[data-program="fileExplorer"]').click();
|
||
await expect(entries).toHaveText(['webserver', 'extern']);
|
||
|
||
// A local picture imported: the new name is the local file's at first.
|
||
await bar.getByRole('button', { name: 'Importieren' }).click();
|
||
const importing = window.getByRole('dialog', { name: 'Datei importieren' });
|
||
await importing.getByRole('button', { name: 'Datei importieren' }).click();
|
||
await expect(importing.getByRole('status')).toHaveText('Eingabe- und Ausgabedatei müssen angegeben werden!');
|
||
await importing.locator('input[type="file"]').setInputFiles(PICTURE);
|
||
await expect(importing.getByRole('textbox', { name: 'Neuer Name:' })).toHaveValue('ordner.png');
|
||
await importing.getByRole('textbox', { name: 'Neuer Name:' }).fill('bild.png');
|
||
await importing.getByRole('button', { name: 'Datei importieren' }).click();
|
||
await expect(importing.getByRole('status')).toHaveText('Datei erfolgreich importiert!');
|
||
await importing.getByRole('button', { name: 'Schließen' }).click();
|
||
await expect(entries).toHaveText(['webserver', 'extern', 'bild.png']);
|
||
|
||
// The viewer: the picture, its name as the title; a text file is said.
|
||
await desktop.locator('.desktop-home').click();
|
||
await desktop.locator('[data-program="imageViewer"]').click();
|
||
const viewer = desktop.locator('[data-program-window="imageViewer"]');
|
||
await expect(viewer.locator('.program-titlebar h3')).toHaveText('Bildbetrachter');
|
||
await viewer.getByRole('button', { name: 'Öffnen' }).click();
|
||
const dialog = viewer.getByRole('dialog', { name: 'Datei Öffnen' });
|
||
await dialog.locator('.file-dialog-entries button', { hasText: 'bild.png' }).click();
|
||
await dialog.getByRole('button', { name: 'Öffnen' }).click();
|
||
await expect(viewer.locator('.program-titlebar h3')).toHaveText('bild.png');
|
||
await expect.poll(() => viewer.getByRole('img', { name: 'Bild' }).evaluate((image: HTMLImageElement) => image.naturalWidth)).toBe(16);
|
||
await viewer.getByRole('button', { name: 'Öffnen' }).click();
|
||
await dialog.locator('.file-dialog-entries button', { hasText: 'webserver' }).click();
|
||
await dialog.locator('.file-dialog-entries button', { hasText: 'index.html' }).click();
|
||
await dialog.getByRole('button', { name: 'Öffnen' }).click();
|
||
await expect(dialog.getByRole('alert')).toHaveText('„index.html“ ist keine Bilddatei.');
|
||
});
|