import { test, expect } from '@playwright/test'; import { login, logout, isLoggedIn } from '../helpers/auth.helper'; import { navigateToDataSync, clickNavLink } from '../helpers/navigation.helper'; import { getDataGridRowCount, getBadgeStyle, clickButton, StatusBadgeStyles } from '../helpers/radzen.helper'; test.describe('Data Sync Requests Page', () => { test.beforeEach(async ({ page }) => { await navigateToDataSync(page); }); test.describe('Page Load and Display', () => { test('page loads and displays Data Sync Requests heading', async ({ page }) => { // Verify page title await expect(page).toHaveTitle(/Data Sync Requests - JDE Scoping Tool/); // Verify heading is visible await expect(page.locator('h4:has-text("Data Sync Requests")')).toBeVisible(); }); test('page shows filter card', async ({ page }) => { // Verify filter card is visible const filterCard = page.locator('.rz-card'); await expect(filterCard.first()).toBeVisible(); }); test('page shows New Request button', async ({ page }) => { const newRequestButton = page.locator('button:has-text("New Request")'); await expect(newRequestButton).toBeVisible(); }); test('page shows Reload Pipelines button', async ({ page }) => { const reloadButton = page.locator('button:has-text("Reload Pipelines")'); await expect(reloadButton).toBeVisible(); }); test('no error notification on page load', async ({ page }) => { const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 5000 }); }); }); test.describe('Filter Controls', () => { test('show pending only checkbox is visible', async ({ page }) => { const checkbox = page.locator('.rz-chkbox'); await expect(checkbox).toBeVisible(); }); test('show pending only label is visible', async ({ page }) => { await expect(page.locator('text=Show pending only')).toBeVisible(); }); test('refresh button is visible', async ({ page }) => { const refreshButton = page.locator('button:has-text("Refresh")'); await expect(refreshButton).toBeVisible(); }); test('clicking refresh button reloads data', async ({ page }) => { // Wait for initial load await page.waitForTimeout(2000); // Click refresh button const refreshButton = page.locator('button:has-text("Refresh")'); await refreshButton.click(); // Wait for reload await page.waitForTimeout(2000); // Verify no error const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 5000 }); }); test('filter checkbox toggles pending only filter', async ({ page }) => { // Wait for initial load await page.waitForTimeout(2000); // Find and click the checkbox const checkbox = page.locator('.rz-chkbox').first(); await checkbox.click(); // Wait for filter to apply await page.waitForTimeout(1000); // Verify no error occurred const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 5000 }); }); }); test.describe('Data Grid Display', () => { test('data grid or empty state message is visible', async ({ page }) => { // Wait for loading to complete await page.waitForTimeout(2000); // Either data grid is visible or info message about no requests const dataGrid = page.locator('.rz-data-grid'); const noRequestsMessage = page.locator('text=No sync requests found'); const gridVisible = await dataGrid.isVisible({ timeout: 3000 }).catch(() => false); const messageVisible = await noRequestsMessage.isVisible({ timeout: 3000 }).catch(() => false); expect(gridVisible || messageVisible).toBe(true); }); }); test.describe('Data Grid Columns', () => { test('data grid shows Pipeline column when requests exist', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { const pipelineHeader = page.locator('.rz-data-grid th:has-text("Pipeline")'); await expect(pipelineHeader).toBeVisible(); } }); test('data grid shows Type column when requests exist', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { const typeHeader = page.locator('.rz-data-grid th:has-text("Type")'); await expect(typeHeader).toBeVisible(); } }); test('data grid shows Requested column when requests exist', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { const requestedHeader = page.locator('.rz-data-grid th:has-text("Requested")'); await expect(requestedHeader).toBeVisible(); } }); test('data grid shows By column when requests exist', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { const byHeader = page.locator('.rz-data-grid th:has-text("By")'); await expect(byHeader).toBeVisible(); } }); test('data grid shows Status column when requests exist', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { const statusHeader = page.locator('.rz-data-grid th:has-text("Status")'); await expect(statusHeader).toBeVisible(); } }); test('data grid shows Actions column when requests exist', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { const actionsHeader = page.locator('.rz-data-grid th:has-text("Actions")'); await expect(actionsHeader).toBeVisible(); } }); test('all expected columns are present when requests exist', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { const headers = page.locator('.rz-data-grid th'); const headerTexts = await headers.allTextContents(); // Verify all expected columns expect(headerTexts.some(h => h.includes('Pipeline'))).toBe(true); expect(headerTexts.some(h => h.includes('Type'))).toBe(true); expect(headerTexts.some(h => h.includes('Requested'))).toBe(true); expect(headerTexts.some(h => h.includes('By'))).toBe(true); expect(headerTexts.some(h => h.includes('Status'))).toBe(true); expect(headerTexts.some(h => h.includes('Actions'))).toBe(true); } }); }); test.describe('Status Badges', () => { test('Pending status displays with appropriate badge style', async ({ page }) => { await page.waitForTimeout(2000); const pendingBadge = page.locator('.rz-data-grid .rz-badge:has-text("Pending")').first(); if (await pendingBadge.isVisible({ timeout: 3000 }).catch(() => false)) { const style = await getBadgeStyle(pendingBadge); // Pending typically uses warning style expect(style).toBeTruthy(); } }); test('Completed status displays with success badge style', async ({ page }) => { await page.waitForTimeout(2000); const completedBadge = page.locator('.rz-data-grid .rz-badge:has-text("Completed")').first(); if (await completedBadge.isVisible({ timeout: 3000 }).catch(() => false)) { const style = await getBadgeStyle(completedBadge); expect(style).toBe('success'); } }); test('Failed status displays with danger badge style', async ({ page }) => { await page.waitForTimeout(2000); const failedBadge = page.locator('.rz-data-grid .rz-badge:has-text("Failed")').first(); if (await failedBadge.isVisible({ timeout: 3000 }).catch(() => false)) { const style = await getBadgeStyle(failedBadge); expect(style).toBe('danger'); } }); }); test.describe('Action Buttons', () => { test('Cancel button appears only for Pending requests', async ({ page }) => { await page.waitForTimeout(2000); const rowCount = await getDataGridRowCount(page); if (rowCount > 0) { // Get all rows const rows = page.locator('.rz-data-grid tbody tr'); const rowCountNum = await rows.count(); for (let i = 0; i < rowCountNum; i++) { const row = rows.nth(i); const statusBadge = row.locator('.rz-badge'); const statusText = await statusBadge.textContent(); const cancelButton = row.locator('button:has-text("Cancel")'); if (statusText?.trim() === 'Pending') { // Pending rows should have Cancel button await expect(cancelButton).toBeVisible(); } else { // Non-pending rows should not have Cancel button await expect(cancelButton).not.toBeVisible(); } } } }); test('New Request button opens dialog', async ({ page }) => { // Click New Request button const newRequestButton = page.locator('button:has-text("New Request")'); await newRequestButton.click(); // Wait for dialog to appear await page.waitForTimeout(1000); // Should see a dialog (Radzen dialog) const dialog = page.locator('.rz-dialog'); const dialogVisible = await dialog.isVisible({ timeout: 5000 }).catch(() => false); // If dialog opened, verify it's there; if not, verify no error if (!dialogVisible) { const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 2000 }); } }); }); test.describe('Data Grid Features', () => { test('data grid supports sorting when requests exist', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { // Click on Pipeline column header to sort const pipelineHeader = page.locator('.rz-data-grid th:has-text("Pipeline")'); await pipelineHeader.click(); await page.waitForTimeout(500); // Verify no error occurred const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 2000 }); } }); test('data grid supports pagination with 20 items per page', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { // Look for pager component const pager = page.locator('.rz-pager'); await expect(pager).toBeVisible({ timeout: 5000 }); } }); test('data grid supports column resize', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { // Verify no error occurred const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 2000 }); } }); }); test.describe('Date Formatting', () => { test('requested date is formatted correctly (MM/dd hh:mm)', async ({ page }) => { await page.waitForTimeout(2000); const dataGrid = page.locator('.rz-data-grid'); if (await dataGrid.isVisible({ timeout: 3000 }).catch(() => false)) { const rowCount = await getDataGridRowCount(page); if (rowCount > 0) { // Get text from Requested column (index 2) const requestedCell = page.locator('.rz-data-grid tbody tr').first().locator('td').nth(2); const dateText = await requestedCell.textContent(); if (dateText && dateText.trim()) { // Verify date format matches MM/dd hh:mm pattern const datePattern = /^\d{2}\/\d{2} \d{2}:\d{2}$/; expect(dateText.trim()).toMatch(datePattern); } } } }); }); test.describe('Empty State', () => { test('displays info message when no sync requests found', async ({ page }) => { await page.waitForTimeout(2000); const rowCount = await getDataGridRowCount(page); if (rowCount === 0) { // Should show info message const noRequestsMessage = page.locator('text=No sync requests found'); await expect(noRequestsMessage).toBeVisible(); } }); test('empty state message suggests creating new request', async ({ page }) => { await page.waitForTimeout(2000); const rowCount = await getDataGridRowCount(page); if (rowCount === 0) { // Message should mention "New Request" const helpMessage = page.locator('text=Click "New Request" to create one'); await expect(helpMessage).toBeVisible(); } }); }); test.describe('Loading State', () => { test('shows loading indicator while fetching data', async ({ page }) => { // Navigate fresh to catch loading state await page.goto('/data-sync/requests'); await page.waitForLoadState('networkidle', { timeout: 60000 }); // If there's a loading indicator, it should eventually disappear const loadingIndicator = page.locator('text=Loading requests'); // Wait for page to settle await page.waitForTimeout(3000); // After waiting, loading should be gone await expect(loadingIndicator).not.toBeVisible({ timeout: 10000 }); }); }); test.describe('Header Buttons', () => { test('Reload Pipelines button has correct tooltip', async ({ page }) => { const reloadButton = page.locator('button:has-text("Reload Pipelines")'); const title = await reloadButton.getAttribute('title'); expect(title).toContain('Admin only'); }); test('New Request button has add icon', async ({ page }) => { const newRequestButton = page.locator('button:has-text("New Request")'); const icon = newRequestButton.locator('.rz-button-icon-left, .material-icons'); // The button should have an icon await expect(newRequestButton).toBeVisible(); }); test('Reload Pipelines button has sync icon', async ({ page }) => { const reloadButton = page.locator('button:has-text("Reload Pipelines")'); // The button should be visible await expect(reloadButton).toBeVisible(); }); }); test.describe('Filter Behavior', () => { test('toggling pending only filter changes displayed data', async ({ page }) => { await page.waitForTimeout(2000); // Get initial row count const initialRowCount = await getDataGridRowCount(page); // Toggle the checkbox const checkbox = page.locator('.rz-chkbox').first(); await checkbox.click(); // Wait for filter to apply await page.waitForTimeout(1000); // Get new row count const newRowCount = await getDataGridRowCount(page); // Counts may or may not be different depending on data // Just verify no error occurred const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 2000 }); }); }); });