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
228 lines
7.3 KiB
JavaScript
228 lines
7.3 KiB
JavaScript
import { test, expect } from '@playwright/test';
|
|
import { LoginPage } from '../../page-objects/LoginPage.js';
|
|
import { DashboardPage } from '../../page-objects/DashboardPage.js';
|
|
import { testCredentials } from '../../fixtures/auth.js';
|
|
|
|
test.describe('Dashboard View', () => {
|
|
let loginPage;
|
|
let dashboardPage;
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
loginPage = new LoginPage(page);
|
|
dashboardPage = new DashboardPage(page);
|
|
|
|
// Mock successful authentication
|
|
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 endpoint
|
|
await page.route('**/api/companies', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([
|
|
{ code: 'COMP1', name: 'Compania Test 1' },
|
|
{ code: 'COMP2', name: 'Compania Test 2' }
|
|
]),
|
|
});
|
|
});
|
|
|
|
// Mock invoices summary endpoint
|
|
await page.route('**/api/invoices/*/summary', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
total: 150,
|
|
paid: 120,
|
|
overdue: 30,
|
|
amount: 850000.50
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Mock payments summary endpoint
|
|
await page.route('**/api/payments/*/summary', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
total: 125,
|
|
amount: 750000.25
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Login first
|
|
await loginPage.navigate();
|
|
await loginPage.login(testCredentials.valid.username, testCredentials.valid.password);
|
|
await page.waitForURL('/dashboard');
|
|
});
|
|
|
|
test('should display dashboard page correctly', async ({ page: _page }) => {
|
|
// Check page elements
|
|
expect(await dashboardPage.isOnDashboardPage()).toBe(true);
|
|
|
|
// Check page title contains "Dashboard"
|
|
const title = await dashboardPage.getPageTitle();
|
|
expect(title).toContain('Dashboard');
|
|
|
|
// Check welcome message includes username
|
|
const welcomeMessage = await dashboardPage.getWelcomeMessage();
|
|
expect(welcomeMessage).toContain('testuser');
|
|
});
|
|
|
|
test('should show company selection when no company selected', async ({ page: _page }) => {
|
|
// Wait for dashboard to load
|
|
await dashboardPage.waitForDashboardLoad();
|
|
|
|
// Should show company selection card
|
|
expect(await dashboardPage.isCompanySelectionVisible()).toBe(true);
|
|
|
|
// Dashboard content should not be visible yet
|
|
expect(await dashboardPage.isDashboardContentVisible()).toBe(false);
|
|
});
|
|
|
|
test('should display dashboard content after company selection', async ({ page }) => {
|
|
// Wait for dashboard to load
|
|
await dashboardPage.waitForDashboardLoad();
|
|
|
|
// Select a company
|
|
await dashboardPage.selectCompany('Compania Test 1');
|
|
|
|
// Wait for dashboard content to appear
|
|
await page.waitForSelector(dashboardPage.dashboardContent, { timeout: 10000 });
|
|
|
|
// Dashboard content should now be visible
|
|
expect(await dashboardPage.isDashboardContentVisible()).toBe(true);
|
|
|
|
// Stats cards should be visible
|
|
expect(await dashboardPage.areStatsCardsVisible()).toBe(true);
|
|
});
|
|
|
|
test('should display correct statistics after company selection', async ({ page }) => {
|
|
// Wait for dashboard to load and select company
|
|
await dashboardPage.waitForDashboardLoad();
|
|
await dashboardPage.selectCompany('Compania Test 1');
|
|
|
|
// Wait for stats to load
|
|
await page.waitForSelector(dashboardPage.statsGrid, { timeout: 10000 });
|
|
|
|
// Check statistics values
|
|
const stats = await dashboardPage.getStatsData();
|
|
|
|
expect(stats.invoices).toBe('150');
|
|
expect(stats.payments).toBe('125');
|
|
expect(stats.company).toContain('Compania Test 1');
|
|
});
|
|
|
|
test('should navigate to invoices view when clicking invoices action', async ({ page }) => {
|
|
// Setup dashboard with company selected
|
|
await dashboardPage.waitForDashboardLoad();
|
|
await dashboardPage.selectCompany('Compania Test 1');
|
|
await page.waitForSelector(dashboardPage.dashboardContent);
|
|
|
|
// Click invoices action button
|
|
await dashboardPage.clickInvoicesAction();
|
|
|
|
// Should navigate to invoices page
|
|
await page.waitForURL('/invoices');
|
|
expect(page.url()).toContain('/invoices');
|
|
});
|
|
|
|
test('should navigate to payments view when clicking payments action', async ({ page }) => {
|
|
// Setup dashboard with company selected
|
|
await dashboardPage.waitForDashboardLoad();
|
|
await dashboardPage.selectCompany('Compania Test 1');
|
|
await page.waitForSelector(dashboardPage.dashboardContent);
|
|
|
|
// Click payments action button
|
|
await dashboardPage.clickPaymentsAction();
|
|
|
|
// Should navigate to payments page
|
|
await page.waitForURL('/payments');
|
|
expect(page.url()).toContain('/payments');
|
|
});
|
|
|
|
test('should handle API errors gracefully', async ({ page }) => {
|
|
// Mock companies API error
|
|
await page.route('**/api/companies', async route => {
|
|
await route.fulfill({
|
|
status: 500,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
detail: 'Internal server error'
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Navigate to dashboard
|
|
await dashboardPage.navigate();
|
|
|
|
// Should still show the page but might show error messages
|
|
expect(await dashboardPage.isOnDashboardPage()).toBe(true);
|
|
|
|
// Check for error toast messages
|
|
const errorToast = page.locator('.p-toast-message-error');
|
|
if (await errorToast.isVisible()) {
|
|
const errorText = await errorToast.textContent();
|
|
expect(errorText.toLowerCase()).toContain('eroare');
|
|
}
|
|
});
|
|
|
|
test('should update stats when switching between companies', async ({ page }) => {
|
|
// Mock different stats for second company
|
|
await page.route('**/api/invoices/COMP2/summary', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
total: 200,
|
|
paid: 180,
|
|
overdue: 20,
|
|
amount: 1200000.75
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/payments/COMP2/summary', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
total: 175,
|
|
amount: 950000.50
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Select first company
|
|
await dashboardPage.waitForDashboardLoad();
|
|
await dashboardPage.selectCompany('Compania Test 1');
|
|
await page.waitForSelector(dashboardPage.statsGrid);
|
|
|
|
const stats1 = await dashboardPage.getStatsData();
|
|
expect(stats1.invoices).toBe('150');
|
|
|
|
// Switch to second company
|
|
await dashboardPage.selectCompany('Compania Test 2');
|
|
await dashboardPage.waitForLoadingToFinish();
|
|
|
|
const stats2 = await dashboardPage.getStatsData();
|
|
expect(stats2.invoices).toBe('200');
|
|
expect(stats2.company).toContain('Compania Test 2');
|
|
});
|
|
}); |