Files
pleno-vue/tests/e2e/fixtures/authHelpers.ts
T
Jeppe Bundgaard f040614f46 Refactor module paths, layout responsiveness, and testing configurations:
- Replaced repetitive department module URL logic with `buildDepartmentModulePath` utility in `DepartmentModulesDisplay.vue`.
- Enhanced layouts and responsive behavior across components, including `PosDepartmentStep1MobileTransactionHistory.vue` and `DepartmentDailyReport.vue`.
- Removed unused `SuperuserInvoicingLocalStore.vue` and refactored to `SuperuserInvoicingLocalStore.ts`.
- Updated media query handling and card layout adjustments in `DepartmentDailyReport.vue`.
- Added new Playwright configurations (`playwright.prod.config.ts` and `playwright.live.config.ts`) for e2e release workflows.
- Extended e2e and unit test coverage for mobile POS and department modules.
2026-04-09 16:32:25 +02:00

243 lines
8.5 KiB
TypeScript

import { Page, expect } from '@playwright/test';
import {
userCredentials,
subuserPhoneCredentials,
subuserUsernameCredentials,
operatorCredentials,
user2FACredentials,
subuser2FACredentials,
qrAuthToken,
} from './testData';
import { generateOTP } from './otp';
// Auth timeout in milliseconds (15 seconds)
const AUTH_TIMEOUT = 15000;
const USER_HOME_URL = /\/user(?:\/)?(?:[?#].*)?$/;
const ADMIN_HOME_URL = /\/admin(?:\/)?(?:[?#].*)?$/;
/**
* Reusable authentication helpers for tests
* Provides login functions for different user types
*/
/**
* Login as a customer user (by customer number)
* @param page - Playwright page object
* @param credentials - Optional custom credentials (defaults to userCredentials)
*/
export async function loginAsUser(
page: Page,
credentials?: { customerNumber: string; password: string; twoFactorAuthentication?: boolean; otpSecret?: string }
) {
const creds = credentials || userCredentials;
await page.goto('/login');
await page.fill('input[name="customer_number"]', creds.customerNumber);
await page.fill('input[name="password"]', creds.password);
await page.click('button[id="login-button"]');
// If two-factor authentication is enabled, wait for OTP input
if (creds.twoFactorAuthentication) {
await page.waitForSelector('input[name="2fa_code"]');
// Generate OTP using secret
await page.fill('input[name="2fa_code"]', generateOTP(creds.otpSecret!));
await page.click('button[id="2fa-verify-button"]');
}
await expect(page).toHaveURL(USER_HOME_URL, { timeout: AUTH_TIMEOUT });
}
/**
* Login as a subuser/driver (by phone number)
* @param page - Playwright page object
* @param credentials - Optional custom credentials (defaults to subuserPhoneCredentials)
*/
export async function loginAsSubuserByPhone(
page: Page,
credentials?: { phoneCountryCode: string; phone: string; password: string; twoFactorAuthentication?: boolean; otpSecret?: string }
) {
const creds = credentials || subuserPhoneCredentials;
await page.goto('/login/driver');
await page.fill('input[name="phone_country_code"]', creds.phoneCountryCode);
await page.fill('input[name="phone"]', creds.phone);
await page.fill('input[name="password"]', creds.password);
await page.click('button[id="subuser-login-button"]');
// If two-factor authentication is enabled, wait for OTP input
if (creds.twoFactorAuthentication) {
await page.waitForSelector('input[name="2fa_code"]');
// Generate OTP using secret
await page.fill('input[name="2fa_code"]', generateOTP(creds.otpSecret!));
await page.click('button[id="2fa-verify-button"]');
}
await expect(page).toHaveURL(USER_HOME_URL, { timeout: AUTH_TIMEOUT });
}
/**
* Login as a subuser/driver (by username)
* @param page - Playwright page object
* @param credentials - Optional custom credentials (defaults to subuserUsernameCredentials)
*/
export async function loginAsSubuserByUsername(
page: Page,
credentials?: { username: string; password: string; twoFactorAuthentication?: boolean; otpSecret?: string }
) {
const creds = credentials || subuserUsernameCredentials;
await page.goto('/login/driver');
await page.click('button[id="subuser_login_method_username_button"]');
await page.waitForSelector('input[name="username"]');
await page.waitForSelector('input[name="password"]');
await page.fill('input[name="username"]', creds.username);
await page.fill('input[name="password"]', creds.password);
await page.click('button[id="subuser-login-button"]');
// If two-factor authentication is enabled, wait for OTP input
if (creds.twoFactorAuthentication) {
await page.waitForSelector('input[name="2fa_code"]');
// Generate OTP using secret
await page.fill('input[name="2fa_code"]', generateOTP(creds.otpSecret!));
await page.click('button[id="2fa-verify-button"]');
}
await expect(page).toHaveURL(USER_HOME_URL, { timeout: AUTH_TIMEOUT });
}
/**
* Login as an operator (by user ID)
* @param page - Playwright page object
* @param credentials - Optional custom credentials (defaults to operatorCredentials)
*/
export async function loginAsOperator(
page: Page,
credentials?: { userId: string; password: string }
) {
const creds = credentials || operatorCredentials;
await page.goto('/admin/login');
await page.fill('input[name="user_id"]', creds.userId);
await page.fill('input[name="password"]', creds.password);
await page.click('button[id="operator_login_button"]');
await expect(page).toHaveURL(ADMIN_HOME_URL, { timeout: AUTH_TIMEOUT });
}
/**
* Navigate to user login page without logging in
* Useful for testing login failures
* @param page - Playwright page object
*/
export async function goToUserLogin(page: Page) {
await page.goto('/login');
}
/**
* Navigate to subuser/driver login page without logging in
* @param page - Playwright page object
*/
export async function goToSubuserLogin(page: Page) {
await page.goto('/login/driver');
}
/**
* Navigate to operator login page without logging in
* @param page - Playwright page object
*/
export async function goToOperatorLogin(page: Page) {
await page.goto('/admin/login');
}
/**
* Login as user and trigger 2FA verification screen
* @param page - Playwright page object
* @param credentials - Optional custom credentials (defaults to user2FACredentials)
*/
export async function loginAsUserWith2FA(
page: Page,
credentials?: { customerNumber: string; password: string }
) {
const creds = credentials || user2FACredentials;
await page.goto('/login');
await page.fill('input[name="customer_number"]', creds.customerNumber);
await page.fill('input[name="password"]', creds.password);
await page.click('button[id="login-button"]');
// Wait for 2FA verification screen
await expect(page.locator('input[name="2fa_code"]')).toBeVisible({ timeout: AUTH_TIMEOUT });
}
/**
* Complete 2FA verification with a code
* @param page - Playwright page object
* @param code - The 6-digit 2FA code
*/
export async function verify2FACode(page: Page, code: string) {
await page.fill('input[name="2fa_code"]', code);
await page.click('button[id="2fa-verify-button"]');
}
/**
* Cancel 2FA verification and return to login
* @param page - Playwright page object
*/
export async function cancel2FA(page: Page) {
await page.click('button:has-text("Back to login"), button:has-text("Tilbage til login")');
}
/**
* Login as subuser and trigger 2FA verification screen
* @param page - Playwright page object
* @param credentials - Optional custom credentials (defaults to subuser2FACredentials)
*/
export async function loginAsSubuserWith2FA(
page: Page,
credentials?: { phoneCountryCode: string; phone: string; password: string }
) {
const creds = credentials || subuser2FACredentials;
await page.goto('/login/driver');
await page.fill('input[name="phone_country_code"]', creds.phoneCountryCode);
await page.fill('input[name="phone"]', creds.phone);
await page.fill('input[name="password"]', creds.password);
await page.click('button[id="subuser-login-button"]');
// Wait for 2FA verification screen
await expect(page.locator('input[name="2fa_code"]')).toBeVisible({ timeout: AUTH_TIMEOUT });
}
/**
* Check if passkey login button is visible
* @param page - Playwright page object
* @returns boolean - true if passkey button is visible
*/
export async function isPasskeyButtonVisible(page: Page): Promise<boolean> {
return await page.locator('button[id="passkey-login-button"]').isVisible();
}
/**
* Login using QR code authentication token
* @param page - Playwright page object
* @param token - Optional QR auth token (defaults to qrAuthToken from testData)
*/
export async function loginWithQRCode(
page: Page,
token?: string
) {
const authToken = token || qrAuthToken;
if (!authToken) {
throw new Error('QR auth token is not set. Set PLENO_QR_TOKEN environment variable or pass token directly.');
}
await page.goto(`/login/qr?token=${authToken}`);
await expect(page).toHaveURL(USER_HOME_URL, { timeout: AUTH_TIMEOUT });
}
/**
* Navigate to QR code login page without authenticating
* Useful for testing QR code scanning UI
* @param page - Playwright page object
*/
export async function goToQRCodeLogin(page: Page) {
await page.goto('/login/qr');
}
/**
* Attempt QR code login with invalid token
* Useful for testing error handling
* @param page - Playwright page object
* @param invalidToken - Invalid token to test with
*/
export async function attemptInvalidQRCodeLogin(
page: Page,
invalidToken: string = 'invalid-token'
) {
await page.goto(`/login/qr?token=${invalidToken}`);
}