// What the data exchange window writes of a frame in the viewer's language: // Java's words of both bundles, the locale's numbers ("1.000.000" in German, // "1,000,000" in English), the time of day with milliseconds, the row of the // highest layer, the details from the layered rows, the text file. import { describe, expect, it } from 'vitest'; import { captureRows, TCP_FLAG, type CapturedFrame, type EthernetFrame } from '@mdg-filius/core'; import type { TraceFrame } from '@mdg-filius/protocol'; import { translate } from '../src/i18n/i18n.js'; import { captureWords, exportText, frameCells, frameDetails, reportRows, reportTraffic } from '../src/simulation/captureCells.js'; /** A TCP segment with data from 10.0.0.1:50000 to 10.0.0.2:80, as the room sends it. */ function frame(number: number, timeMilliseconds: number): TraceFrame { const ethernet: EthernetFrame = { identity: number, sourceMacAddress: '02:00:00:00:00:01', destinationMacAddress: '02:00:00:00:00:02', payload: { kind: 'ipv4', packet: { identity: 9, senderIPAddress: '10.0.0.1', receiverIPAddress: '10.0.0.2', timeToLive: 64, protocol: 'tcp', payload: { kind: 'tcp', segment: { sourcePort: 50_000, destinationPort: 80, sequenceNumber: 2_000_001, acknowledgementNumber: 1_000_001, flags: TCP_FLAG.acknowledgement, payload: new TextEncoder().encode('GET / HTTP/1.1\nHost: x\n'), }, }, }, }, }; const captured: CapturedFrame[] = [{ number, timeMilliseconds, frame: ethernet }]; return { number, rows: captureRows(captured, { withAddresses: true }), layeredRows: captureRows(captured, { withAddresses: false }) }; } const words = (language: 'de' | 'en') => captureWords((key) => translate(language, key), language); const clock = new Date(2026, 9, 4, 8, 30, 0, 0).getTime(); describe("the data exchange window's cells", () => { it("writes a frame's row by its highest layer, in either language", () => { expect(frameCells(frame(3, 1_234), clock, words('de'))).toEqual(['3', '08:30:01.234', '10.0.0.1:50000', '10.0.0.2:80', 'HTTP', 'Anwendung', 'GET / HTTP/1.1\nHost: x\n']); expect(frameCells(frame(3, 1_234), clock, words('en'))).toEqual(['3', '08:30:01.234', '10.0.0.1:50000', '10.0.0.2:80', 'HTTP', 'Application', 'GET / HTTP/1.1\nHost: x\n']); expect(words('de').header).toEqual(['Nr.', 'Zeit', 'Quelle', 'Ziel', 'Protokoll', 'Schicht', 'Bemerkungen / Details']); expect(words('en').header).toEqual(['No.', 'Time', 'Source', 'Destination', 'Protocol', 'Layer', 'Comment / Details']); }); it("writes the details with the locale's numbers and Java's words", () => { const german = frameDetails(frame(3, 0), clock, words('de')).map((line) => line.text); expect(german[0]).toBe('Nr.: 3 / Zeit: 08:30:00.000'); expect(german).toContain('Bemerkungen / Details: SEQ: 2.000.001, ACK: 1.000.001'); expect(german).toContain('Bemerkungen / Details: Protokoll: 6, TTL: 64'); expect(german).toContain('Bemerkungen / Details (23 Bytes): '); const english = frameDetails(frame(3, 0), clock, words('en')).map((line) => line.text); expect(english[0]).toBe('No.: 3 / Time: 08:30:00.000'); expect(english).toContain('Comment / Details: SEQ: 2,000,001, ACK: 1,000,001'); expect(english).toContain('Comment / Details: Protocol: 6, TTL: 64'); expect(frameDetails({ number: 1, rows: [], layeredRows: [] }, clock, words('de'))).toEqual([]); }); it('exports the table with the words of its header', () => { const text = exportText([frame(1, 0), frame(2, 5)], clock, words('en')); expect(text.split('\r\n')[1]).toBe( `| No. | Time | Source | Destination | Protocol | Layer | ${'Comment / Details'.padEnd(40)} | `, ); expect(text.split('\r\n')[3]).toBe( `| 1 | 08:30:00.000 | 10.0.0.1:50000 | 10.0.0.2:80 | HTTP | Application | ${'GET / HTTP/1.1 Host: x'.padEnd(40)} | `, ); expect(text.match(/^\+-/gm)).toHaveLength(2); }); }); describe("the report's data exchange", () => { it('writes every layer of a frame as a row, with addresses, as Java\'s report does', () => { expect(reportRows([frame(3, 1_234)], clock, words('de'))).toEqual([ ['3', '08:30:01.234', '02:00:00:00:00:01', '02:00:00:00:00:02', '', 'Netzzugang', '0x800'], ['3', '08:30:01.234', '10.0.0.1', '10.0.0.2', 'IP', 'Vermittlung', 'Protokoll: 6, TTL: 64'], ['3', '08:30:01.234', '10.0.0.1:50000', '10.0.0.2:80', 'TCP', 'Transport', 'SEQ: 2.000.001, ACK: 1.000.001'], ['3', '08:30:01.234', '10.0.0.1:50000', '10.0.0.2:80', 'HTTP', 'Anwendung', 'GET / HTTP/1.1\nHost: x\n'], ]); }); it("lists each card that saw frames, under its device's label and address; one without frames not", () => { const cards = [ { portId: 'a', label: 'Rechner 1', ipAddress: '10.0.0.1' }, { portId: 'b', label: 'Rechner 2', ipAddress: '10.0.0.2' }, { portId: 'c', label: 'Rechner 3', ipAddress: '10.0.0.3' }, ]; const traffic = reportTraffic(cards, new Map([['a', [frame(1, 0)]], ['b', []]]), clock, words('de')); expect(traffic.map((card) => card.title)).toEqual(['Rechner 1 - 10.0.0.1']); expect(traffic[0]!.header).toEqual(words('de').header); expect(traffic[0]!.rows.length).toBe(4); }); });