Files
Rediet Wogayehu 7bf4343f88
Some checks failed
E2E Tests / test (push) Has been cancelled
Extract PassDashboard Playwright e2e suite into standalone repo
Copies the Playwright end-to-end suite out of the PassDashboard app repo so
it can be run and deployed independently. Nothing was removed from the app
repo - this is a copy.

Carried over verbatim: tests/ (10 spec files, 4 helper modules, README) plus
playwright.config.ts and .env.test.example. The suite drives the deployed app
over HTTP and imports nothing from src/, which is what makes it standalone.

New for this repo:
- package.json with only the deps the suite actually uses (@playwright/test,
  allure-playwright, xlsx, typescript, @types/node) and the e2e scripts.
- tsconfig.json covering tests/ - the app repo's tsconfig.app.json only
  included src/, so these files were never typechecked before.
- .gitea/workflows/e2e-tests.yml for Gitea Actions. The app repo derived ENV
  from github.ref_name; that doesn't apply here since this repo has no
  per-environment branches, so the target is an explicit workflow_dispatch
  input and scheduled/push runs default to develop. Reports are uploaded as
  artifacts rather than deleted.
- .gitignore and README covering local setup and the required CI secrets.

The Jest unit tests under src/__tests__/ were deliberately left in the app
repo: they import application source directly (AuthContext, ProtectedRoute,
cryptoUtils, api) and cannot run without it.

Verified: npx tsc --noEmit is clean and playwright collects all 43 tests
across all 10 spec files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 15:50:50 +03:00

82 lines
3.3 KiB
TypeScript

import { Page } from '@playwright/test';
import { getEnvironmentConfig } from './environment';
const env = getEnvironmentConfig();
export const TEST_CREDENTIALS = {
username: env.username,
password: env.password,
loginPath: process.env.TEST_LOGIN_PATH || '/neopaas/portal/login',
dashboardPath: '/dashboard',
};
export async function loginAs(page: Page, identifier: string, password: string): Promise<void> {
console.log('Starting login process...');
// The initial navigation is the most failure-prone step on a loaded local
// machine (headless browser launch/navigation can occasionally stall well
// past a single timeout even though the server itself responds in ~1-3s) -
// retry it in place rather than letting one slow navigation fail the whole
// test and force an expensive full test-level retry.
let lastError: unknown;
for (let attempt = 1; attempt <= 3; attempt++) {
try {
await page.goto(TEST_CREDENTIALS.loginPath, { waitUntil: 'domcontentloaded' });
lastError = undefined;
break;
} catch (error) {
lastError = error;
console.log(`Login page navigation attempt ${attempt} failed, retrying...`);
}
}
if (lastError) throw lastError;
await page.waitForTimeout(2000);
// The button's own text is the language you'd switch TO, not the current
// one - so it reads "English" while the page is in Arabic (click to go to
// English), and "العربية" once already in English. The language preference
// persists across logout/login (e.g. via localStorage), so on a second
// login within the same test the page may already be in English - only
// click when the button would actually switch TO English.
console.log('Checking for language switcher...');
const languageArticle = page.locator('article.ant-typography.font-poppins-500.css-1kfsfla').first();
const isVisible = await languageArticle.isVisible({ timeout: 2000 }).catch(() => false);
if (isVisible) {
const text = (await languageArticle.textContent())?.trim();
console.log(`Found language button with text: "${text}"`);
if (text === 'English') {
console.log('Clicking to switch language to English...');
await languageArticle.click();
await page.waitForTimeout(2000);
console.log('✓ Language switched');
} else {
console.log('Already in English - no switch needed');
}
}
console.log('Filling username...');
await page.fill('input[type="email"], input[type="text"], input[name="email"], input[name="username"], input[placeholder*="email" i], input[placeholder*="username" i]', identifier);
console.log('Filling password...');
await page.fill('input[type="password"], input[name="password"]', password);
console.log('Clicking login button...');
await page.click('button[type="submit"], button:has-text("Login"), button:has-text("Sign in")');
console.log('Waiting for redirect...');
await page.waitForURL(/\/(dashboard|terminals)/, { timeout: 30000 }).catch(() => {
console.log('Post-login URL not detected, checking for navigation...');
});
await page.waitForLoadState('domcontentloaded').catch(() => {});
await page.waitForTimeout(2000);
console.log('Login completed successfully');
}
export async function login(page: Page): Promise<void> {
await loginAs(page, TEST_CREDENTIALS.username, TEST_CREDENTIALS.password);
}