Files
mdg-filius/packages/editor/browser/files.spec.ts
T
julian.kiedaischandClaude Opus 5.5 3dc2713f81 feat: F4.6f3 the file explorer and the image viewer
Oracle FileExplorerWindowRun uses Java's explorer and viewer windows as a
person does (the right-click menu, Java's questions answered, the import
window, the viewer's open). Both windows work on the document's files
through the core's operations: the tree of folders and files, the chosen
folder's entries, Java's menu (also as buttons, no right click needed),
copy that stays and cut that goes once pasted, the import; the viewer's
picture and title. Not taken from Java: a file chosen in the tree taken
for a folder, pictures laid over each other, a text file as an empty
picture. writeFiles numbers the entries in the file system's order again
when a rename made a new key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 18:13:22 +00:00

148 lines
9.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 } 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 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="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.');
});