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
This commit is contained in:
194
reports-app/frontend/tests/utils/helpers.js
Normal file
194
reports-app/frontend/tests/utils/helpers.js
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Test utility functions for ROA2WEB frontend testing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wait for element to be visible with custom timeout
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {string} selector - CSS selector
|
||||
* @param {number} timeout - Timeout in milliseconds
|
||||
*/
|
||||
export async function waitForVisible(page, selector, timeout = 10000) {
|
||||
await page.waitForSelector(selector, {
|
||||
state: 'visible',
|
||||
timeout
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for all API calls to complete
|
||||
* @param {Page} page - Playwright page object
|
||||
*/
|
||||
export async function waitForApiCalls(page) {
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
/**
|
||||
* Take screenshot with timestamp
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {string} name - Screenshot name
|
||||
*/
|
||||
export async function takeTimestampedScreenshot(page, name) {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
await page.screenshot({
|
||||
path: `test-results/${name}-${timestamp}.png`,
|
||||
fullPage: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if element exists in DOM (without waiting)
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {string} selector - CSS selector
|
||||
* @returns {boolean} True if element exists
|
||||
*/
|
||||
export async function elementExists(page, selector) {
|
||||
const element = await page.$(selector);
|
||||
return element !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element text content safely
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {string} selector - CSS selector
|
||||
* @returns {string|null} Text content or null if element not found
|
||||
*/
|
||||
export async function getTextContent(page, selector) {
|
||||
try {
|
||||
const element = await page.locator(selector);
|
||||
if (await element.isVisible()) {
|
||||
return await element.textContent();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Element not found: ${selector}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for toast message and return its content
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {string} type - Toast type: 'error', 'success', 'info', 'warn'
|
||||
* @param {number} timeout - Timeout in milliseconds
|
||||
* @returns {string|null} Toast message content
|
||||
*/
|
||||
export async function waitForToast(page, type = 'error', timeout = 5000) {
|
||||
try {
|
||||
const toastSelector = `.p-toast-message-${type}`;
|
||||
await page.waitForSelector(toastSelector, { timeout });
|
||||
return await page.locator(toastSelector).textContent();
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill form fields from object
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {Object} fields - Object with selector: value pairs
|
||||
*/
|
||||
export async function fillForm(page, fields) {
|
||||
for (const [selector, value] of Object.entries(fields)) {
|
||||
await page.fill(selector, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current URL matches pattern
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {string} pattern - URL pattern to match
|
||||
* @returns {boolean} True if URL matches
|
||||
*/
|
||||
export function urlMatches(page, pattern) {
|
||||
return page.url().includes(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock successful authentication for tests
|
||||
* @param {Page} page - Playwright page object
|
||||
*/
|
||||
export async function mockSuccessfulAuth(page) {
|
||||
await page.route('**/api/auth/login', async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
access_token: 'mock_access_token',
|
||||
refresh_token: 'mock_refresh_token',
|
||||
user: {
|
||||
id: 1,
|
||||
username: 'testuser',
|
||||
full_name: 'Test User'
|
||||
}
|
||||
}),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock companies API
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {Array} companies - Array of company objects
|
||||
*/
|
||||
export async function mockCompanies(page, companies = []) {
|
||||
const defaultCompanies = [
|
||||
{ code: 'COMP1', name: 'Test Company 1' },
|
||||
{ code: 'COMP2', name: 'Test Company 2' }
|
||||
];
|
||||
|
||||
await page.route('**/api/companies', async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(companies.length > 0 ? companies : defaultCompanies),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock API error response
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {string} endpoint - API endpoint pattern
|
||||
* @param {number} status - HTTP status code
|
||||
* @param {string} message - Error message
|
||||
*/
|
||||
export async function mockApiError(page, endpoint, status = 500, message = 'Internal server error') {
|
||||
await page.route(endpoint, async route => {
|
||||
await route.fulfill({
|
||||
status,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
detail: message
|
||||
}),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all localStorage data
|
||||
* @param {Page} page - Playwright page object
|
||||
*/
|
||||
export async function clearStorage(page) {
|
||||
await page.evaluate(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set viewport to common device size
|
||||
* @param {Page} page - Playwright page object
|
||||
* @param {string} device - Device name: 'mobile', 'tablet', 'desktop'
|
||||
*/
|
||||
export async function setDeviceViewport(page, device) {
|
||||
const viewports = {
|
||||
mobile: { width: 375, height: 667 },
|
||||
tablet: { width: 768, height: 1024 },
|
||||
desktop: { width: 1024, height: 768 },
|
||||
wide: { width: 1920, height: 1080 }
|
||||
};
|
||||
|
||||
if (viewports[device]) {
|
||||
await page.setViewportSize(viewports[device]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user