Files
mdg-filius/packages/editor/browser/mail.spec.ts
T
julian.kiedaischandClaude Opus 5.5 16d9cc2095 feat(editor): the simulation opens sheets, not windows in the window
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>
2026-10-10 17:18:25 +00:00

195 lines
12 KiB
TypeScript

// The e-mail program and the e-mail server on a computer's desktop in
// Chromium (F4.6e), on FILIUS' example email_komplett: a mail written with
// Java's checks and sent, the server's accounts and lines, the mail fetched,
// chosen, answered and deleted, the account dialog; the server's new account,
// its question before it removes one, its domain only while it is off.
import { expect, test, type Page } from '@playwright/test';
import { drawing, onDevice, openEditor, 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();
});
async function start(page: Page): Promise<void> {
await page.getByRole('button', { name: 'Wechselt in den Aktionsmodus' }).click();
await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'simulation');
}
/** Opens a computer's desktop with a tap and a program's window on it. */
async function openProgram(page: Page, name: string, program: string) {
const id = (await drawing(page)).nodes.find((node) => node.name === name)!.id;
const desktop = await showDesktop(page, id);
await desktop.locator(`[data-program="${program}"]`).click();
return { desktop, window: desktop.locator(`[data-program-window="${program}"]`) };
}
test("writes, sends, fetches, answers and deletes mails as Java's e-mail program does", async ({ page }) => {
test.setTimeout(120_000);
await openEditor(page, server.base, 'email_komplett.fls');
await start(page);
const one = await openProgram(page, 'Rechner 1', 'emailClient');
const window = one.window;
await expect(window.locator('.mail-welcome')).toHaveText('Willkommen in der E-Mail-Anwendung!');
await expect(window.getByRole('tab', { name: 'Posteingang' })).toHaveAttribute('aria-selected', 'true');
// The account dialog shows the account, as Java's does.
await window.getByRole('button', { name: 'Konto einrichten' }).click();
const dialog = window.getByRole('form', { name: 'E-Mail-Konto verwalten' });
await expect(dialog.getByRole('textbox', { name: 'Name:', exact: true })).toHaveValue('Rechner 1');
await expect(dialog.getByRole('textbox', { name: 'E-Mail-Adresse:' })).toHaveValue('rechner1@filius.de');
await dialog.getByRole('textbox', { name: 'SMTP-Port:' }).fill('25x');
await dialog.getByRole('button', { name: 'Speichern' }).click();
await expect(window.getByRole('alertdialog')).toHaveText(/Die Angaben sind entweder fehlerhaft oder unvollständig!/);
await window.getByRole('alertdialog').getByRole('button', { name: 'OK' }).click();
await dialog.getByRole('button', { name: 'Abbrechen' }).click();
await expect(dialog).toHaveCount(0);
// Writing: Java's checks, then sent; the window shows the sent mails.
await window.getByRole('button', { name: 'Neue E-Mail verfassen' }).click();
const compose = window.getByRole('form', { name: 'E-Mail verfassen' });
await compose.getByRole('textbox', { name: 'An:' }).fill('rechner2@filius.de, kein');
await expect(compose.getByRole('textbox', { name: 'An:' })).toHaveAttribute('aria-invalid', 'true');
await compose.getByRole('button', { name: 'Senden' }).click();
await expect(window.getByRole('alertdialog')).toHaveText(/Mindestens eine eingegebene E-Mail-Adresse ist nicht korrekt!/);
await window.getByRole('alertdialog').getByRole('button', { name: 'OK' }).click();
await compose.getByRole('textbox', { name: 'An:' }).fill('');
await compose.getByRole('button', { name: 'Senden' }).click();
await expect(window.getByRole('alertdialog')).toHaveText(/Sie müssen mindestens eine Empfänger-Adresse eingeben!/);
await window.getByRole('alertdialog').getByRole('button', { name: 'OK' }).click();
await compose.getByRole('textbox', { name: 'An:' }).fill('rechner2@filius.de');
await compose.getByRole('textbox', { name: 'Betreff:' }).fill('Hallo Zwei');
await compose.getByRole('textbox', { name: 'Text der E-Mail' }).fill('Erste Zeile\nZweite Zeile');
await compose.getByRole('button', { name: 'Senden' }).click();
await expect(compose).toHaveCount(0);
await expect(window.getByRole('tab', { name: 'Gesendete' })).toHaveAttribute('aria-selected', 'true');
await expect(window.getByRole('tabpanel').locator('tbody tr').last()).toHaveText(/<rechner2@filius\.de>\s*Hallo Zwei/, { timeout: 30_000 });
await one.desktop.getByRole('button', { name: 'Schließen' }).click();
// The server holds it; its lines show the dialogue.
const mailServer = await openProgram(page, 'Mail-Server (filius.de)', 'emailServer');
await mailServer.window.getByRole('tab', { name: 'Konten-Liste' }).click();
await expect(mailServer.window.getByRole('tabpanel').locator('tbody tr')).toHaveText([/rechner1@filius\.de/, /rechner2@filius\.de\s*\d+/]);
await mailServer.window.getByRole('tab', { name: 'Log Fenster' }).click();
await expect(mailServer.window.getByRole('tabpanel')).toContainText('E-Mail für rechner2 im Postfach gespeichert.');
await mailServer.desktop.getByRole('button', { name: 'Schließen' }).click();
// Fetched, chosen, answered, deleted.
const two = await openProgram(page, 'Rechner 2', 'emailClient');
// Fetching shows the inbox, from any folder.
await two.window.getByRole('tab', { name: 'Gesendete' }).click();
await two.window.getByRole('button', { name: 'E-Mails abrufen' }).click();
await expect(two.window.getByRole('tab', { name: 'Posteingang' })).toHaveAttribute('aria-selected', 'true');
const rows = two.window.getByRole('tabpanel').locator('tbody tr');
await expect(rows.filter({ hasText: 'Hallo Zwei' })).toHaveCount(1, { timeout: 30_000 });
await expect(rows.filter({ hasText: 'Hallo Zwei' })).toHaveText(/Rechner 1\s*Hallo Zwei/);
await rows.filter({ hasText: 'Hallo Zwei' }).click();
await expect(two.window.locator('.mail-preview pre')).toHaveText(/Erste Zeile\nZweite Zeile/);
await two.window.getByRole('button', { name: 'E-Mail beantworten' }).click();
const reply = two.window.getByRole('form', { name: 'E-Mail verfassen' });
await expect(reply.getByRole('textbox', { name: 'An:' })).toHaveValue('Rechner 1 <rechner1@filius.de>');
await expect(reply.getByRole('textbox', { name: 'Betreff:' })).toHaveValue('RE: Hallo Zwei');
await expect(reply.getByRole('textbox', { name: 'Text der E-Mail' })).toHaveValue(/^\n\nRechner 1 <rechner1@filius\.de> schrieb:\n> Erste Zeile\n> Zweite Zeile/);
// Sent: the window shows the sent mails, the answer among them.
await reply.getByRole('button', { name: 'Senden' }).click();
await expect(two.window.getByRole('tab', { name: 'Gesendete' })).toHaveAttribute('aria-selected', 'true');
await expect(rows.filter({ hasText: 'RE: Hallo Zwei' })).toHaveCount(1, { timeout: 30_000 });
// The mail chosen belongs to its folder: chosen here, nothing is chosen in the inbox, and deleting deletes nothing there (Java's deleted one).
await rows.filter({ hasText: 'RE: Hallo Zwei' }).click();
await expect(two.window.locator('.mail-preview pre')).toHaveText(/schrieb:/);
await two.window.getByRole('tab', { name: 'Posteingang' }).click();
const inboxBefore = await rows.count();
await expect(two.window.locator('.mail-welcome')).toBeVisible();
await two.window.getByRole('button', { name: 'E-Mail löschen' }).click();
await expect(rows).toHaveCount(inboxBefore);
const before = await rows.count();
await rows.filter({ hasText: 'Hallo Zwei' }).click();
await two.window.getByRole('button', { name: 'E-Mail löschen' }).click();
await expect(rows).toHaveCount(before - 1);
await expect(rows.filter({ hasText: 'Hallo Zwei' })).toHaveCount(0);
await expect(two.window.locator('.mail-welcome')).toBeVisible();
});
test("makes and removes accounts and sets the domain as Java's e-mail server window does", async ({ page }) => {
test.setTimeout(120_000);
await openEditor(page, server.base, 'email_komplett.fls');
await start(page);
const { window } = await openProgram(page, 'Mail-Server (filius.de)', 'emailServer');
const domain = window.getByRole('textbox', { name: 'Maildomain:' });
await expect(domain).toHaveValue('filius.de');
await expect(domain).toBeDisabled();
// A new account: Java's checks, one the server has already, then made.
const form = window.getByRole('tabpanel', { name: 'Neues Konto' });
const create = form.getByRole('button', { name: 'Konto erstellen' });
await create.click();
await expect(form.getByRole('status')).toHaveText('Geben Sie einen Benutzernamen und ein Passwort ein!');
await form.getByRole('textbox', { name: 'Benutzername:' }).fill('carl os');
await form.getByLabel('Passwort:').fill('x');
await create.click();
await expect(form.getByRole('status')).toHaveText('Der Benutzername darf keine Leerzeichen enthalten!');
await form.getByRole('textbox', { name: 'Benutzername:' }).fill('RECHNER1');
await create.click();
await expect(form.getByRole('status')).toHaveText('Das Benutzerkonto RECHNER1 gibt es schon.');
await form.getByRole('textbox', { name: 'Benutzername:' }).fill('neu');
await form.getByRole('textbox', { name: 'Vorname und Nachname:' }).fill('Neue Nutzerin');
await create.click();
await expect(form.getByRole('status')).toHaveText('Das Benutzerkonto neu wurde angelegt.');
await expect(form.getByRole('textbox', { name: 'Benutzername:' })).toHaveValue('');
// The list: chosen, asked, removed.
await window.getByRole('tab', { name: 'Konten-Liste' }).click();
const list = window.getByRole('tabpanel', { name: 'Konten-Liste' });
await expect(list.locator('tbody tr')).toHaveText([/rechner1@filius\.de/, /rechner2@filius\.de/, /neu@filius\.de\s*0/]);
await expect(list.getByRole('button', { name: 'Markiertes Konto entfernen' })).toBeDisabled();
await list.locator('tbody tr').filter({ hasText: 'neu@' }).click();
await list.getByRole('button', { name: 'Markiertes Konto entfernen' }).click();
const question = list.getByRole('alertdialog', { name: 'Sicherheitsabfrage!' });
await expect(question).toContainText("Wollen Sie das Konto 'neu' wirklich entfernen?");
await question.getByRole('button', { name: 'Abbrechen' }).click();
await expect(list.locator('tbody tr')).toHaveCount(3);
await list.getByRole('button', { name: 'Markiertes Konto entfernen' }).click();
await list.getByRole('alertdialog').getByRole('button', { name: 'Ja' }).click();
await expect(list.locator('tbody tr')).toHaveCount(2);
// Stopped: the domain can be set, red where Java's pattern does not take it.
await window.getByRole('button', { name: 'Anhalten' }).click();
await expect(window.getByRole('button', { name: 'Starten' })).toBeVisible();
await expect(domain).toBeEnabled();
await domain.fill('kein domain!');
await expect(domain).toHaveAttribute('aria-invalid', 'true');
await domain.fill('schule.de');
await domain.press('Enter');
await expect(list.locator('tbody tr')).toHaveText([/rechner1@schule\.de/, /rechner2@schule\.de/]);
await window.getByRole('tab', { name: 'Log Fenster' }).click();
await expect(window.getByRole('tabpanel')).toContainText('POP3- und SMTP-Server wurden angehalten.');
});
test("shows the program's failure as Java's dialog does, and saves a new account", async ({ page }) => {
test.setTimeout(120_000);
await openEditor(page, server.base, 'email_komplett.fls');
await start(page);
const { window } = await openProgram(page, 'Rechner 1', 'emailClient');
await window.getByRole('button', { name: 'Konto einrichten' }).click();
const dialog = window.getByRole('form', { name: 'E-Mail-Konto verwalten' });
await dialog.getByRole('textbox', { name: 'SMTP-Server:' }).fill('niemand.filius.de');
await dialog.getByRole('textbox', { name: 'Name:', exact: true }).fill('Anna Maria Muster');
await dialog.getByRole('button', { name: 'Speichern' }).click();
await expect(dialog).toHaveCount(0);
await window.getByRole('button', { name: 'Konto einrichten' }).click();
await expect(dialog.getByRole('textbox', { name: 'Name:', exact: true })).toHaveValue('Anna Maria Muster');
await expect(dialog.getByRole('textbox', { name: 'SMTP-Server:' })).toHaveValue('niemand.filius.de');
await dialog.getByRole('button', { name: 'Abbrechen' }).click();
await window.getByRole('button', { name: 'Neue E-Mail verfassen' }).click();
const compose = window.getByRole('form', { name: 'E-Mail verfassen' });
await compose.getByRole('textbox', { name: 'An:' }).fill('rechner2@filius.de');
await compose.getByRole('button', { name: 'Senden' }).click();
await expect(window.getByRole('alertdialog')).toHaveText(/Ziel konnte nicht bestimmt werden!/, { timeout: 60_000 });
});