Some checks failed
E2E Tests / test (push) Has been cancelled
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>
82 lines
3.3 KiB
TypeScript
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);
|
|
}
|