GnutellaWindowRun uses Java's Gnutella window on terminal-net beside the line of GnutellaRun's story "search". The core cancels a search as Java does (every connection of the program's own requests closed, passed-on queries kept), lets the number of peers change and lists /peer2peer's files; the room takes the window's actions and sends its view; the editor shows Java's four tabs with Java's symbols. Not adopted from Java: a number of peers of one digit only, a results table that keeps the last search's rows, a files table that lags behind the folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
5.6 KiB
TypeScript
113 lines
5.6 KiB
TypeScript
// 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<void> };
|
||
|
||
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);
|
||
});
|