cqt-agent
Version:
374 lines (303 loc) • 15.8 kB
text/typescript
// Comprehensive E2E Test Template - Exhaustive Coverage
// This template generates detailed tests for all possible scenarios
import { test, expect, Page } from '@playwright/test';
// Test Data Factory for comprehensive coverage
class TestDataFactory {
static validUser = {
email: 'valid.user@hubtel.com',
password: 'ValidPassword123!',
name: 'Valid User',
phone: '+233123456789'
};
static invalidUsers = [
{ email: '', password: '', name: '', error: 'required_fields' },
{ email: 'invalid-email', password: 'weak', name: '', error: 'format_validation' },
{ email: 'test@hubtel.com', password: '123', name: '', error: 'password_strength' },
{ email: 'very.long.email.address.that.exceeds.normal.limits@hubtel.com', password: 'ValidPassword123!', name: 'Very Long Name That Exceeds Character Limits', error: 'length_validation' },
{ email: 'sql@injection.com\'; DROP TABLE users; --', password: 'SqlInjection123!', name: '<script>alert("xss")</script>', error: 'security_validation' }
];
static edgeCaseInputs = [
{ type: 'unicode', value: '测试用户@hubtel.com', expected: 'unicode_support' },
{ type: 'special_chars', value: 'user+tag@hubtel.com', expected: 'email_plus_addressing' },
{ type: 'whitespace', value: ' spaced.user@hubtel.com ', expected: 'whitespace_handling' },
{ type: 'case_sensitivity', value: 'CaSeSeNsItIvE@HUBTEL.COM', expected: 'case_normalization' }
];
}
// Comprehensive Authentication Test Suite
test.describe('Authentication - Complete Coverage', () => {
let page: Page;
test.beforeEach(async ({ browser }) => {
page = await browser.newPage();
await page.goto('/login');
});
// Happy Path Scenarios
test.describe('Login Success Scenarios', () => {
test('successful login with valid credentials', async () => {
await page.fill('[data-testid=email]', TestDataFactory.validUser.email);
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.click('[data-testid=login-btn]');
// Verify successful login
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('[data-testid=user-profile]')).toBeVisible();
// Verify session storage
const token = await page.evaluate(() => localStorage.getItem('auth_token'));
expect(token).toBeTruthy();
});
test('login with remember me functionality', async () => {
await page.fill('[data-testid=email]', TestDataFactory.validUser.email);
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.check('[data-testid=remember-me]');
await page.click('[data-testid=login-btn]');
// Close browser and reopen to test persistence
await page.close();
page = await browser.newPage();
await page.goto('/');
// Should be automatically logged in
await expect(page).toHaveURL('/dashboard');
});
test('login redirects to intended page after authentication', async () => {
// Try to access protected page
await page.goto('/protected-page');
await expect(page).toHaveURL(/.*login.*returnUrl=.*protected-page/);
// Login
await page.fill('[data-testid=email]', TestDataFactory.validUser.email);
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.click('[data-testid=login-btn]');
// Should redirect to originally intended page
await expect(page).toHaveURL('/protected-page');
});
});
// Comprehensive Failure Scenarios
test.describe('Login Failure Scenarios', () => {
TestDataFactory.invalidUsers.forEach((invalidUser, index) => {
test(`login fails with ${invalidUser.error}`, async () => {
await page.fill('[data-testid=email]', invalidUser.email);
await page.fill('[data-testid=password]', invalidUser.password);
await page.click('[data-testid=login-btn]');
// Verify error handling
await expect(page.locator('[data-testid=error-message]')).toBeVisible();
await expect(page).toHaveURL('/login'); // Should stay on login page
// Verify form state preservation for valid fields
if (invalidUser.email && !invalidUser.email.includes('injection')) {
await expect(page.locator('[data-testid=email]')).toHaveValue(invalidUser.email);
}
// Password should be cleared for security
await expect(page.locator('[data-testid=password]')).toHaveValue('');
});
});
test('account lockout after multiple failed attempts', async () => {
const maxAttempts = 5;
// Attempt failed logins
for (let i = 0; i < maxAttempts; i++) {
await page.fill('[data-testid=email]', TestDataFactory.validUser.email);
await page.fill('[data-testid=password]', 'wrong-password');
await page.click('[data-testid=login-btn]');
if (i < maxAttempts - 1) {
await expect(page.locator('[data-testid=error-message]')).toContainText('Invalid credentials');
}
}
// Account should be locked
await expect(page.locator('[data-testid=error-message]')).toContainText('Account locked');
await expect(page.locator('[data-testid=login-btn]')).toBeDisabled();
// Even correct credentials should fail
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.click('[data-testid=login-btn]');
await expect(page.locator('[data-testid=error-message]')).toContainText('Account locked');
});
test('concurrent login attempts handling', async () => {
const secondPage = await browser.newPage();
await secondPage.goto('/login');
// Start login process on both pages simultaneously
const loginPromises = [
page.evaluate(async () => {
document.querySelector('[data-testid=email]').value = 'valid.user@hubtel.com';
document.querySelector('[data-testid=password]').value = 'ValidPassword123!';
document.querySelector('[data-testid=login-btn]').click();
}),
secondPage.evaluate(async () => {
document.querySelector('[data-testid=email]').value = 'valid.user@hubtel.com';
document.querySelector('[data-testid=password]').value = 'ValidPassword123!';
document.querySelector('[data-testid=login-btn]').click();
})
];
await Promise.all(loginPromises);
// Both should succeed or one should handle concurrent session
const firstPageUrl = page.url();
const secondPageUrl = secondPage.url();
expect(firstPageUrl === '/dashboard' || firstPageUrl.includes('concurrent-session')).toBeTruthy();
expect(secondPageUrl === '/dashboard' || secondPageUrl.includes('concurrent-session')).toBeTruthy();
});
});
// Edge Cases and Security
test.describe('Edge Cases and Security', () => {
test('session timeout during login process', async () => {
// Start filling form
await page.fill('[data-testid=email]', TestDataFactory.validUser.email);
// Simulate session timeout (mock or wait)
await page.evaluate(() => {
// Mock session expiration
localStorage.removeItem('session_token');
sessionStorage.clear();
});
// Complete login
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.click('[data-testid=login-btn]');
// Should create new session
await expect(page).toHaveURL('/dashboard');
});
test('network interruption during login', async () => {
// Simulate network failure
await page.route('**/api/auth/login', route => route.abort());
await page.fill('[data-testid=email]', TestDataFactory.validUser.email);
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.click('[data-testid=login-btn]');
// Should show network error
await expect(page.locator('[data-testid=network-error]')).toBeVisible();
await expect(page.locator('[data-testid=retry-btn]')).toBeVisible();
// Restore network and retry
await page.unroute('**/api/auth/login');
await page.click('[data-testid=retry-btn]');
await expect(page).toHaveURL('/dashboard');
});
TestDataFactory.edgeCaseInputs.forEach((edgeCase) => {
test(`handles ${edgeCase.type} input correctly`, async () => {
await page.fill('[data-testid=email]', edgeCase.value);
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.click('[data-testid=login-btn]');
// Verify appropriate handling based on expected behavior
switch (edgeCase.expected) {
case 'unicode_support':
await expect(page).toHaveURL('/dashboard');
break;
case 'email_plus_addressing':
await expect(page).toHaveURL('/dashboard');
break;
case 'whitespace_handling':
await expect(page).toHaveURL('/dashboard');
// Verify email was trimmed
const userEmail = await page.locator('[data-testid=user-email]').textContent();
expect(userEmail?.trim()).toBe(edgeCase.value.trim());
break;
case 'case_normalization':
await expect(page).toHaveURL('/dashboard');
break;
}
});
});
});
// Accessibility and Usability
test.describe('Accessibility and Usability', () => {
test('keyboard navigation support', async () => {
// Tab through form elements
await page.keyboard.press('Tab'); // Email field
await expect(page.locator('[data-testid=email]')).toBeFocused();
await page.keyboard.press('Tab'); // Password field
await expect(page.locator('[data-testid=password]')).toBeFocused();
await page.keyboard.press('Tab'); // Remember me checkbox
await expect(page.locator('[data-testid=remember-me]')).toBeFocused();
await page.keyboard.press('Tab'); // Login button
await expect(page.locator('[data-testid=login-btn]')).toBeFocused();
// Submit with Enter
await page.fill('[data-testid=email]', TestDataFactory.validUser.email);
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.keyboard.press('Enter');
await expect(page).toHaveURL('/dashboard');
});
test('screen reader accessibility', async () => {
// Verify ARIA labels and roles
await expect(page.locator('[data-testid=email]')).toHaveAttribute('aria-label', 'Email address');
await expect(page.locator('[data-testid=password]')).toHaveAttribute('aria-label', 'Password');
await expect(page.locator('[data-testid=login-form]')).toHaveAttribute('role', 'form');
// Verify error announcements
await page.fill('[data-testid=email]', 'invalid-email');
await page.click('[data-testid=login-btn]');
await expect(page.locator('[data-testid=error-message]')).toHaveAttribute('role', 'alert');
await expect(page.locator('[data-testid=error-message]')).toHaveAttribute('aria-live', 'polite');
});
test('responsive design across viewports', async () => {
const viewports = [
{ width: 320, height: 568, name: 'mobile' },
{ width: 768, height: 1024, name: 'tablet' },
{ width: 1920, height: 1080, name: 'desktop' }
];
for (const viewport of viewports) {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
// Verify form is accessible and functional
await expect(page.locator('[data-testid=login-form]')).toBeVisible();
await expect(page.locator('[data-testid=email]')).toBeVisible();
await expect(page.locator('[data-testid=password]')).toBeVisible();
await expect(page.locator('[data-testid=login-btn]')).toBeVisible();
// Verify mobile-specific elements if on mobile
if (viewport.name === 'mobile') {
await expect(page.locator('[data-testid=mobile-keyboard-friendly]')).toBeVisible();
}
}
});
});
});
// Comprehensive CRUD Operations Test Template
test.describe('Entity Management - Complete CRUD Coverage', () => {
const entityName = '{{ENTITY_NAME}}'; // Template placeholder
const entityPath = '/{{ENTITY_PATH}}'; // Template placeholder
test.beforeEach(async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid=email]', TestDataFactory.validUser.email);
await page.fill('[data-testid=password]', TestDataFactory.validUser.password);
await page.click('[data-testid=login-btn]');
await expect(page).toHaveURL('/dashboard');
await page.goto(entityPath);
});
test.describe('List Operations - All States', () => {
test('displays empty state with create option', async () => {
// Mock empty response
await page.route(`**/api${entityPath}`, route => route.fulfill({
status: 200,
body: JSON.stringify({ data: [], total: 0 })
}));
await page.reload();
await expect(page.locator('[data-testid=empty-state]')).toBeVisible();
await expect(page.locator('[data-testid=empty-state-message]')).toContainText(`No ${entityName.toLowerCase()}s found`);
await expect(page.locator('[data-testid=create-btn]')).toBeVisible();
});
test('displays populated list with all controls', async () => {
await expect(page.locator('[data-testid=entity-list]')).toBeVisible();
await expect(page.locator('[data-testid=pagination]')).toBeVisible();
await expect(page.locator('[data-testid=sort-controls]')).toBeVisible();
await expect(page.locator('[data-testid=filter-controls]')).toBeVisible();
await expect(page.locator('[data-testid=search-input]')).toBeVisible();
});
test('handles loading states appropriately', async () => {
// Delay API response to test loading state
await page.route(`**/api${entityPath}`, route => {
setTimeout(() => route.fulfill({
status: 200,
body: JSON.stringify({ data: [], total: 0 })
}), 2000);
});
await page.reload();
await expect(page.locator('[data-testid=loading-skeleton]')).toBeVisible();
await expect(page.locator('[data-testid=loading-spinner]')).toBeVisible();
// Wait for loading to complete
await expect(page.locator('[data-testid=loading-skeleton]')).toBeHidden();
});
test('handles error states with recovery options', async () => {
// Mock error response
await page.route(`**/api${entityPath}`, route => route.fulfill({
status: 500,
body: JSON.stringify({ error: 'Internal server error' })
}));
await page.reload();
await expect(page.locator('[data-testid=error-state]')).toBeVisible();
await expect(page.locator('[data-testid=retry-btn]')).toBeVisible();
// Test retry functionality
await page.unroute(`**/api${entityPath}`);
await page.click('[data-testid=retry-btn]');
await expect(page.locator('[data-testid=entity-list]')).toBeVisible();
});
});
// Additional comprehensive test scenarios would continue here...
// Including Create, Read, Update, Delete operations with all variations
// Performance tests, security tests, integration tests, etc.
});
// Export for reuse in other test files
export { TestDataFactory };