import { test, expect } from '@playwright/test'; import { LoginPage } from '../../page-objects/LoginPage.js'; import { InvoicesPage } from '../../page-objects/InvoicesPage.js'; import { testCredentials } from '../../fixtures/auth.js'; import { mockInvoices } from '../../fixtures/invoices.js'; test.describe('Invoices View', () => { let loginPage; let invoicesPage; test.beforeEach(async ({ page }) => { loginPage = new LoginPage(page); invoicesPage = new InvoicesPage(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 invoices endpoint - FIX: Use query parameters instead of path parameter await page.route('**/api/invoices**', async route => { const url = route.request().url(); const urlParams = new URL(url).searchParams; const partnerType = urlParams.get('partner_type') || 'CLIENTI'; // Return different data based on partner_type const invoicesData = partnerType === 'CLIENTI' ? mockInvoices.filter(inv => inv.type === 'client') : mockInvoices.filter(inv => inv.type === 'supplier'); await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ invoices: invoicesData, total_count: invoicesData.length, filtered_count: invoicesData.length, total_amount: invoicesData.reduce((sum, inv) => sum + inv.totctva, 0), page: parseInt(urlParams.get('page') || '1'), page_size: parseInt(urlParams.get('page_size') || '50'), has_more: false }), }); }); // Login and navigate to invoices await loginPage.navigate(); await loginPage.login(testCredentials.valid.username, testCredentials.valid.password); await page.waitForURL('/dashboard'); await invoicesPage.navigate(); }); test('should display invoices page correctly', async ({ page: _page }) => { expect(await invoicesPage.isOnInvoicesPage()).toBe(true); const title = await invoicesPage.getPageTitle(); expect(title).toContain('Facturi'); }); test('should show company selection when no company selected', async ({ page: _page }) => { await invoicesPage.waitForPageLoad(); expect(await invoicesPage.isCompanySelectionVisible()).toBe(true); expect(await invoicesPage.isInvoicesTableVisible()).toBe(false); }); test('should display invoices table after company selection', async ({ page }) => { await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable, { timeout: 10000 }); expect(await invoicesPage.isInvoicesTableVisible()).toBe(true); }); test('should filter invoices by search term', async ({ page }) => { await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Search for specific invoice await invoicesPage.searchInvoices('INV001'); await invoicesPage.waitForLoadingToFinish(); const visibleRows = await invoicesPage.getVisibleInvoicesCount(); expect(visibleRows).toBeGreaterThan(0); // Check that displayed invoices contain search term const firstRowData = await invoicesPage.getFirstInvoiceData(); expect(firstRowData.number).toContain('INV001'); }); test('should filter invoices by status', async ({ page }) => { await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Filter by paid status await invoicesPage.filterByStatus('paid'); await invoicesPage.waitForLoadingToFinish(); const visibleRows = await invoicesPage.getVisibleInvoicesCount(); expect(visibleRows).toBeGreaterThan(0); }); test('should sort invoices by date', async ({ page }) => { await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Click date column header to sort await invoicesPage.sortByColumn('date'); await invoicesPage.waitForLoadingToFinish(); // Verify sorting worked const firstRowDate = await invoicesPage.getFirstInvoiceData(); expect(firstRowDate.date).toBeTruthy(); }); test('should display invoice details when clicking on row', async ({ page }) => { await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Click on first invoice row await invoicesPage.clickFirstInvoiceRow(); // Check if details panel or modal appears expect(await invoicesPage.isInvoiceDetailsVisible()).toBe(true); }); test('should export invoices data', async ({ page }) => { await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Set up download handler const downloadPromise = page.waitForEvent('download'); await invoicesPage.clickExportButton(); const download = await downloadPromise; expect(download.suggestedFilename()).toContain('facturi'); }); test('should handle pagination correctly', async ({ page }) => { // Mock large dataset await page.route('**/api/invoices/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: mockInvoices.slice((page_num - 1) * 10, page_num * 10), total: 25, page: page_num, size: 10, pages: 3 }), }); }); await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Check pagination controls appear expect(await invoicesPage.isPaginationVisible()).toBe(true); // Navigate to next page await invoicesPage.goToNextPage(); await invoicesPage.waitForLoadingToFinish(); // Verify page changed const currentPage = await invoicesPage.getCurrentPage(); expect(currentPage).toBe(2); }); test('should handle API errors gracefully', async ({ page }) => { // Mock API error await page.route('**/api/invoices/COMP1', async route => { await route.fulfill({ status: 500, contentType: 'application/json', body: JSON.stringify({ detail: 'Internal server error' }), }); }); await invoicesPage.waitForPageLoad(); await invoicesPage.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 invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Click refresh button await invoicesPage.clickRefreshButton(); await invoicesPage.waitForLoadingToFinish(); // Table should still be visible after refresh expect(await invoicesPage.isInvoicesTableVisible()).toBe(true); }); // NEW TESTS for fixed issues test('should filter by invoice type (CLIENTI/FURNIZORI)', async ({ page }) => { let capturedPartnerType = null; // Intercept API requests to verify partner_type parameter await page.route('**/api/invoices**', async route => { const url = route.request().url(); const urlParams = new URL(url).searchParams; capturedPartnerType = urlParams.get('partner_type'); await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ invoices: [], total_count: 0, filtered_count: 0, total_amount: 0, page: 1, page_size: 50, has_more: false }), }); }); await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Select FURNIZORI from dropdown await page.locator('[placeholder="Tip factură"]').click(); await page.locator('.p-dropdown-item').filter({ hasText: 'Furnizori' }).click(); await page.waitForTimeout(1000); // Wait for API call // Verify partner_type parameter was sent correctly expect(capturedPartnerType).toBe('FURNIZORI'); }); test('should filter by cont (account number)', async ({ page }) => { let capturedCont = null; // Intercept API requests to verify cont parameter await page.route('**/api/invoices**', async route => { const url = route.request().url(); const urlParams = new URL(url).searchParams; capturedCont = urlParams.get('cont'); await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ invoices: [], total_count: 0, filtered_count: 0, total_amount: 0, page: 1, page_size: 50, has_more: false }), }); }); await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Enter cont filter await page.locator('[placeholder="Filtru cont (ex: 4111)"]').fill('4111'); await page.waitForTimeout(1000); // Wait for debounced API call // Verify cont parameter was sent correctly expect(capturedCont).toBe('4111'); }); test('should use partner_name parameter for search', async ({ page }) => { let capturedPartnerName = null; let capturedSearchParam = null; // Intercept API requests to verify correct parameter name await page.route('**/api/invoices**', async route => { const url = route.request().url(); const urlParams = new URL(url).searchParams; capturedPartnerName = urlParams.get('partner_name'); capturedSearchParam = urlParams.get('search'); await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ invoices: [], total_count: 0, filtered_count: 0, total_amount: 0, page: 1, page_size: 50, has_more: false }), }); }); await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Search for partner name await page.locator('[placeholder="Căutați după număr, partener..."]').fill('Test Partner'); await page.waitForTimeout(1000); // Wait for debounced API call // Verify partner_name parameter was sent (not search) expect(capturedPartnerName).toBe('Test Partner'); expect(capturedSearchParam).toBeNull(); }); test('should export XLSX with all filters applied', async ({ page }) => { let exportRequestParams = null; // Intercept export API request await page.route('**/api/invoices**', async route => { const url = route.request().url(); const urlParams = new URL(url).searchParams; // Capture params if it's the export request (page_size = 999999) if (urlParams.get('page_size') === '999999') { exportRequestParams = { partner_type: urlParams.get('partner_type'), partner_name: urlParams.get('partner_name'), cont: urlParams.get('cont'), page_size: urlParams.get('page_size') }; } await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ invoices: [ { cont: '4111', nract: 'INV001', dataact: '2024-01-01', datascad: '2024-02-01', nume: 'Test Client', totctva: 1000, achitat: 500, soldfinal: 500 } ], total_count: 1, filtered_count: 1, total_amount: 1000, page: 1, page_size: 999999, has_more: false }), }); }); await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Apply filters before export await page.locator('[placeholder="Tip factură"]').click(); await page.locator('.p-dropdown-item').filter({ hasText: 'Furnizori' }).click(); await page.locator('[placeholder="Filtru cont (ex: 4111)"]').fill('4111'); await page.waitForTimeout(500); // Click Excel export const downloadPromise = page.waitForEvent('download', { timeout: 10000 }).catch(() => null); await page.locator('button:has-text("Export Excel")').click(); await page.waitForTimeout(2000); // Wait for export to complete // Verify export request included all filters expect(exportRequestParams).toBeTruthy(); expect(exportRequestParams.partner_type).toBe('FURNIZORI'); expect(exportRequestParams.cont).toBe('4111'); expect(exportRequestParams.page_size).toBe('999999'); // Download may or may not occur due to mock, but we verified the API call await downloadPromise; }); test('should have hover effect on table rows', async ({ page }) => { await invoicesPage.waitForPageLoad(); await invoicesPage.selectCompany('Compania Test 1'); await page.waitForSelector(invoicesPage.invoicesTable); // Wait for table rows to load const firstRow = page.locator('.p-datatable-tbody tr').first(); await firstRow.waitFor(); // Get initial background color const initialBgColor = await firstRow.evaluate(el => window.getComputedStyle(el).backgroundColor ); // Hover over the row await firstRow.hover(); await page.waitForTimeout(300); // Wait for transition // Get background color after hover const hoverBgColor = await firstRow.evaluate(el => window.getComputedStyle(el).backgroundColor ); // Background color should change on hover expect(hoverBgColor).not.toBe(initialBgColor); // Verify hover color is the expected blue (#e3f2fd = rgb(227, 242, 253)) expect(hoverBgColor).toBe('rgb(227, 242, 253)'); }); });