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:
228
reports-app/frontend/tests/e2e/dashboard/dashboard.spec.js
Normal file
228
reports-app/frontend/tests/e2e/dashboard/dashboard.spec.js
Normal file
@@ -0,0 +1,228 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user