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:
254
reports-app/frontend/tests/e2e/payments/payments.spec.js
Normal file
254
reports-app/frontend/tests/e2e/payments/payments.spec.js
Normal file
@@ -0,0 +1,254 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from '../../page-objects/LoginPage.js';
|
||||
import { PaymentsPage } from '../../page-objects/PaymentsPage.js';
|
||||
import { testCredentials } from '../../fixtures/auth.js';
|
||||
import { mockPayments } from '../../fixtures/payments.js';
|
||||
|
||||
test.describe('Payments View', () => {
|
||||
let loginPage;
|
||||
let paymentsPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
loginPage = new LoginPage(page);
|
||||
paymentsPage = new PaymentsPage(page);
|
||||
|
||||
// Mock 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
|
||||
await page.route('**/api/companies', async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify([
|
||||
{ code: 'COMP1', name: 'Compania Test 1' }
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
// Mock payments endpoint
|
||||
await page.route('**/api/payments/COMP1', async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(mockPayments),
|
||||
});
|
||||
});
|
||||
|
||||
// Login and navigate to payments
|
||||
await loginPage.navigate();
|
||||
await loginPage.login(testCredentials.valid.username, testCredentials.valid.password);
|
||||
await page.waitForURL('/dashboard');
|
||||
await paymentsPage.navigate();
|
||||
});
|
||||
|
||||
test('should display payments page correctly', async ({ page: _page }) => {
|
||||
expect(await paymentsPage.isOnPaymentsPage()).toBe(true);
|
||||
|
||||
const title = await paymentsPage.getPageTitle();
|
||||
expect(title).toContain('Încasări');
|
||||
});
|
||||
|
||||
test('should show company selection when no company selected', async ({ page: _page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
expect(await paymentsPage.isCompanySelectionVisible()).toBe(true);
|
||||
expect(await paymentsPage.isPaymentsTableVisible()).toBe(false);
|
||||
});
|
||||
|
||||
test('should display payments table after company selection', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
|
||||
await page.waitForSelector(paymentsPage.paymentsTable, { timeout: 10000 });
|
||||
expect(await paymentsPage.isPaymentsTableVisible()).toBe(true);
|
||||
});
|
||||
|
||||
test('should filter payments by search term', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Search for specific payment
|
||||
await paymentsPage.searchPayments('PAY001');
|
||||
await paymentsPage.waitForLoadingToFinish();
|
||||
|
||||
const visibleRows = await paymentsPage.getVisiblePaymentsCount();
|
||||
expect(visibleRows).toBeGreaterThan(0);
|
||||
|
||||
// Check that displayed payments contain search term
|
||||
const firstRowData = await paymentsPage.getFirstPaymentData();
|
||||
expect(firstRowData.reference).toContain('PAY001');
|
||||
});
|
||||
|
||||
test('should filter payments by method', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Filter by bank transfer
|
||||
await paymentsPage.filterByMethod('bank_transfer');
|
||||
await paymentsPage.waitForLoadingToFinish();
|
||||
|
||||
const visibleRows = await paymentsPage.getVisiblePaymentsCount();
|
||||
expect(visibleRows).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('should sort payments by date', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Click date column header to sort
|
||||
await paymentsPage.sortByColumn('date');
|
||||
await paymentsPage.waitForLoadingToFinish();
|
||||
|
||||
// Verify sorting worked
|
||||
const firstRowDate = await paymentsPage.getFirstPaymentData();
|
||||
expect(firstRowDate.date).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should display payment details when clicking on row', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Click on first payment row
|
||||
await paymentsPage.clickFirstPaymentRow();
|
||||
|
||||
// Check if details panel or modal appears
|
||||
expect(await paymentsPage.isPaymentDetailsVisible()).toBe(true);
|
||||
});
|
||||
|
||||
test('should export payments data', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Set up download handler
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
await paymentsPage.clickExportButton();
|
||||
|
||||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toContain('incasari');
|
||||
});
|
||||
|
||||
test('should display correct payment totals', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Check if totals card is visible and contains data
|
||||
if (await paymentsPage.isTotalsCardVisible()) {
|
||||
const totals = await paymentsPage.getTotalsData();
|
||||
expect(parseFloat(totals.totalAmount)).toBeGreaterThan(0);
|
||||
expect(parseInt(totals.totalCount)).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
test('should filter payments by date range', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Apply this month filter
|
||||
await paymentsPage.filterByDateRange('thisMonth');
|
||||
await paymentsPage.waitForLoadingToFinish();
|
||||
|
||||
const visibleRows = await paymentsPage.getVisiblePaymentsCount();
|
||||
expect(visibleRows).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test('should handle pagination correctly', async ({ page }) => {
|
||||
// Mock large dataset
|
||||
await page.route('**/api/payments/COMP1*', async route => {
|
||||
const url = route.request().url();
|
||||
const urlParams = new URL(url).searchParams;
|
||||
const page_num = parseInt(urlParams.get('page') || '1');
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
items: mockPayments.slice((page_num - 1) * 10, page_num * 10),
|
||||
total: 25,
|
||||
page: page_num,
|
||||
size: 10,
|
||||
pages: 3
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Check pagination controls appear
|
||||
expect(await paymentsPage.isPaginationVisible()).toBe(true);
|
||||
|
||||
// Navigate to next page
|
||||
await paymentsPage.goToNextPage();
|
||||
await paymentsPage.waitForLoadingToFinish();
|
||||
|
||||
// Verify page changed
|
||||
const currentPage = await paymentsPage.getCurrentPage();
|
||||
expect(currentPage).toBe(2);
|
||||
});
|
||||
|
||||
test('should handle API errors gracefully', async ({ page }) => {
|
||||
// Mock API error
|
||||
await page.route('**/api/payments/COMP1', async route => {
|
||||
await route.fulfill({
|
||||
status: 500,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ detail: 'Internal server error' }),
|
||||
});
|
||||
});
|
||||
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
|
||||
// Should show error message
|
||||
const errorToast = page.locator('.p-toast-message-error');
|
||||
if (await errorToast.isVisible()) {
|
||||
const errorText = await errorToast.textContent();
|
||||
expect(errorText.toLowerCase()).toContain('eroare');
|
||||
}
|
||||
});
|
||||
|
||||
test('should refresh data when refresh button clicked', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Click refresh button
|
||||
await paymentsPage.clickRefreshButton();
|
||||
await paymentsPage.waitForLoadingToFinish();
|
||||
|
||||
// Table should still be visible after refresh
|
||||
expect(await paymentsPage.isPaymentsTableVisible()).toBe(true);
|
||||
});
|
||||
|
||||
test('should group payments by method in summary view', async ({ page }) => {
|
||||
await paymentsPage.waitForPageLoad();
|
||||
await paymentsPage.selectCompany('Compania Test 1');
|
||||
await page.waitForSelector(paymentsPage.paymentsTable);
|
||||
|
||||
// Switch to summary view if available
|
||||
if (await paymentsPage.isSummaryViewAvailable()) {
|
||||
await paymentsPage.switchToSummaryView();
|
||||
await paymentsPage.waitForLoadingToFinish();
|
||||
|
||||
expect(await paymentsPage.isSummaryViewVisible()).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user