mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 05:23:31 +01:00
MAESTRO: add test selection e2e spec
This commit is contained in:
@@ -71,7 +71,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)
|
||||||
- [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`)
|
- [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
|
- [x] 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
|
||||||
- [ ] T060 [P] Create `frontend/e2e/rate-limiting.spec.ts` — test US6 rate limiting flow: submit requests until 429 response → verify CAPTCHA modal appears → solve CAPTCHA → verify bypass token stored → retry original request succeeds. Test that the CAPTCHA modal is keyboard accessible and visually correct
|
- [ ] T060 [P] Create `frontend/e2e/rate-limiting.spec.ts` — test US6 rate limiting flow: submit requests until 429 response → verify CAPTCHA modal appears → solve CAPTCHA → verify bypass token stored → retry original request succeeds. Test that the CAPTCHA modal is keyboard accessible and visually correct
|
||||||
|
|||||||
109
frontend/e2e/test-selection.spec.ts
Normal file
109
frontend/e2e/test-selection.spec.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
import { AnalyzerPage } from "./pages/analyzer-page";
|
||||||
|
|
||||||
|
const headersPath = path.resolve(__dirname, "fixtures/sample-headers.txt");
|
||||||
|
|
||||||
|
const extractSearchQuery = (label: string): string => {
|
||||||
|
const trimmed = label.trim();
|
||||||
|
if (!trimmed) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const firstToken = trimmed.split(/\s+/)[0] ?? "";
|
||||||
|
if (firstToken.length >= 4) {
|
||||||
|
return firstToken;
|
||||||
|
}
|
||||||
|
return trimmed.length > 4 ? trimmed.slice(0, 4) : trimmed;
|
||||||
|
};
|
||||||
|
|
||||||
|
test("test selection filters and limits analysis results", async ({ page }) => {
|
||||||
|
const headers = await fs.readFile(headersPath, "utf8");
|
||||||
|
const analyzer = new AnalyzerPage(page);
|
||||||
|
|
||||||
|
await analyzer.goto();
|
||||||
|
|
||||||
|
const checkboxLocator = page.locator('[data-testid^="test-checkbox-"]');
|
||||||
|
await expect.poll(async () => checkboxLocator.count()).toBeGreaterThanOrEqual(106);
|
||||||
|
const totalCount = await checkboxLocator.count();
|
||||||
|
expect(totalCount).toBeGreaterThanOrEqual(106);
|
||||||
|
|
||||||
|
const firstCheckbox = checkboxLocator.first();
|
||||||
|
await firstCheckbox.waitFor({ state: "visible" });
|
||||||
|
const firstLabel = await firstCheckbox.getAttribute("aria-label");
|
||||||
|
if (!firstLabel) {
|
||||||
|
throw new Error("Expected test checkbox to have an aria-label.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchInput = page.getByTestId("test-search-input");
|
||||||
|
const searchQuery = extractSearchQuery(firstLabel);
|
||||||
|
if (!searchQuery) {
|
||||||
|
throw new Error("Unable to derive search query from test label.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await searchInput.fill(searchQuery);
|
||||||
|
await expect
|
||||||
|
.poll(async () => checkboxLocator.count())
|
||||||
|
.toBeLessThan(totalCount);
|
||||||
|
const filteredCount = await checkboxLocator.count();
|
||||||
|
expect(filteredCount).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
await searchInput.fill("");
|
||||||
|
await expect.poll(async () => checkboxLocator.count()).toBe(totalCount);
|
||||||
|
|
||||||
|
const resolveToggle = page.getByTestId("toggle-resolve");
|
||||||
|
const decodeToggle = page.getByTestId("toggle-decode-all");
|
||||||
|
|
||||||
|
if ((await resolveToggle.getAttribute("aria-checked")) !== "true") {
|
||||||
|
await resolveToggle.click();
|
||||||
|
}
|
||||||
|
if ((await decodeToggle.getAttribute("aria-checked")) !== "true") {
|
||||||
|
await decodeToggle.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(resolveToggle).toHaveAttribute("aria-checked", "true");
|
||||||
|
await expect(decodeToggle).toHaveAttribute("aria-checked", "true");
|
||||||
|
|
||||||
|
await analyzer.deselectAll();
|
||||||
|
|
||||||
|
const selectedIds = await checkboxLocator.evaluateAll((nodes) => {
|
||||||
|
const ids: number[] = [];
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (ids.length >= 3) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const testId = node.getAttribute("data-testid") ?? "";
|
||||||
|
const numeric = Number(testId.replace("test-checkbox-", ""));
|
||||||
|
if (Number.isFinite(numeric)) {
|
||||||
|
ids.push(numeric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selectedIds.length < 3) {
|
||||||
|
throw new Error("Expected at least three tests to select.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await analyzer.selectTests(selectedIds);
|
||||||
|
await analyzer.pasteHeaders(headers);
|
||||||
|
|
||||||
|
const analysisResponse = page.waitForResponse(
|
||||||
|
(response) => response.url().includes("/api/analyse") && response.status() === 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
await analyzer.clickAnalyse();
|
||||||
|
await analysisResponse;
|
||||||
|
await analyzer.waitForResults();
|
||||||
|
|
||||||
|
const resultCards = analyzer.getResultCards();
|
||||||
|
await expect(resultCards).toHaveCount(3, { timeout: 30000 });
|
||||||
|
|
||||||
|
for (const id of selectedIds) {
|
||||||
|
await expect(page.getByTestId(`test-result-card-${id}`)).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(resolveToggle).toHaveAttribute("aria-checked", "true");
|
||||||
|
await expect(decodeToggle).toHaveAttribute("aria-checked", "true");
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user