first commit

This commit is contained in:
2026-05-22 11:46:43 +02:00
commit 2ebd8fb16b
29 changed files with 3356 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
import { Page } from '@playwright/test';
const BASE_URL = process.env.BASE_URL ?? 'https://pbx.local:4443';
const ADMIN_USER = process.env.ADMIN_USER ?? 'admin';
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD ?? 'admin';
/** Navigates to the admin portal login page and authenticates. */
export async function login(page: Page, baseUrl = BASE_URL): Promise<void> {
await page.goto(`${baseUrl}/login`, { waitUntil: 'domcontentloaded' });
await page
.locator('[name="username"],[name="user"],#username,#user,input[type="text"]:first-of-type')
.first()
.fill(ADMIN_USER);
await page
.locator('[name="password"],#password,input[type="password"]')
.first()
.fill(ADMIN_PASSWORD);
await page
.locator('[type="submit"],button:has-text("Login"),button:has-text("Sign in"),button:has-text("Entrar")')
.first()
.click();
await page.waitForURL(/dashboard|home|index|admin|portal/, { timeout: 20_000 });
}
export async function logout(page: Page): Promise<void> {
await page
.locator('button:has-text("Logout"),a:has-text("Logout"),a:has-text("Log out"),[aria-label="Logout"]')
.first()
.click();
}
export { BASE_URL, ADMIN_USER };
+31
View File
@@ -0,0 +1,31 @@
import { test } from '@playwright/test';
export type AutomationStatus = 'automated' | 'partial' | 'manual';
/**
* Marks a test as MANUAL — annotates it and skips execution in automated runs.
* The test body should document the required manual steps as comments.
*
* In the HTML test plan report this test appears as 🔴 MANUAL.
*/
export function markManual(reason: string): void {
test.info().annotations.push({ type: 'automationStatus', description: 'manual' });
test.info().annotations.push({ type: 'manualReason', description: reason });
test.skip(true, `⚠️ MANUAL TEST — ${reason}`);
}
/**
* Marks a test as PARTIALLY automatable — automated steps run normally;
* manual steps are annotated but skipped.
*
* In the HTML test plan report this test appears as ⚡ PARTIAL.
*/
export function markPartial(notes: string): void {
test.info().annotations.push({ type: 'automationStatus', description: 'partial' });
test.info().annotations.push({ type: 'partialNotes', description: notes });
}
/** Tags a test as "write" type — the result must be attached to the report. */
export function markWrite(): void {
test.info().annotations.push({ type: 'testType', description: 'write' });
}
+36
View File
@@ -0,0 +1,36 @@
import { TestInfo } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
const RESULTS_DIR = path.join(process.cwd(), 'test-results', 'write-outputs');
/**
* Attaches captured data to the test report and writes it to disk.
* Used by all "write" type tests to persist their recorded output.
*/
export async function attachWriteResult(
testInfo: TestInfo,
data: Record<string, unknown>,
label = 'recorded-output',
): Promise<void> {
if (!fs.existsSync(RESULTS_DIR)) {
fs.mkdirSync(RESULTS_DIR, { recursive: true });
}
const sanitizedTitle = testInfo.title.replace(/[^a-z0-9-_]/gi, '_').slice(0, 100);
const filePath = path.join(RESULTS_DIR, `${sanitizedTitle}.json`);
const output = {
testId: testInfo.title,
capturedAt: new Date().toISOString(),
status: 'recorded',
data,
};
fs.writeFileSync(filePath, JSON.stringify(output, null, 2), 'utf-8');
await testInfo.attach(label, {
body: JSON.stringify(output, null, 2),
contentType: 'application/json',
});
}