Files
roa2web-service-auto/reports-app/frontend/tests/e2e/real-world-comprehensive.spec.js
Marius Mutu 6b13ffa183 Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot
Modern ERP Reports Application with microservices architecture

Tech Stack:
- Backend: FastAPI + python-oracledb (Oracle DB integration)
- Frontend: Vue.js 3 + PrimeVue + Vite
- Telegram Bot: python-telegram-bot + SQLite
- Infrastructure: Shared database pool, JWT authentication, SSH tunnel

Features:
- FastAPI backend with async Oracle connection pool
- Vue.js 3 responsive frontend with PrimeVue components
- Telegram bot alternative interface
- Microservices architecture with shared components
- Complete deployment support (Linux Docker + Windows IIS)
- Comprehensive testing (Playwright E2E + pytest)

Repository Structure:
- reports-app/ - Main application (backend, frontend, telegram-bot)
- shared/ - Shared components (database pool, auth, utils)
- deployment/ - Deployment scripts (Linux & Windows)
- docs/ - Project documentation
- security/ - Security scanning and git hooks
2025-10-25 14:55:08 +03:00

364 lines
12 KiB
JavaScript

//! 🌍 COMPREHENSIVE REAL-WORLD TESTING SUITE
//! Created: 2025-08-04
//! Purpose: Test complete application flows with real data and interactions
import { test, expect } from '@playwright/test';
import { LoginPage } from '../page-objects/LoginPage.js';
import { DashboardPage } from '../page-objects/DashboardPage.js';
test.describe('🌍 ROA2WEB Real-World Comprehensive Testing', () => {
let loginPage;
let dashboardPage;
let performanceMetrics = [];
let networkErrors = [];
let consoleErrors = [];
test.beforeEach(async ({ page }) => {
// Reset metrics
performanceMetrics = [];
networkErrors = [];
consoleErrors = [];
// Setup comprehensive monitoring
page.on('response', response => {
const timing = {
url: response.url(),
status: response.status(),
timing: response.request().timing(),
size: response.headers()['content-length'] || 0,
timestamp: new Date().toISOString()
};
performanceMetrics.push(timing);
if (response.status() >= 400) {
networkErrors.push({
url: response.url(),
status: response.status(),
statusText: response.statusText()
});
}
});
page.on('console', msg => {
if (msg.type() === 'error') {
consoleErrors.push({
text: msg.text(),
location: msg.location(),
timestamp: new Date().toISOString()
});
}
});
loginPage = new LoginPage(page);
dashboardPage = new DashboardPage(page);
});
test('🎯 COMPLETE USER JOURNEY - Login to Dashboard to Reports', async ({ page }) => {
console.log('\n🌍 === COMPLETE USER JOURNEY TEST ===');
const startTime = Date.now();
// Phase 1: Navigate to Login
console.log('\n📍 Phase 1: Navigate to Login');
await loginPage.navigate();
await page.screenshot({ path: 'journey-01-login-page.png' });
// Verify login page loads correctly
await expect(page).toHaveTitle(/ROA Reports/);
console.log('✅ Login page loaded');
// Phase 2: Attempt Authentication with Real Credentials
console.log('\n📍 Phase 2: Authentication Flow');
// Test with test credentials first
await page.fill('#username', 'MARIUS M');
await page.fill('#password input', 'PAROLA9911');
// Verify button becomes enabled
await page.waitForTimeout(200);
const buttonEnabled = !(await page.locator('button[type="submit"]').isDisabled());
expect(buttonEnabled).toBe(true);
console.log('✅ Login button enabled with credentials');
// Monitor the authentication request
const authPromise = page.waitForResponse('**/auth/login').catch(() => null);
await page.click('button[type="submit"]');
const authResponse = await authPromise;
if (authResponse) {
console.log(`📊 Auth Response: ${authResponse.status()}`);
if (authResponse.status() === 200) {
console.log('✅ Authentication successful');
// Wait for redirect to dashboard
await page.waitForURL('**/dashboard', { timeout: 10000 }).catch(() => {
console.log('⚠️ No redirect to dashboard - checking current state');
});
} else if (authResponse.status() === 422) {
console.log('❌ Validation error - checking response');
const responseBody = await authResponse.text();
console.log('Response body:', responseBody);
} else if (authResponse.status() === 401) {
console.log('❌ Authentication failed - invalid credentials');
} else {
console.log(`❌ Unexpected response: ${authResponse.status()}`);
}
} else {
console.log('⚠️ No authentication response received');
}
await page.screenshot({ path: 'journey-02-after-auth.png' });
// Phase 3: Dashboard Interaction (if successful)
const currentUrl = page.url();
console.log(`📍 Current URL: ${currentUrl}`);
if (currentUrl.includes('/dashboard')) {
console.log('\n📍 Phase 3: Dashboard Interaction');
// Wait for dashboard to load
await page.waitForSelector('.dashboard-container', { timeout: 5000 }).catch(() => {
console.log('⚠️ Dashboard container not found');
});
// Test dashboard functionality
const companySelector = page.locator('select, .p-dropdown');
if (await companySelector.first().isVisible()) {
console.log('✅ Company selector visible');
// Try to select a company
await companySelector.first().click();
await page.waitForTimeout(500);
const options = page.locator('.p-dropdown-item');
const optionCount = await options.count();
if (optionCount > 0) {
await options.first().click();
console.log('✅ Company selected');
// Wait for data to load
await page.waitForTimeout(2000);
// Check if statistics are displayed
const statsCards = page.locator('.stat-card, .dashboard-stat, .metric-card');
const statsCount = await statsCards.count();
console.log(`📊 Statistics cards found: ${statsCount}`);
}
}
await page.screenshot({ path: 'journey-03-dashboard.png' });
// Phase 4: Navigation Test
console.log('\n📍 Phase 4: Navigation Test');
const navLinks = page.locator('nav a, .nav-link, .menu-item');
const navCount = await navLinks.count();
console.log(`🧭 Navigation links found: ${navCount}`);
if (navCount > 0) {
// Try to navigate to invoices
const invoicesLink = page.locator('text=/facturi|invoice/i').first();
if (await invoicesLink.isVisible()) {
await invoicesLink.click();
await page.waitForTimeout(1000);
console.log('✅ Navigated to invoices');
await page.screenshot({ path: 'journey-04-invoices.png' });
}
}
}
const totalTime = Date.now() - startTime;
console.log(`\n⏱️ Total journey time: ${totalTime}ms`);
});
test('🔍 NETWORK PERFORMANCE ANALYSIS', async ({ page }) => {
console.log('\n🔍 === NETWORK PERFORMANCE ANALYSIS ===');
// Navigate and monitor performance
await loginPage.navigate();
// Wait for all initial requests to complete
await page.waitForLoadState('networkidle');
// Analyze performance metrics
const slowRequests = performanceMetrics.filter(metric => {
const timing = metric.timing;
return timing && (timing.responseEnd - timing.requestStart) > 2000;
});
const failedRequests = performanceMetrics.filter(metric => metric.status >= 400);
console.log(`📊 Total requests: ${performanceMetrics.length}`);
console.log(`🐌 Slow requests (>2s): ${slowRequests.length}`);
console.log(`❌ Failed requests: ${failedRequests.length}`);
if (slowRequests.length > 0) {
console.log('\n🐌 Slow requests:');
slowRequests.forEach(request => {
const duration = request.timing ?
(request.timing.responseEnd - request.timing.requestStart) : 'unknown';
console.log(` - ${request.url}: ${duration}ms`);
});
}
if (failedRequests.length > 0) {
console.log('\n❌ Failed requests:');
failedRequests.forEach(request => {
console.log(` - ${request.status} ${request.url}`);
});
}
// Performance assertions
expect(slowRequests.length).toBeLessThan(5); // Max 5 slow requests
expect(failedRequests.length).toBeLessThan(3); // Max 3 failed requests
});
test('🧪 ERROR HANDLING STRESS TEST', async ({ page }) => {
console.log('\n🧪 === ERROR HANDLING STRESS TEST ===');
await loginPage.navigate();
// Test various error scenarios
const errorScenarios = [
{
name: 'Server Error 500',
setup: () => page.route('**/auth/login', route =>
route.fulfill({ status: 500, body: '{"detail": "Internal server error"}' })
)
},
{
name: 'Network Timeout',
setup: () => page.route('**/auth/login', route => route.abort('timeout'))
},
{
name: 'Invalid JSON Response',
setup: () => page.route('**/auth/login', route =>
route.fulfill({ status: 200, body: 'invalid json' })
)
},
{
name: 'Rate Limiting 429',
setup: () => page.route('**/auth/login', route =>
route.fulfill({ status: 429, body: '{"detail": "Too many requests"}' })
)
}
];
for (const scenario of errorScenarios) {
console.log(`\n--- Testing: ${scenario.name} ---`);
// Setup error scenario
await scenario.setup();
// Fill credentials and submit
await page.fill('#username', 'test');
await page.fill('#password input', 'test');
await page.click('button[type="submit"]');
// Wait for error handling
await page.waitForTimeout(3000);
// Check if error is handled gracefully
const errorMessage = await page.locator('.error-message, .p-toast-error').isVisible();
console.log(`Error message shown: ${errorMessage}`);
// Verify user is still on login page
const isOnLogin = await loginPage.isOnLoginPage();
console.log(`Still on login page: ${isOnLogin}`);
expect(isOnLogin).toBe(true);
// Clear the route override
await page.unroute('**/auth/login');
// Clear form for next test
await page.fill('#username', '');
await page.fill('#password input', '');
await page.waitForTimeout(500);
}
});
test('📱 RESPONSIVE DESIGN VALIDATION', async ({ page }) => {
console.log('\n📱 === RESPONSIVE DESIGN VALIDATION ===');
const viewports = [
{ name: 'Mobile Portrait', width: 375, height: 667 },
{ name: 'Mobile Landscape', width: 667, height: 375 },
{ name: 'Tablet', width: 768, height: 1024 },
{ name: 'Desktop', width: 1920, height: 1080 }
];
for (const viewport of viewports) {
console.log(`\n--- Testing: ${viewport.name} (${viewport.width}x${viewport.height}) ---`);
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await loginPage.navigate();
// Wait for layout to adjust
await page.waitForTimeout(500);
// Check if login form is visible and accessible
const formVisible = await page.locator('.login-form').isVisible();
const buttonVisible = await page.locator('button[type="submit"]').isVisible();
console.log(`Form visible: ${formVisible}`);
console.log(`Button visible: ${buttonVisible}`);
expect(formVisible).toBe(true);
expect(buttonVisible).toBe(true);
// Test form interaction
await page.fill('#username', 'test');
await page.fill('#password input', 'test');
const buttonEnabled = !(await page.locator('button[type="submit"]').isDisabled());
expect(buttonEnabled).toBe(true);
// Take screenshot for visual verification
await page.screenshot({
path: `responsive-${viewport.name.toLowerCase().replace(' ', '-')}.png`,
fullPage: true
});
}
});
test.afterEach(async ({ page }) => {
// Generate comprehensive test report
console.log('\n📋 === COMPREHENSIVE TEST REPORT ===');
console.log(`🌐 Total Network Requests: ${performanceMetrics.length}`);
console.log(`❌ Network Errors: ${networkErrors.length}`);
console.log(`🔥 Console Errors: ${consoleErrors.length}`);
if (networkErrors.length > 0) {
console.log('\n❌ Network Errors:');
networkErrors.forEach(error => {
console.log(` - ${error.status} ${error.url}`);
});
}
if (consoleErrors.length > 0) {
console.log('\n🔥 Console Errors:');
consoleErrors.forEach(error => {
console.log(` - ${error.text}`);
});
}
// Performance summary
const avgResponseTime = performanceMetrics.length > 0 ?
performanceMetrics.reduce((sum, metric) => {
const timing = metric.timing;
return sum + (timing ? (timing.responseEnd - timing.requestStart) : 0);
}, 0) / performanceMetrics.length : 0;
console.log(`⚡ Average Response Time: ${Math.round(avgResponseTime)}ms`);
if (avgResponseTime > 1000) {
console.log('⚠️ Performance Warning: Average response time is high');
}
});
});