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
324 lines
11 KiB
JavaScript
324 lines
11 KiB
JavaScript
//! 🔍 DEBUGGING COMPREHENSIVE TEST - ROA2WEB Real Issues Detection
|
|
//! Created: 2025-08-04
|
|
//! Purpose: Find and fix REAL problems, not just "passing tests"
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { LoginPage } from '../page-objects/LoginPage.js';
|
|
|
|
test.describe('🔍 ROA2WEB Real Issues Debugging Suite', () => {
|
|
let loginPage;
|
|
let networkRequests = [];
|
|
let consoleErrors = [];
|
|
let apiResponses = [];
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
// Reset monitoring arrays
|
|
networkRequests = [];
|
|
consoleErrors = [];
|
|
apiResponses = [];
|
|
|
|
// Setup comprehensive monitoring
|
|
page.on('request', request => {
|
|
networkRequests.push({
|
|
url: request.url(),
|
|
method: request.method(),
|
|
headers: request.headers(),
|
|
postData: request.postData(),
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
});
|
|
|
|
page.on('response', response => {
|
|
apiResponses.push({
|
|
url: response.url(),
|
|
status: response.status(),
|
|
statusText: response.statusText(),
|
|
headers: response.headers(),
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
// Log failed requests immediately
|
|
if (response.status() >= 400) {
|
|
console.log(`❌ API Error: ${response.status()} ${response.url()}`);
|
|
}
|
|
});
|
|
|
|
page.on('console', msg => {
|
|
if (msg.type() === 'error') {
|
|
const error = {
|
|
type: msg.type(),
|
|
text: msg.text(),
|
|
location: msg.location(),
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
consoleErrors.push(error);
|
|
console.log(`🔥 Console Error: ${msg.text()}`);
|
|
}
|
|
});
|
|
|
|
page.on('pageerror', err => {
|
|
consoleErrors.push({
|
|
type: 'pageerror',
|
|
text: err.message,
|
|
stack: err.stack,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
console.log(`💥 Page Error: ${err.message}`);
|
|
});
|
|
|
|
loginPage = new LoginPage(page);
|
|
await loginPage.navigate();
|
|
});
|
|
|
|
test('🧪 REAL AUTH FLOW - Find FormData vs JSON Issues', async ({ page }) => {
|
|
console.log('\n🔍 === TESTING REAL AUTHENTICATION FLOW ===');
|
|
|
|
// Fill real credentials (update these with actual test credentials)
|
|
const username = 'test_user';
|
|
const password = 'test_password';
|
|
|
|
await page.fill('#username', username);
|
|
await page.fill('#password input', password);
|
|
|
|
// Monitor the actual request being sent
|
|
const [response] = await Promise.all([
|
|
page.waitForResponse('**/auth/login'),
|
|
page.click('button[type="submit"]')
|
|
]);
|
|
|
|
// CRITICAL: Analyze the actual request format
|
|
const request = response.request();
|
|
const postData = request.postData();
|
|
const contentType = request.headers()['content-type'];
|
|
|
|
console.log('\n📊 === REQUEST ANALYSIS ===');
|
|
console.log('Content-Type:', contentType);
|
|
console.log('Request Method:', request.method());
|
|
console.log('Request Body:', postData);
|
|
console.log('Response Status:', response.status());
|
|
|
|
// Check if FormData or JSON is being sent
|
|
if (contentType && contentType.includes('application/json')) {
|
|
console.log('✅ Sending JSON (correct)');
|
|
try {
|
|
const jsonData = JSON.parse(postData);
|
|
expect(jsonData).toHaveProperty('username');
|
|
expect(jsonData).toHaveProperty('password');
|
|
} catch (e) {
|
|
console.log('❌ Invalid JSON format');
|
|
}
|
|
} else if (contentType && contentType.includes('multipart/form-data')) {
|
|
console.log('⚠️ Sending FormData (may cause issues)');
|
|
} else {
|
|
console.log('❓ Unknown content type:', contentType);
|
|
}
|
|
|
|
// Check response
|
|
if (response.status() === 422) {
|
|
const responseBody = await response.text();
|
|
console.log('🚨 422 Validation Error:', responseBody);
|
|
}
|
|
|
|
// Generate comprehensive monitoring report
|
|
console.log('\n📈 === MONITORING REPORT ===');
|
|
console.log(`Network Requests: ${networkRequests.length}`);
|
|
console.log(`API Responses: ${apiResponses.length}`);
|
|
console.log(`Console Errors: ${consoleErrors.length}`);
|
|
|
|
// Take screenshot for analysis
|
|
await page.screenshot({
|
|
path: 'debug-auth-flow.png',
|
|
fullPage: true
|
|
});
|
|
});
|
|
|
|
test('🔧 LOGIN BUTTON STATE - Debug Disabled Logic', async ({ page }) => {
|
|
console.log('\n🔍 === DEBUGGING LOGIN BUTTON STATE ===');
|
|
|
|
// Test initial state
|
|
const initialDisabled = await page.locator('button[type="submit"]').isDisabled();
|
|
console.log('Initial button disabled:', initialDisabled);
|
|
|
|
// Test empty fields
|
|
await page.fill('#username', '');
|
|
await page.fill('#password input', '');
|
|
|
|
const emptyFieldsDisabled = await page.locator('button[type="submit"]').isDisabled();
|
|
console.log('Empty fields - button disabled:', emptyFieldsDisabled);
|
|
|
|
// Test with only username
|
|
await page.fill('#username', 'test');
|
|
const usernameOnlyDisabled = await page.locator('button[type="submit"]').isDisabled();
|
|
console.log('Username only - button disabled:', usernameOnlyDisabled);
|
|
|
|
// Test with both fields
|
|
await page.fill('#password input', 'password');
|
|
const bothFieldsDisabled = await page.locator('button[type="submit"]').isDisabled();
|
|
console.log('Both fields - button disabled:', bothFieldsDisabled);
|
|
|
|
// Check form validation state
|
|
const formValidation = await page.evaluate(() => {
|
|
const usernameInput = document.getElementById('username');
|
|
const passwordInput = document.querySelector('#password input');
|
|
const button = document.querySelector('button[type="submit"]');
|
|
|
|
return {
|
|
usernameValue: usernameInput?.value,
|
|
passwordValue: passwordInput?.value,
|
|
buttonDisabled: button?.disabled,
|
|
buttonClasses: button?.className,
|
|
usernameValid: usernameInput?.checkValidity(),
|
|
passwordValid: passwordInput?.checkValidity()
|
|
};
|
|
});
|
|
|
|
console.log('Form validation state:', JSON.stringify(formValidation, null, 2));
|
|
|
|
// Take screenshot of current state
|
|
await page.screenshot({ path: 'debug-button-state.png' });
|
|
});
|
|
|
|
test('🚨 ERROR MESSAGE FORMAT - Debug Toast Issues', async ({ page }) => {
|
|
console.log('\n🔍 === DEBUGGING ERROR MESSAGE FORMAT ===');
|
|
|
|
// Force a network error by using wrong endpoint
|
|
await page.route('**/auth/login', async route => {
|
|
await route.fulfill({
|
|
status: 500,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ detail: 'Server error for testing' })
|
|
});
|
|
});
|
|
|
|
await page.fill('#username', 'test');
|
|
await page.fill('#password input', 'test');
|
|
await page.click('button[type="submit"]');
|
|
|
|
// Wait for error message and analyze its content
|
|
await page.waitForTimeout(2000); // Wait for toast to appear
|
|
|
|
// Check various possible error message selectors
|
|
const errorSelectors = [
|
|
'.error-message',
|
|
'.p-toast-message-error',
|
|
'.p-toast-message-text',
|
|
'.p-toast-summary',
|
|
'.p-toast-detail'
|
|
];
|
|
|
|
for (const selector of errorSelectors) {
|
|
const elements = await page.locator(selector).all();
|
|
for (let i = 0; i < elements.length; i++) {
|
|
const text = await elements[i].textContent();
|
|
if (text && text.trim()) {
|
|
console.log(`Error message found in ${selector}[${i}]:`, text);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check all visible text content that might contain error messages
|
|
const allText = await page.evaluate(() => {
|
|
const textNodes = [];
|
|
const walker = document.createTreeWalker(
|
|
document.body,
|
|
NodeFilter.SHOW_TEXT,
|
|
null,
|
|
false
|
|
);
|
|
|
|
let node;
|
|
while (node = walker.nextNode()) {
|
|
const text = node.textContent.trim();
|
|
if (text.toLowerCase().includes('eroare') ||
|
|
text.toLowerCase().includes('error') ||
|
|
text.toLowerCase().includes('conectare')) {
|
|
textNodes.push(text);
|
|
}
|
|
}
|
|
return textNodes;
|
|
});
|
|
|
|
console.log('All error-related text found:', allText);
|
|
|
|
// Take screenshot of error state
|
|
await page.screenshot({ path: 'debug-error-messages.png' });
|
|
});
|
|
|
|
test('🌐 NETWORK MONITORING - Real API Behavior', async ({ page }) => {
|
|
console.log('\n🔍 === COMPREHENSIVE NETWORK MONITORING ===');
|
|
|
|
// Test various scenarios
|
|
const testScenarios = [
|
|
{ name: 'Valid Login', username: 'valid_user', password: 'valid_pass' },
|
|
{ name: 'Invalid Credentials', username: 'invalid', password: 'invalid' },
|
|
{ name: 'Empty Credentials', username: '', password: '' }
|
|
];
|
|
|
|
for (const scenario of testScenarios) {
|
|
console.log(`\n--- Testing: ${scenario.name} ---`);
|
|
|
|
// Clear form
|
|
await page.fill('#username', '');
|
|
await page.fill('#password input', '');
|
|
|
|
// Fill credentials if provided
|
|
if (scenario.username) await page.fill('#username', scenario.username);
|
|
if (scenario.password) await page.fill('#password input', scenario.password);
|
|
|
|
// Reset monitoring arrays for this scenario
|
|
networkRequests.length = 0;
|
|
apiResponses.length = 0;
|
|
consoleErrors.length = 0;
|
|
|
|
// Try to submit (if button is enabled)
|
|
const isDisabled = await page.locator('button[type="submit"]').isDisabled();
|
|
if (!isDisabled) {
|
|
try {
|
|
const [response] = await Promise.all([
|
|
page.waitForResponse('**/auth/login', { timeout: 5000 }),
|
|
page.click('button[type="submit"]')
|
|
]);
|
|
|
|
console.log(`Response Status: ${response.status()}`);
|
|
const responseBody = await response.text();
|
|
console.log(`Response Body: ${responseBody.substring(0, 200)}...`);
|
|
|
|
} catch (error) {
|
|
console.log(`No API call made: ${error.message}`);
|
|
}
|
|
} else {
|
|
console.log('Button is disabled - no API call expected');
|
|
}
|
|
|
|
// Wait a bit for any async operations
|
|
await page.waitForTimeout(1000);
|
|
|
|
console.log(`Network requests: ${networkRequests.length}`);
|
|
console.log(`Console errors: ${consoleErrors.length}`);
|
|
}
|
|
});
|
|
|
|
test.afterEach(async ({ page }) => {
|
|
// Generate final report
|
|
console.log('\n📋 === FINAL TEST REPORT ===');
|
|
console.log(`Total Network Requests: ${networkRequests.length}`);
|
|
console.log(`Total API Responses: ${apiResponses.length}`);
|
|
console.log(`Total Console Errors: ${consoleErrors.length}`);
|
|
|
|
if (consoleErrors.length > 0) {
|
|
console.log('\n🚨 Console Errors Found:');
|
|
consoleErrors.forEach((error, index) => {
|
|
console.log(`${index + 1}. ${error.text}`);
|
|
});
|
|
}
|
|
|
|
if (apiResponses.some(r => r.status >= 400)) {
|
|
console.log('\n❌ Failed API Requests:');
|
|
apiResponses
|
|
.filter(r => r.status >= 400)
|
|
.forEach(response => {
|
|
console.log(`${response.status} ${response.url}`);
|
|
});
|
|
}
|
|
});
|
|
}); |