// 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 }; test.beforeAll(async () => { server = await serveEditor(); }); test.afterAll(async () => { await server.close(); }); async function start(page: Page): Promise { 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(/\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 '); 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 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 }); });