// Gnutella on two notebooks' desktops in Chromium (F4.6g4), on FILIUS' // example email_komplett with the program installed during the run: Java's // four tabs, an address refused with Java's words, a peer joined, a file // found on the other notebook and downloaded (the files tab shows it), the // number of peers saved (also one Java refuses, and what is no number), the // network left. import { expect, test, type Locator, type Page } from '@playwright/test'; import { drawing, onDevice, openEditor, serveEditor } 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 idOf(page: Page, name: string) { return (await drawing(page)).nodes.find((node) => node.name === name)!.id; } async function desktopOf(page: Page, name: string) { const id = await idOf(page, name); const at = await onDevice(page, id); await page.mouse.click(at.x, at.y); return page.locator(`section[data-desktop="${id}"]`); } async function install(desktop: Locator, program: string) { await desktop.locator('[data-program="installation"]').click(); const installation = desktop.locator('[data-program-window="installation"]'); await installation.locator('[data-list="available"] [role="option"]').filter({ hasText: program }).click(); await installation.getByRole('button', { name: 'Installieren' }).click(); await installation.getByRole('button', { name: 'Änderungen annehmen' }).click(); } test('joins, searches and downloads as Java’s Gnutella window does', async ({ page }) => { test.setTimeout(120_000); await openEditor(page, server.base, 'email_komplett.fls'); await page.getByRole('button', { name: 'Wechselt in den Aktionsmodus' }).click(); await expect(page.locator('svg.filius-canvas')).toHaveAttribute('data-mode', 'simulation'); // The other notebook shares a file. const other = await desktopOf(page, 'Rechner 2'); await install(other, 'Gnutella'); await expect(other.locator('[data-program="gnutella"]')).toBeVisible(); const otherId = await idOf(page, 'Rechner 2'); await page.evaluate( (id) => (window as unknown as { filiusDev: { writeFile: (id: string, path: string, text: string) => void } }).filiusDev.writeFile(id, '/peer2peer/lied.txt', 'La la la'), otherId, ); await other.locator('.desktop-close').click(); const desktop = await desktopOf(page, 'Rechner 1'); await install(desktop, 'Gnutella'); await desktop.locator('[data-program="gnutella"]').click(); const window = desktop.locator('[data-program-window="gnutella"]'); await expect(window.locator('.program-titlebar h3')).toHaveText('Gnutella'); await expect(window.getByRole('tab')).toHaveText(['Netzwerk', 'Suche', 'Dateien', 'Einstellungen']); // What is no address: red, and Java's words. const address = window.getByRole('textbox', { name: 'Teilnehmer IP-Adresse:' }); await expect(address).toHaveValue('192.168.0.1'); await address.fill('141.99.50.300'); await expect(address).toHaveAttribute('aria-invalid', 'true'); await window.getByRole('button', { name: 'Netz beitreten' }).click(); await expect(window.getByRole('alert')).toHaveText('Dies ist keine gültige IP-Adresse'); await address.fill('141.99.50.182'); await window.getByRole('button', { name: 'Netz beitreten' }).click(); const peers = window.getByRole('table', { name: 'Liste der verbundenen Nachbarn:' }).locator('tbody tr'); await expect(peers).toHaveCount(1, { timeout: 30_000 }); await expect(peers.first().locator('td')).toHaveText(['1', '141.99.50.182']); // The other's file found and downloaded. await window.getByRole('tab', { name: 'Suche' }).click(); await window.getByRole('textbox', { name: 'Suchbegriff:' }).fill('lied'); await window.getByRole('button', { name: 'Suchen' }).click(); const results = window.getByRole('table', { name: 'Suche' }).locator('tbody tr'); await expect(results).toHaveCount(1, { timeout: 30_000 }); await expect(results.first().locator('td')).toHaveText(['141.99.50.182', 'lied.txt', '8 B']); const download = window.getByRole('button', { name: 'Herunterladen' }); await expect(download).toBeDisabled(); await results.first().dispatchEvent('pointerdown'); await download.click(); // The list emptied: nothing is chosen any more. await window.getByRole('button', { name: 'Liste leeren' }).click(); await expect(results).toHaveCount(0); await expect(download).toBeDisabled(); await window.getByRole('tab', { name: 'Dateien' }).click(); await expect(window.getByRole('table', { name: 'Dateien' }).locator('tbody tr td')).toHaveText(['lied.txt', 'txt'], { timeout: 30_000 }); // The number of peers: 12 saved (Java takes one digit only), what is no number refused with Java's words. await window.getByRole('tab', { name: 'Einstellungen' }).click(); const maximum = window.getByRole('textbox', { name: 'Maximale Anzahl direkter Nachbarn:' }); await expect(maximum).toHaveValue('3'); await maximum.fill('012'); await window.getByRole('button', { name: 'Speichern' }).click(); // The field shows the program's number once it is saved. await expect(maximum).toHaveValue('12'); await expect(window.getByRole('alert')).toHaveCount(0); await maximum.fill('x'); await expect(maximum).toHaveAttribute('aria-invalid', 'true'); await window.getByRole('button', { name: 'Speichern' }).click(); await expect(window.getByRole('alert')).toHaveText('Es sind nur Zahlen erlaubt!'); // The network left. await window.getByRole('tab', { name: 'Netzwerk' }).click(); await window.getByRole('button', { name: 'Netz verlassen' }).click(); await expect(peers).toHaveCount(0); });