WellNuo Lite architecture: - Simplified navigation flow with NavigationController - Profile editing with API sync (/auth/profile endpoint) - OTP verification improvements - ESP WiFi provisioning setup (espProvisioning.ts) - E2E testing infrastructure (Playwright) - Speech recognition hooks (web/native) - Backend auth enhancements This is the stable version submitted to App Store. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
174 lines
6.6 KiB
TypeScript
174 lines
6.6 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
const BASE_URL = 'https://wellnuo.smartlaunchhub.com/app/';
|
|
|
|
// Run tests serially to avoid rate limiting (429)
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
// This test uses a pre-created temp email account
|
|
// Email: coeoh@virgilian.com, Password: xd4wa7nb
|
|
test.describe('Complete Authentication Flow', () => {
|
|
|
|
test('Full login with OTP from temp email', async ({ page }) => {
|
|
// Debug logging
|
|
page.on('console', msg => {
|
|
if (msg.type() !== 'log') return;
|
|
console.log(`BROWSER: ${msg.text()}`);
|
|
});
|
|
page.on('pageerror', err => console.log(`ERROR: ${err.message}`));
|
|
|
|
const testEmail = 'coeoh@virgilian.com';
|
|
console.log(`Testing with temp email: ${testEmail}`);
|
|
|
|
// 1. Go to login page
|
|
await page.goto(BASE_URL);
|
|
await page.waitForLoadState('networkidle');
|
|
console.log('Step 1: Login page loaded');
|
|
|
|
// Verify login page
|
|
await expect(page.getByText('Welcome to WellNuo')).toBeVisible();
|
|
await expect(page.getByPlaceholder('Enter your email')).toBeVisible();
|
|
|
|
// 2. Enter email
|
|
await page.getByPlaceholder('Enter your email').fill(testEmail);
|
|
console.log(`Step 2: Email entered: ${testEmail}`);
|
|
|
|
// 3. Click Continue
|
|
await page.getByText('Continue').click();
|
|
console.log('Step 3: Clicked Continue');
|
|
|
|
// 4. Wait for OTP screen
|
|
await expect(page.getByText('Check your email')).toBeVisible({ timeout: 15000 });
|
|
console.log('Step 4: OTP screen visible');
|
|
|
|
// Take screenshot of OTP screen
|
|
await page.screenshot({ path: 'screenshots/otp-screen-complete.png', quality: 50, scale: 'css' });
|
|
|
|
// Verify OTP screen elements
|
|
await expect(page.getByText(testEmail)).toBeVisible();
|
|
await expect(page.getByText('Verify')).toBeVisible();
|
|
await expect(page.getByText('Resend')).toBeVisible();
|
|
|
|
console.log('Step 5: OTP screen elements verified');
|
|
console.log('✅ Auth flow to OTP screen working!');
|
|
|
|
// NOTE: To complete the flow, we need to:
|
|
// 1. Wait for email to arrive (use temp-mail MCP)
|
|
// 2. Extract OTP code from email
|
|
// 3. Enter OTP code using keyboard (hidden input)
|
|
// 4. Wait for navigation to next screen
|
|
});
|
|
|
|
test('OTP input accepts digits via keyboard', async ({ page }) => {
|
|
page.on('console', msg => console.log(`BROWSER: ${msg.text()}`));
|
|
|
|
await page.goto(BASE_URL);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Enter email and go to OTP
|
|
await page.getByPlaceholder('Enter your email').fill('test@example.com');
|
|
await page.getByText('Continue').click();
|
|
|
|
// Wait for OTP screen
|
|
await expect(page.getByText('Check your email')).toBeVisible({ timeout: 15000 });
|
|
|
|
// Click on code boxes area to focus hidden input
|
|
await page.click('[class*="codeContainer"]').catch(() => {
|
|
// If selector fails, try clicking in the middle of the page
|
|
console.log('Trying alternative click location');
|
|
});
|
|
|
|
// Type OTP digits
|
|
await page.keyboard.type('123456', { delay: 100 });
|
|
|
|
// Wait a moment for auto-submit (if 6 digits triggers it)
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Take screenshot
|
|
await page.screenshot({ path: 'screenshots/otp-entered.png', quality: 50, scale: 'css' });
|
|
|
|
console.log('✅ OTP input accepts keyboard input');
|
|
});
|
|
|
|
test('Wrong OTP shows error message', async ({ page }) => {
|
|
page.on('console', msg => console.log(`BROWSER: ${msg.text()}`));
|
|
|
|
await page.goto(BASE_URL);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Enter email and go to OTP
|
|
await page.getByPlaceholder('Enter your email').fill('test@example.com');
|
|
await page.getByText('Continue').click();
|
|
|
|
// Wait for OTP screen
|
|
await expect(page.getByText('Check your email')).toBeVisible({ timeout: 15000 });
|
|
|
|
// Type wrong OTP
|
|
await page.keyboard.type('000000', { delay: 50 });
|
|
|
|
// Wait for auto-submit and error response
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Check for error message (from line 177: 'Invalid verification code. Please try again.')
|
|
const hasError = await page.getByText(/Invalid|incorrect|error/i).isVisible().catch(() => false);
|
|
|
|
// Take screenshot
|
|
await page.screenshot({ path: 'screenshots/wrong-otp-error.png', quality: 50, scale: 'css' });
|
|
|
|
// We should still be on OTP screen
|
|
const stillOnOtp = await page.getByText('Check your email').isVisible();
|
|
expect(stillOnOtp).toBe(true);
|
|
|
|
console.log(`Error message visible: ${hasError}`);
|
|
console.log('✅ Wrong OTP keeps user on OTP screen');
|
|
});
|
|
|
|
test('Back button returns to login', async ({ page }) => {
|
|
page.on('console', msg => console.log(`BROWSER: ${msg.text()}`));
|
|
|
|
await page.goto(BASE_URL);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Go to OTP screen
|
|
await page.getByPlaceholder('Enter your email').fill('back@test.com');
|
|
await page.getByText('Continue').click();
|
|
await expect(page.getByText('Check your email')).toBeVisible({ timeout: 15000 });
|
|
|
|
// Click back arrow (using Ionicons back arrow)
|
|
const backButton = page.locator('[data-testid="back-button"]').or(
|
|
page.locator('div').filter({ has: page.locator('svg') }).first()
|
|
);
|
|
|
|
// Try clicking the "Use a different email" link instead
|
|
await page.getByText('Use a different email').click();
|
|
|
|
// Should return to login
|
|
await expect(page.getByText('Welcome to WellNuo')).toBeVisible({ timeout: 5000 });
|
|
|
|
console.log('✅ Back navigation works');
|
|
});
|
|
|
|
test('Resend link shows cooldown', async ({ page }) => {
|
|
page.on('console', msg => console.log(`BROWSER: ${msg.text()}`));
|
|
|
|
await page.goto(BASE_URL);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Go to OTP screen
|
|
await page.getByPlaceholder('Enter your email').fill('resend@test.com');
|
|
await page.getByText('Continue').click();
|
|
await expect(page.getByText('Check your email')).toBeVisible({ timeout: 15000 });
|
|
|
|
// Click Resend
|
|
await page.getByText('Resend').click();
|
|
|
|
// Should show cooldown timer (e.g., "Resend in 60s")
|
|
await expect(page.getByText(/Resend in \d+s/)).toBeVisible({ timeout: 5000 });
|
|
|
|
await page.screenshot({ path: 'screenshots/resend-cooldown.png', quality: 50, scale: 'css' });
|
|
|
|
console.log('✅ Resend cooldown works');
|
|
});
|
|
|
|
});
|