import { test, expect, Page } from '@playwright/test'; import path from 'path'; const TEST_DATA_DIR = path.join(__dirname, '..', 'test-data'); // Test credentials - FakeAuthService accepts any credentials in development mode const TEST_USERNAME = 'testuser'; const TEST_PASSWORD = 'testpass'; // Helper to login to the application async function login(page: Page) { // Check if we're on the login page const loginForm = page.locator('text=Authentication Required'); if (await loginForm.isVisible({ timeout: 5000 }).catch(() => false)) { // Wait for form inputs to be ready await page.locator('input[name="Username"]').waitFor({ state: 'visible', timeout: 10000 }); // Fill credentials await page.locator('input[name="Username"]').fill(TEST_USERNAME); await page.locator('input[name="Password"]').fill(TEST_PASSWORD); // Click the submit button in the form await page.locator('button[type="submit"]:has-text("LOGIN")').click(); // Wait for login to process await page.waitForTimeout(3000); // After login, force navigation to search page to refresh state await page.goto('/search'); await page.waitForLoadState('networkidle', { timeout: 60000 }); // If still on login page after navigation, login failed if (await loginForm.isVisible({ timeout: 2000 }).catch(() => false)) { throw new Error('Login failed - still on login page after attempt'); } } } // Helper to navigate to search page and wait for Blazor WASM to be ready async function navigateToSearchPage(page: Page) { // Navigate to the search page await page.goto('/search'); // Wait for page to load initially await page.waitForLoadState('networkidle', { timeout: 60000 }); // Check if we need to login await login(page); // Wait for the page to have meaningful content - either Radzen dropdown or Search text await page.locator('.rz-dropdown').or(page.locator('text=Search Details')).first().waitFor({ state: 'visible', timeout: 120000 }); // Additional wait for Radzen components to fully initialize await page.waitForTimeout(2000); } // Helper to select search type from dropdown async function selectSearchType(page: Page, searchType: string) { // Click the search type dropdown await page.locator('.rz-dropdown').first().click(); // Wait for dropdown to open and select the option await page.locator(`text="${searchType}"`).click(); // Wait for the filter panel to appear await page.waitForTimeout(500); } // Helper to upload file via RadzenUpload async function uploadFile(page: Page, filePath: string) { // RadzenUpload creates a hidden file input - find it and set the file const fileInput = page.locator('input[type="file"]'); await fileInput.setInputFiles(filePath); // Wait for upload to complete (notification appears or grid updates) await page.waitForTimeout(2000); } test.describe('Work Order Search', () => { test.beforeEach(async ({ page }) => { await navigateToSearchPage(page); }); test('TC-010-P01: Upload single work order and verify it appears in grid', async ({ page }) => { // Select Work Order search type await selectSearchType(page, 'Work Order'); // Verify Filter by Work Order panel is visible await expect(page.locator('text=Filter by Work Order')).toBeVisible(); // Enter search name await page.fill('input[placeholder=" "]', 'TC-010 Single Work Order Test'); // Upload the work order file const testFile = path.join(TEST_DATA_DIR, 'single_workorder.xlsx'); await uploadFile(page, testFile); // Verify work order appears in the grid // Note: The actual work order may not be found in the database if it doesn't exist // This test verifies the upload mechanism works const gridText = await page.locator('.rz-data-grid').textContent(); // Check that either the work order appears or "No records" if not in DB // The important thing is no error notification const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 5000 }); }); test('TC-010-P02: Upload multiple work orders', async ({ page }) => { await selectSearchType(page, 'Work Order'); const testFile = path.join(TEST_DATA_DIR, 'multiple_workorders.xlsx'); await uploadFile(page, testFile); // Verify no error const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 5000 }); }); test('TC-010-P03: Download template contains correct format', async ({ page }) => { await selectSearchType(page, 'Work Order'); // Click download template const downloadPromise = page.waitForEvent('download'); await page.click('text=Download Template'); const download = await downloadPromise; expect(download.suggestedFilename()).toContain('.xlsx'); }); test('TC-010-P04: Clear data removes all entries', async ({ page }) => { await selectSearchType(page, 'Work Order'); // First upload some data const testFile = path.join(TEST_DATA_DIR, 'single_workorder.xlsx'); await uploadFile(page, testFile); // Click Clear Data await page.click('text=Clear Data'); // Wait for and confirm the dialog (uses "OK" button, not "Yes") await page.waitForSelector('text=Confirm Clear', { timeout: 5000 }); await page.click('button:has-text("OK")'); await page.waitForTimeout(500); // Verify grid shows "No records to display" await expect(page.locator('text=No records to display')).toBeVisible(); }); }); test.describe('Component Lot Search', () => { test.beforeEach(async ({ page }) => { await navigateToSearchPage(page); }); test('TC-020-P01: Upload component lot file', async ({ page }) => { await selectSearchType(page, 'Component Lot'); await expect(page.locator('text=Filter By Component Lot')).toBeVisible(); const testFile = path.join(TEST_DATA_DIR, 'single_lot.xlsx'); await uploadFile(page, testFile); const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 5000 }); }); }); test.describe('Time Span + Profit Center + Item Number Search', () => { test.beforeEach(async ({ page }) => { await navigateToSearchPage(page); }); test('TC-060-P01: Upload item numbers file', async ({ page }) => { await selectSearchType(page, 'Time Span + Profit Center + Item Number'); await expect(page.locator('text=Filter by Item Number')).toBeVisible(); const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, testFile); const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 5000 }); }); }); test.describe('Time Span + Profit Center + Item/Operation/MIS Search', () => { test.beforeEach(async ({ page }) => { await navigateToSearchPage(page); }); test('TC-070-P01: Upload part operations file', async ({ page }) => { await selectSearchType(page, 'Time Span + Profit Center + Item/Operation/MIS'); await expect(page.locator('text=Filter By Item/Operation/MIS')).toBeVisible(); const testFile = path.join(TEST_DATA_DIR, 'single_operation.xlsx'); await uploadFile(page, testFile); const errorNotification = page.locator('.rz-notification-error'); await expect(errorNotification).not.toBeVisible({ timeout: 5000 }); }); });