/** * E2E Test Helpers for WellNuo * Common utilities for Playwright tests */ import { Page, expect } from '@playwright/test'; // E2E tests run against local Expo web server // Start with: npm run web (expo start --web) export const BASE_URL = process.env.E2E_BASE_URL || 'http://localhost:8081'; // Test credentials (use bypass OTP for testing) export const TEST_CREDENTIALS = { // Test user with existing beneficiary existingUser: { email: 'e2e.test@wellnuo.com', bypassOtp: '000000', }, // Test user for new registration newUser: { email: `e2e.new.${Date.now()}@test.com`, bypassOtp: '000000', }, // Invalid test cases invalid: { email: 'invalid-email', wrongOtp: '999999', }, }; /** * Wait for app to fully load */ export async function waitForAppLoad(page: Page, timeout = 15000): Promise { await page.waitForLoadState('networkidle', { timeout }); } /** * Navigate to login page and verify it loaded */ export async function goToLogin(page: Page): Promise { await page.goto(BASE_URL); await waitForAppLoad(page); await expect(page.getByText('Welcome to WellNuo')).toBeVisible({ timeout: 10000 }); } /** * Enter email and proceed to OTP screen */ export async function enterEmailAndSubmit(page: Page, email: string): Promise { await page.getByPlaceholder('Enter your email').fill(email); await page.getByText('Continue').click(); } /** * Enter OTP code using keyboard (handles hidden input) */ export async function enterOtpCode(page: Page, code: string): Promise { // Click on the OTP container to focus await page.click('body'); await page.keyboard.type(code, { delay: 50 }); } /** * Wait for OTP screen to appear */ export async function waitForOtpScreen(page: Page): Promise { await expect(page.getByText('Check your email')).toBeVisible({ timeout: 15000 }); } /** * Complete login flow (email + OTP) */ export async function loginWithOtp( page: Page, email: string, otp: string = '000000' ): Promise { await goToLogin(page); await enterEmailAndSubmit(page, email); await waitForOtpScreen(page); await enterOtpCode(page, otp); // Wait for navigation after OTP await page.waitForTimeout(2000); } /** * Wait for dashboard/main screen */ export async function waitForDashboard(page: Page): Promise { // Could be beneficiary list or dashboard depending on user state await expect( page.getByText('My Loved Ones').or(page.getByText('Dashboard')) ).toBeVisible({ timeout: 15000 }); } /** * Wait for enter-name screen (new user onboarding) */ export async function waitForEnterNameScreen(page: Page): Promise { await expect( page.getByText('What should we call you?').or(page.getByPlaceholder('Your name')) ).toBeVisible({ timeout: 10000 }); } /** * Wait for add-loved-one screen */ export async function waitForAddLovedOneScreen(page: Page): Promise { await expect( page.getByText('Add a Loved One').or(page.getByText('loved one')) ).toBeVisible({ timeout: 10000 }); } /** * Check if still on login page */ export async function isOnLoginPage(page: Page): Promise { return await page.getByText('Welcome to WellNuo').isVisible({ timeout: 1000 }).catch(() => false); } /** * Check if still on OTP page */ export async function isOnOtpPage(page: Page): Promise { return await page.getByText('Check your email').isVisible({ timeout: 1000 }).catch(() => false); } /** * Navigate to beneficiary detail */ export async function goToBeneficiaryDetail(page: Page, beneficiaryName?: string): Promise { if (beneficiaryName) { await page.getByText(beneficiaryName).click(); } else { // Click first beneficiary card await page.locator('[data-testid="beneficiary-card"]').first().click(); } await page.waitForTimeout(1000); } /** * Navigate to subscription screen for a beneficiary */ export async function goToSubscription(page: Page): Promise { await page.getByText('Subscription').click(); await page.waitForTimeout(1000); } /** * Navigate to equipment screen for a beneficiary */ export async function goToEquipment(page: Page): Promise { await page.getByText('Equipment').or(page.getByText('Sensors')).click(); await page.waitForTimeout(1000); } /** * Navigate to profile */ export async function goToProfile(page: Page): Promise { await page.getByText('Profile').click(); await page.waitForTimeout(1000); } /** * Click back button */ export async function goBack(page: Page): Promise { const backButton = page.locator('[data-testid="back-button"]').or( page.getByText('Use a different email') ); if (await backButton.isVisible()) { await backButton.click(); await page.waitForTimeout(500); } else { // Try browser back await page.goBack(); } } /** * Log browser console messages (for debugging) */ export function enableConsoleLogging(page: Page): void { page.on('console', msg => { if (msg.type() !== 'log') return; console.log(`BROWSER: ${msg.text()}`); }); page.on('pageerror', err => console.log(`ERROR: ${err.message}`)); } /** * Take named screenshot */ export async function screenshot(page: Page, name: string): Promise { await page.screenshot({ path: `screenshots/${name}.png`, quality: 50, scale: 'css', }); } /** * Verify error message is displayed */ export async function expectErrorMessage(page: Page, pattern: RegExp | string): Promise { if (typeof pattern === 'string') { await expect(page.getByText(pattern)).toBeVisible({ timeout: 5000 }); } else { await expect(page.getByText(pattern)).toBeVisible({ timeout: 5000 }); } } /** * Wait for toast/notification */ export async function waitForToast(page: Page, text: string | RegExp): Promise { await expect(page.getByText(text)).toBeVisible({ timeout: 5000 }); } /** * Check API response in network */ export async function interceptApiCall( page: Page, urlPattern: string | RegExp, callback: (response: { status: number; body: unknown }) => void ): Promise { page.on('response', async response => { if (response.url().match(urlPattern)) { const body = await response.json().catch(() => null); callback({ status: response.status(), body }); } }); }