mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 05:23:31 +01:00
MAESTRO: add file drop e2e coverage
This commit is contained in:
@@ -70,7 +70,7 @@ All tasks in this phase are parallelizable [P] since they are independent E2E sp
|
|||||||
|
|
||||||
- [x] T054 Create Page Object Model in `frontend/e2e/pages/analyzer-page.ts` — encapsulates all page interactions: `pasteHeaders(text)`, `dropFile(path)`, `clickAnalyse()`, `pressCtrlEnter()`, `selectTests(ids)`, `deselectAll()`, `selectAll()`, `toggleDns()`, `toggleDecodeAll()`, `waitForResults()`, `getResultCards()`, `expandCard(index)`, `collapseCard(index)`, `searchReport(query)`, `exportJson()`, `exportHtml()`, `clearCache()`, `getCaptchaModal()`. Copy test fixture files to `frontend/e2e/fixtures/sample-headers.txt` and `frontend/e2e/fixtures/sample.eml`
|
- [x] T054 Create Page Object Model in `frontend/e2e/pages/analyzer-page.ts` — encapsulates all page interactions: `pasteHeaders(text)`, `dropFile(path)`, `clickAnalyse()`, `pressCtrlEnter()`, `selectTests(ids)`, `deselectAll()`, `selectAll()`, `toggleDns()`, `toggleDecodeAll()`, `waitForResults()`, `getResultCards()`, `expandCard(index)`, `collapseCard(index)`, `searchReport(query)`, `exportJson()`, `exportHtml()`, `clearCache()`, `getCaptchaModal()`. Copy test fixture files to `frontend/e2e/fixtures/sample-headers.txt` and `frontend/e2e/fixtures/sample.eml`
|
||||||
- [x] T055 [P] Create `frontend/e2e/paste-and-analyse.spec.ts` — test US1+US3+US4 primary flow: paste sample headers → click Analyse → verify progress indicator appears with test names → verify report renders with severity-coloured cards → expand/collapse a card → verify hop chain visualisation rendered. Also test Ctrl+Enter keyboard shortcut triggers analysis. Assert on UI effects of SSE progress (progress bar increments, test name updates)
|
- [x] T055 [P] Create `frontend/e2e/paste-and-analyse.spec.ts` — test US1+US3+US4 primary flow: paste sample headers → click Analyse → verify progress indicator appears with test names → verify report renders with severity-coloured cards → expand/collapse a card → verify hop chain visualisation rendered. Also test Ctrl+Enter keyboard shortcut triggers analysis. Assert on UI effects of SSE progress (progress bar increments, test name updates)
|
||||||
- [ ] T056 [P] Create `frontend/e2e/file-drop.spec.ts` — test US1 file drop flow: dispatch synthetic DataTransfer+drop events on drop zone with `.eml` fixture → verify textarea auto-populates with header content → click Analyse → verify report renders. Test rejection of unsupported file types (e.g., `.pdf`)
|
- [x] T056 [P] Create `frontend/e2e/file-drop.spec.ts` — test US1 file drop flow: dispatch synthetic DataTransfer+drop events on drop zone with `.eml` fixture → verify textarea auto-populates with header content → click Analyse → verify report renders. Test rejection of unsupported file types (e.g., `.pdf`)
|
||||||
- [ ] T057 [P] Create `frontend/e2e/test-selection.spec.ts` — test US2: open test selector → verify 106+ tests listed → click Deselect All → select 3 specific tests → analyse → verify only 3 results in report. Test search/filter narrows visible tests. Test DNS and decode-all toggle states persist through analysis
|
- [ ] T057 [P] Create `frontend/e2e/test-selection.spec.ts` — test US2: open test selector → verify 106+ tests listed → click Deselect All → select 3 specific tests → analyse → verify only 3 results in report. Test search/filter narrows visible tests. Test DNS and decode-all toggle states persist through analysis
|
||||||
- [ ] T058 [P] Create `frontend/e2e/report-interaction.spec.ts` — test US4 report features: expand all cards → collapse all → search for a term → verify filtered results → clear search → export JSON → verify downloaded file is valid JSON. Export HTML → verify downloaded file contains styled content
|
- [ ] T058 [P] Create `frontend/e2e/report-interaction.spec.ts` — test US4 report features: expand all cards → collapse all → search for a term → verify filtered results → clear search → export JSON → verify downloaded file is valid JSON. Export HTML → verify downloaded file contains styled content
|
||||||
- [ ] T059 [P] Create `frontend/e2e/browser-cache.spec.ts` — test US5: complete analysis → reload page → verify headers and results restored from cache → click Clear Cache → verify input and report cleared → reload → verify empty state
|
- [ ] T059 [P] Create `frontend/e2e/browser-cache.spec.ts` — test US5: complete analysis → reload page → verify headers and results restored from cache → click Clear Cache → verify input and report cleared → reload → verify empty state
|
||||||
|
|||||||
60
frontend/e2e/file-drop.spec.ts
Normal file
60
frontend/e2e/file-drop.spec.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
import { AnalyzerPage } from "./pages/analyzer-page";
|
||||||
|
|
||||||
|
const emlPath = path.resolve(__dirname, "fixtures/sample.eml");
|
||||||
|
|
||||||
|
const extractHeaderBlock = (content: string): string => {
|
||||||
|
const normalized = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
||||||
|
const lines = normalized.split("\n");
|
||||||
|
const headerLines: string[] = [];
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.trim() === "") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
headerLines.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headerLines.length === 0) {
|
||||||
|
return normalized.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerLines.join("\n").trimEnd();
|
||||||
|
};
|
||||||
|
|
||||||
|
test("drop eml file populates headers and renders report", async ({ page }) => {
|
||||||
|
const analyzer = new AnalyzerPage(page);
|
||||||
|
const emlContents = await fs.readFile(emlPath, "utf8");
|
||||||
|
const expectedHeaders = extractHeaderBlock(emlContents);
|
||||||
|
|
||||||
|
await analyzer.goto();
|
||||||
|
await analyzer.dropFile(emlPath);
|
||||||
|
|
||||||
|
const headerInput = page.getByRole("textbox", { name: "Header Input" });
|
||||||
|
await expect(headerInput).toHaveValue(expectedHeaders);
|
||||||
|
|
||||||
|
await analyzer.clickAnalyse();
|
||||||
|
await analyzer.waitForResults();
|
||||||
|
|
||||||
|
await expect(analyzer.getResultCards().first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("rejects unsupported file types", async ({ page }, testInfo) => {
|
||||||
|
const analyzer = new AnalyzerPage(page);
|
||||||
|
const invalidPath = testInfo.outputPath("unsupported.pdf");
|
||||||
|
await fs.writeFile(invalidPath, "%PDF-1.4 fake pdf content");
|
||||||
|
|
||||||
|
await analyzer.goto();
|
||||||
|
await analyzer.dropFile(invalidPath, "application/pdf");
|
||||||
|
|
||||||
|
const alert = page
|
||||||
|
.locator('p[role="alert"]')
|
||||||
|
.filter({ hasText: "Only .eml or .txt files are supported." });
|
||||||
|
await expect(alert).toBeVisible();
|
||||||
|
|
||||||
|
const headerInput = page.getByRole("textbox", { name: "Header Input" });
|
||||||
|
await expect(headerInput).toHaveValue("");
|
||||||
|
});
|
||||||
@@ -2,12 +2,18 @@ import type { Download, Locator, Page } from "@playwright/test";
|
|||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const resolveMimeType = (fileName: string): string => {
|
const resolveMimeType = (fileName: string, override?: string): string => {
|
||||||
|
if (override) {
|
||||||
|
return override;
|
||||||
|
}
|
||||||
const extension = path.extname(fileName).toLowerCase();
|
const extension = path.extname(fileName).toLowerCase();
|
||||||
if (extension === ".eml") {
|
if (extension === ".eml") {
|
||||||
return "message/rfc822";
|
return "message/rfc822";
|
||||||
}
|
}
|
||||||
return "text/plain";
|
if (extension === ".txt") {
|
||||||
|
return "text/plain";
|
||||||
|
}
|
||||||
|
return "application/octet-stream";
|
||||||
};
|
};
|
||||||
|
|
||||||
export class AnalyzerPage {
|
export class AnalyzerPage {
|
||||||
@@ -25,10 +31,10 @@ export class AnalyzerPage {
|
|||||||
await this.headerInput().fill(text);
|
await this.headerInput().fill(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
async dropFile(filePath: string): Promise<void> {
|
async dropFile(filePath: string, mimeTypeOverride?: string): Promise<void> {
|
||||||
const resolvedPath = path.resolve(filePath);
|
const resolvedPath = path.resolve(filePath);
|
||||||
const fileName = path.basename(resolvedPath);
|
const fileName = path.basename(resolvedPath);
|
||||||
const mimeType = resolveMimeType(fileName);
|
const mimeType = resolveMimeType(fileName, mimeTypeOverride);
|
||||||
const fileBuffer = await fs.readFile(resolvedPath);
|
const fileBuffer = await fs.readFile(resolvedPath);
|
||||||
const base64 = fileBuffer.toString("base64");
|
const base64 = fileBuffer.toString("base64");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user