mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 05:23:31 +01:00
179 lines
5.0 KiB
TypeScript
179 lines
5.0 KiB
TypeScript
import type { Download, Locator, Page } from "@playwright/test";
|
|
import fs from "fs/promises";
|
|
import path from "path";
|
|
|
|
const resolveMimeType = (fileName: string, override?: string): string => {
|
|
if (override) {
|
|
return override;
|
|
}
|
|
const extension = path.extname(fileName).toLowerCase();
|
|
if (extension === ".eml") {
|
|
return "message/rfc822";
|
|
}
|
|
if (extension === ".txt") {
|
|
return "text/plain";
|
|
}
|
|
return "application/octet-stream";
|
|
};
|
|
|
|
export class AnalyzerPage {
|
|
readonly page: Page;
|
|
|
|
constructor(page: Page) {
|
|
this.page = page;
|
|
}
|
|
|
|
async goto(): Promise<void> {
|
|
await this.page.goto("http://localhost:3100");
|
|
}
|
|
|
|
async pasteHeaders(text: string): Promise<void> {
|
|
await this.headerInput().fill(text);
|
|
}
|
|
|
|
async dropFile(filePath: string, mimeTypeOverride?: string): Promise<void> {
|
|
const resolvedPath = path.resolve(filePath);
|
|
const fileName = path.basename(resolvedPath);
|
|
const mimeType = resolveMimeType(fileName, mimeTypeOverride);
|
|
const fileBuffer = await fs.readFile(resolvedPath);
|
|
const base64 = fileBuffer.toString("base64");
|
|
|
|
await this.page.evaluate(
|
|
({ base64Data, name, type }) => {
|
|
const binary = atob(base64Data);
|
|
const bytes = new Uint8Array(binary.length);
|
|
for (let index = 0; index < binary.length; index += 1) {
|
|
bytes[index] = binary.charCodeAt(index);
|
|
}
|
|
const file = new File([bytes], name, { type });
|
|
const dataTransfer = new DataTransfer();
|
|
dataTransfer.items.add(file);
|
|
|
|
const dropZone = document.querySelector('[data-testid="file-drop-zone"]');
|
|
if (!dropZone) {
|
|
throw new Error("File drop zone not found");
|
|
}
|
|
|
|
const dragOver = new DragEvent("dragover", {
|
|
dataTransfer,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
});
|
|
dropZone.dispatchEvent(dragOver);
|
|
|
|
const drop = new DragEvent("drop", {
|
|
dataTransfer,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
});
|
|
dropZone.dispatchEvent(drop);
|
|
},
|
|
{ base64Data: base64, name: fileName, type: mimeType },
|
|
);
|
|
}
|
|
|
|
async clickAnalyse(): Promise<void> {
|
|
await this.analyseButton().click();
|
|
}
|
|
|
|
async pressCtrlEnter(): Promise<void> {
|
|
await this.page.keyboard.press("Control+Enter");
|
|
}
|
|
|
|
async selectTests(ids: number[]): Promise<void> {
|
|
await this.page.getByTestId("test-selector").waitFor({ state: "visible" });
|
|
for (const id of ids) {
|
|
const checkbox = this.page.getByTestId(`test-checkbox-${id}`);
|
|
await checkbox.scrollIntoViewIfNeeded();
|
|
if (!(await checkbox.isChecked())) {
|
|
await checkbox.check();
|
|
}
|
|
}
|
|
}
|
|
|
|
async deselectAll(): Promise<void> {
|
|
await this.page.getByTestId("deselect-all-tests").click();
|
|
}
|
|
|
|
async selectAll(): Promise<void> {
|
|
await this.page.getByTestId("select-all-tests").click();
|
|
}
|
|
|
|
async toggleDns(): Promise<void> {
|
|
await this.page.getByTestId("toggle-resolve").click();
|
|
}
|
|
|
|
async toggleDecodeAll(): Promise<void> {
|
|
await this.page.getByTestId("toggle-decode-all").click();
|
|
}
|
|
|
|
async waitForResults(): Promise<void> {
|
|
await this.page.getByTestId("report-container").waitFor({
|
|
state: "visible",
|
|
timeout: 30000,
|
|
});
|
|
}
|
|
|
|
getResultCards(): Locator {
|
|
return this.page.locator('[data-testid^="test-result-card-"]');
|
|
}
|
|
|
|
async expandCard(index: number): Promise<void> {
|
|
const toggle = this.getResultCards()
|
|
.nth(index)
|
|
.locator('[data-testid^="test-result-toggle-"]');
|
|
await toggle.waitFor({ state: "visible" });
|
|
const expanded = await toggle.getAttribute("aria-expanded");
|
|
if (expanded !== "true") {
|
|
await toggle.click();
|
|
}
|
|
}
|
|
|
|
async collapseCard(index: number): Promise<void> {
|
|
const toggle = this.getResultCards()
|
|
.nth(index)
|
|
.locator('[data-testid^="test-result-toggle-"]');
|
|
await toggle.waitFor({ state: "visible" });
|
|
const expanded = await toggle.getAttribute("aria-expanded");
|
|
if (expanded === "true") {
|
|
await toggle.click();
|
|
}
|
|
}
|
|
|
|
async searchReport(query: string): Promise<void> {
|
|
await this.page.getByTestId("report-search-input").fill(query);
|
|
}
|
|
|
|
async exportJson(): Promise<Download> {
|
|
const [download] = await Promise.all([
|
|
this.page.waitForEvent("download"),
|
|
this.page.getByTestId("report-export-json").click(),
|
|
]);
|
|
return download;
|
|
}
|
|
|
|
async exportHtml(): Promise<Download> {
|
|
const [download] = await Promise.all([
|
|
this.page.waitForEvent("download"),
|
|
this.page.getByTestId("report-export-html").click(),
|
|
]);
|
|
return download;
|
|
}
|
|
|
|
async clearCache(): Promise<void> {
|
|
await this.page.getByRole("button", { name: "Clear Cache" }).click();
|
|
}
|
|
|
|
getCaptchaModal(): Locator {
|
|
return this.page.getByTestId("captcha-challenge");
|
|
}
|
|
|
|
private headerInput(): Locator {
|
|
return this.page.getByRole("textbox", { name: "Header Input" });
|
|
}
|
|
|
|
private analyseButton(): Locator {
|
|
return this.page.getByRole("button", { name: "Analyse Headers" });
|
|
}
|
|
}
|