Files
rxminder/tests/e2e/auth-debug.spec.ts
William Valentin 4d12aeef61 feat: add comprehensive authentication debug test suite
Add automated authentication testing infrastructure:

- AUTH-DEBUG-GUIDE.md: Complete guide for auth debugging
- auth-debug.spec.ts: Comprehensive auth flow validation tests
- playwright.auth.config.ts: Specialized config with extended timeouts
- auth-debug-setup.ts: Global test environment setup
- auth-debug-teardown.ts: Test cleanup and environment reset

Features:
- Admin user validation and permissions testing
- Email format validation including localhost domains
- User registration and OAuth integration testing
- Database connectivity and session management
- Password security and error handling validation
- Cross-browser testing with mobile support
- Enhanced reporting and interactive debugging
- CI/CD integration with artifacts and JUnit reports

Replaces manual browser console debugging scripts with
automated, cross-browser E2E tests for better reliability
and maintainability.
2025-09-08 01:48:26 -07:00

391 lines
13 KiB
TypeScript

import { test, expect } from '@playwright/test';
test.describe('Authentication Debug and Validation', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
// Wait for login form to be ready
await page.waitForSelector('input[type="email"]', { timeout: 10000 });
});
test('should validate admin user exists in system', async ({ page }) => {
// Check if admin user exists by attempting login
await page.fill('input[type="email"]', 'admin@localhost');
await page.fill('input[type="password"]', 'admin123!');
await page.click('button[type="submit"]');
// Should successfully login and reach main app
await expect(page.locator('h1')).toContainText('Medication Reminder', {
timeout: 15000,
});
await expect(page.locator('text=Admin')).toBeVisible({ timeout: 10000 });
// Verify admin role and permissions
await page.click('button:has-text("Admin")');
await expect(page.locator('text=Admin Interface')).toBeVisible({
timeout: 10000,
});
await expect(page.locator('text=User Management')).toBeVisible();
});
test('should validate email format with localhost domain', async ({
page,
}) => {
const testEmails = [
{ email: 'admin@localhost', valid: true },
{ email: 'user@localhost', valid: true },
{ email: 'test@example.com', valid: true },
{ email: 'invalid-email', valid: false },
{ email: '@localhost', valid: false },
{ email: 'user@', valid: false },
{ email: '', valid: false },
];
for (const { email, valid } of testEmails) {
console.log(`Testing email validation for: ${email}`);
// Clear form
await page.fill('input[type="email"]', '');
await page.fill('input[type="password"]', '');
// Fill email
await page.fill('input[type="email"]', email);
await page.fill('input[type="password"]', 'testpass123');
// Try to submit
await page.click('button[type="submit"]');
if (valid) {
// Should not show validation error for valid emails
await expect(page.locator('text=Invalid email format')).not.toBeVisible(
{ timeout: 3000 }
);
} else {
// Should show validation error for invalid emails
await expect(page.locator('text=Invalid')).toBeVisible({
timeout: 3000,
});
}
// Wait a bit between tests and reload to reset state
await page.waitForTimeout(1000);
await page.reload();
await page.waitForSelector('input[type="email"]');
}
});
test('should validate user creation with password', async ({ page }) => {
// Go to registration
await page.click('text=Register');
await page.waitForSelector('input[name="username"]', { timeout: 5000 });
// Test user data with timestamp to ensure uniqueness
const timestamp = Date.now();
const testUser = {
email: `debug-test-${timestamp}@localhost`,
username: `debugtest${timestamp}`,
password: 'DebugTest123!',
};
console.log(`Testing user creation for: ${testUser.email}`);
// Fill registration form
await page.fill('input[type="email"]', testUser.email);
await page.fill('input[name="username"]', testUser.username);
await page.fill('input[type="password"]', testUser.password);
// Submit registration
await page.click('button[type="submit"]');
// Should show success or verification message
await expect(
page
.locator('text=verification')
.or(page.locator('text=registered'))
.or(page.locator('text=success'))
).toBeVisible({ timeout: 10000 });
});
test('should handle OAuth user creation flow', async ({ page }) => {
// Check OAuth buttons are present
await expect(page.locator('button:has-text("Google")')).toBeVisible({
timeout: 5000,
});
await expect(page.locator('button:has-text("GitHub")')).toBeVisible({
timeout: 5000,
});
// Store current URL to verify navigation attempt
const currentUrl = page.url();
// Click OAuth button (won't actually authenticate in test)
await page.click('button:has-text("Google")');
// Should either redirect to OAuth provider or show error in test environment
await page.waitForTimeout(2000);
// Verify some action was taken (URL change or error message)
const newUrl = page.url();
const hasError = await page.locator('text=error').isVisible();
// One of these should be true: URL changed (redirect) or error shown
const actionTaken = newUrl !== currentUrl || hasError;
expect(actionTaken).toBeTruthy();
});
test('should validate database connection status', async ({ page }) => {
// Login as admin to access system status
await page.fill('input[type="email"]', 'admin@localhost');
await page.fill('input[type="password"]', 'admin123!');
await page.click('button[type="submit"]');
await expect(page.locator('h1')).toContainText('Medication Reminder', {
timeout: 15000,
});
// Check if there's a system status indicator
const statusIndicator = page.locator('[data-testid="system-status"]');
const statusVisible = await statusIndicator.isVisible();
if (statusVisible) {
await expect(statusIndicator).toContainText('Connected');
console.log('System status indicator found and shows: Connected');
} else {
console.log(
'System status indicator not found - this is optional functionality'
);
}
// Alternative: Check if app loads successfully (indicates DB connection)
await expect(page.locator('button:has-text("Add Medication")')).toBeVisible(
{ timeout: 5000 }
);
console.log(
'App loaded successfully - database connection presumed working'
);
});
test('should validate password strength requirements', async ({ page }) => {
await page.click('text=Register');
await page.waitForSelector('input[name="username"]', { timeout: 5000 });
const weakPasswords = [
{ password: '123', description: 'too short' },
{ password: 'password', description: 'common word' },
{ password: 'admin', description: 'common admin' },
{ password: 'abc123', description: 'simple pattern' },
{ password: 'PASSWORD', description: 'all caps, no numbers' },
];
const strongPasswords = [
{
password: 'StrongPass123!',
description: 'mixed case, numbers, symbols',
},
{
password: 'MySecure@Password1',
description: 'long with special chars',
},
{ password: 'Complex#Pass9', description: 'complex pattern' },
];
// Test weak passwords
for (const { password, description } of weakPasswords) {
console.log(`Testing weak password (${description}): ${password}`);
const timestamp = Date.now();
await page.fill('input[type="email"]', `test${timestamp}@localhost`);
await page.fill('input[name="username"]', `testuser${timestamp}`);
await page.fill('input[type="password"]', password);
await page.click('button[type="submit"]');
// Should show password strength error
const errorVisible = await page
.locator('text=Password must')
.or(page.locator('text=weak'))
.or(page.locator('text=strength'))
.isVisible();
if (!errorVisible) {
console.log(
`Warning: Password strength validation may not be implemented for: ${password}`
);
}
// Clear form for next test
await page.fill('input[type="password"]', '');
await page.waitForTimeout(500);
}
// Test strong passwords
for (const { password, description } of strongPasswords) {
console.log(`Testing strong password (${description}): ${password}`);
const timestamp = Date.now();
await page.fill('input[type="email"]', `strong${timestamp}@localhost`);
await page.fill('input[name="username"]', `stronguser${timestamp}`);
await page.fill('input[type="password"]', password);
await page.click('button[type="submit"]');
// Should not show password strength error, but might show other validation
await page.waitForTimeout(1000);
const hasPasswordError = await page
.locator('text=Password must')
.or(page.locator('text=weak'))
.isVisible();
if (hasPasswordError) {
console.log(`Unexpected: Strong password rejected: ${password}`);
}
// Clear form for next test
await page.fill('input[type="password"]', '');
await page.waitForTimeout(500);
}
});
test('should validate session persistence', async ({ page }) => {
console.log('Testing session persistence...');
// Login
await page.fill('input[type="email"]', 'admin@localhost');
await page.fill('input[type="password"]', 'admin123!');
await page.click('button[type="submit"]');
await expect(page.locator('h1')).toContainText('Medication Reminder', {
timeout: 15000,
});
console.log('Initial login successful');
// Refresh page
await page.reload();
await page.waitForLoadState('networkidle');
// Should still be logged in
try {
await expect(page.locator('h1')).toContainText('Medication Reminder', {
timeout: 10000,
});
await expect(page.locator('text=Admin')).toBeVisible({ timeout: 5000 });
console.log(
'Session persistence verified - user remains logged in after refresh'
);
} catch (_error) {
console.log(
'Session persistence not implemented - user was logged out after refresh'
);
// This might be expected behavior depending on implementation
await expect(page.locator('input[type="email"]')).toBeVisible();
}
});
test('should handle invalid login attempts', async ({ page }) => {
const invalidCredentials = [
{
email: 'nonexistent@localhost',
password: 'password123',
type: 'nonexistent user',
},
{
email: 'admin@localhost',
password: 'wrongpassword',
type: 'wrong password',
},
{
email: 'invalid-email',
password: 'password123',
type: 'invalid email format',
},
{ email: '', password: 'password123', type: 'empty email' },
{ email: 'admin@localhost', password: '', type: 'empty password' },
];
for (const { email, password, type } of invalidCredentials) {
console.log(`Testing invalid login: ${type}`);
await page.fill('input[type="email"]', email);
await page.fill('input[type="password"]', password);
await page.click('button[type="submit"]');
// Wait for response
await page.waitForTimeout(2000);
// Should show error message or stay on login page
const hasError = await page
.locator('text=Invalid')
.or(page.locator('text=error'))
.or(page.locator('text=failed'))
.isVisible();
const staysOnLogin = await page
.locator('input[type="email"]')
.isVisible();
if (!hasError && !staysOnLogin) {
console.log(`Warning: No clear error indication for ${type}`);
}
// Should not redirect to main app
const redirectedToApp = await page
.locator('h1:has-text("Medication Reminder")')
.isVisible();
expect(redirectedToApp).toBeFalsy();
// Clear form for next test
await page.fill('input[type="email"]', '');
await page.fill('input[type="password"]', '');
await page.waitForTimeout(1000);
}
});
test('should validate user lookup functionality', async ({ page }) => {
console.log('Testing user lookup functionality...');
// Login as admin first
await page.fill('input[type="email"]', 'admin@localhost');
await page.fill('input[type="password"]', 'admin123!');
await page.click('button[type="submit"]');
await expect(page.locator('h1')).toContainText('Medication Reminder', {
timeout: 15000,
});
// Open admin interface
await page.click('button:has-text("Admin")');
await expect(page.locator('text=User Management')).toBeVisible({
timeout: 10000,
});
// Should show admin user in user list
await expect(page.locator('text=admin@localhost')).toBeVisible({
timeout: 5000,
});
console.log('Admin user found in user list');
// Test user search if available
const searchBox = page
.locator('input[placeholder*="search"]')
.or(page.locator('input[type="search"]'));
const searchVisible = await searchBox.isVisible();
if (searchVisible) {
console.log('Search functionality found, testing...');
await searchBox.fill('admin');
await page.waitForTimeout(1000); // Allow for search debouncing
await expect(page.locator('text=admin@localhost')).toBeVisible();
console.log('Search functionality works correctly');
} else {
console.log('Search functionality not implemented - this is optional');
}
// Test user count or list functionality
const userRows = page.locator(
'tr:has-text("@"), .user-item, [data-testid*="user"]'
);
const userCount = await userRows.count();
console.log(`Found ${userCount} users in the system`);
expect(userCount).toBeGreaterThan(0); // At least admin should be present
});
});