/** * Playwright E2E tests for Search Type 80: * Time Span + Profit Center + Work Order + Item/Operation/MIS * * This is the most comprehensive search type, allowing users to find work orders * within a specified date range, filtered by profit center, specific work order * numbers, and part operations (Item Number, Operation Number, MIS Number, MIS Revision). * * Filters Enabled: * - Timespan (Min Date, Max Date) * - Profit Center (autocomplete) * - Work Order (file upload) * - Part Operations (file upload) */ import { test, expect } from '@playwright/test'; import { navigateToSearchPage } from '../helpers/navigation.helper'; import { selectSearchType, SearchTypes, enterSearchName, clickSubmitSearch, confirmSubmitSearch } from '../helpers/search-type.helper'; import { setDateRange, setMinDate, setMaxDate, clearMinDate, clearMaxDate, TestDateRanges } from '../helpers/date-picker.helper'; import { addProfitCenter, addProfitCenters, profitCenterConfig, getAutocompleteItemCount, isAutocompletePanelVisible, TestAutocompleteData } from '../helpers/autocomplete.helper'; import { uploadFile, workOrderConfig, partOperationConfig, getTestFile, TestFiles, getUploadedItemCount, isFileUploadPanelVisible } from '../helpers/file-upload.helper'; import { assertNoErrorNotification, hasErrorNotification } from '../helpers/radzen.helper'; import { hasValidationErrors, submitAndExpectError, ValidationMessages } from '../helpers/validation.helper'; // Test data constants for Type 80 const TYPE_80_NAME = SearchTypes.TIMESPAN_PC_WO_PARTOP; // Valid profit centers from test documentation const VALID_PROFIT_CENTERS = ['1AM', '1BM', '1CM', '1PM', '2DM', '2SM', '3TM', '4IM', '5SM']; // Standard date ranges for testing const STANDARD_DATE_RANGE = { min: '2018-01-01', max: '2020-09-01' }; const RECENT_DATE_RANGE = TestDateRanges.RECENT; const HISTORICAL_DATE_RANGE = TestDateRanges.HISTORICAL; const SAME_DAY_DATE_RANGE = { min: '2019-06-15', max: '2019-06-15' }; const INVALID_DATE_RANGE = TestDateRanges.INVALID_REVERSED; test.describe('Search Type 80: Time Span + Profit Center + Work Order + Item/Operation/MIS', () => { test.beforeEach(async ({ page }) => { await navigateToSearchPage(page); }); test.describe('Positive Test Cases', () => { test('TC-080-P01: Single value for all filters', async ({ page }) => { // Step 1: Enter search name await enterSearchName(page, 'TC-080-P01 Single Values Test'); // Step 2: Select search type await selectSearchType(page, TYPE_80_NAME); // Verify all filter panels are visible expect(await isAutocompletePanelVisible(page, profitCenterConfig)).toBe(true); expect(await isFileUploadPanelVisible(page, workOrderConfig)).toBe(true); expect(await isFileUploadPanelVisible(page, partOperationConfig)).toBe(true); // Step 3: Set date range await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); // Step 4: Add single profit center await addProfitCenter(page, '1AM'); expect(await getAutocompleteItemCount(page, profitCenterConfig)).toBe(1); // Step 5: Upload single work order file await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); // Step 6: Upload single part operation file await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); // Step 7: Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); // Verify no error notification await assertNoErrorNotification(page); }); test('TC-080-P02: Multiple profit centers with single work order and part operation', async ({ page }) => { await enterSearchName(page, 'TC-080-P02 Multiple PC Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); // Add multiple profit centers await addProfitCenters(page, ['1AM', '1BM', '1CM']); expect(await getAutocompleteItemCount(page, profitCenterConfig)).toBe(3); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-080-P03: Single profit center with multiple work orders', async ({ page }) => { await enterSearchName(page, 'TC-080-P03 Multiple WO Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1PM'); // Upload multiple work orders await uploadFile(page, workOrderConfig, getTestFile(TestFiles.MULTIPLE_WORKORDERS)); const woCount = await getUploadedItemCount(page, workOrderConfig); expect(woCount).toBeGreaterThan(1); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-080-P04: Single profit center and work order with multiple part operations', async ({ page }) => { await enterSearchName(page, 'TC-080-P04 Multiple Part Ops Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '2DM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); // Upload multiple part operations await uploadFile(page, partOperationConfig, getTestFile(TestFiles.MULTIPLE_OPERATIONS)); const opCount = await getUploadedItemCount(page, partOperationConfig); expect(opCount).toBeGreaterThan(1); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-080-P05: Multiple values for all filter types', async ({ page }) => { await enterSearchName(page, 'TC-080-P05 All Multiple Values Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); // Multiple profit centers await addProfitCenters(page, ['1PM', '2SM', '3TM']); expect(await getAutocompleteItemCount(page, profitCenterConfig)).toBe(3); // Multiple work orders await uploadFile(page, workOrderConfig, getTestFile(TestFiles.MULTIPLE_WORKORDERS)); // Multiple part operations await uploadFile(page, partOperationConfig, getTestFile(TestFiles.MULTIPLE_OPERATIONS)); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-080-P06: Recent date range', async ({ page }) => { await enterSearchName(page, 'TC-080-P06 Recent Range Test'); await selectSearchType(page, TYPE_80_NAME); // Use recent date range await setDateRange(page, RECENT_DATE_RANGE.min, RECENT_DATE_RANGE.max); await addProfitCenter(page, '4IM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-080-P07: Historical date range', async ({ page }) => { await enterSearchName(page, 'TC-080-P07 Historical Range Test'); await selectSearchType(page, TYPE_80_NAME); // Use historical date range await setDateRange(page, HISTORICAL_DATE_RANGE.min, HISTORICAL_DATE_RANGE.max); await addProfitCenter(page, '5SM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-080-P08: Same day date range', async ({ page }) => { await enterSearchName(page, 'TC-080-P08 Same Day Test'); await selectSearchType(page, TYPE_80_NAME); // Use same day for min and max await setDateRange(page, SAME_DAY_DATE_RANGE.min, SAME_DAY_DATE_RANGE.max); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-080-P09: All profit centers', async ({ page }) => { await enterSearchName(page, 'TC-080-P09 All Profit Centers Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); // Add all valid profit centers await addProfitCenters(page, VALID_PROFIT_CENTERS); expect(await getAutocompleteItemCount(page, profitCenterConfig)).toBe(VALID_PROFIT_CENTERS.length); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-080-P10: Maximum work orders', async ({ page }) => { await enterSearchName(page, 'TC-080-P10 Many Work Orders Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1AM'); // Upload maximum work orders file await uploadFile(page, workOrderConfig, getTestFile(TestFiles.MULTIPLE_WORKORDERS)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); }); test.describe('Negative Test Cases', () => { test('TC-080-N01: Missing search name', async ({ page }) => { // Do NOT enter search name await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await submitAndExpectError(page); }); test('TC-080-N02: Missing profit center', async ({ page }) => { await enterSearchName(page, 'TC-080-N02 Missing PC Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); // Do NOT add profit center await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await submitAndExpectError(page); }); test('TC-080-N03: Missing work order', async ({ page }) => { await enterSearchName(page, 'TC-080-N03 Missing WO Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1AM'); // Do NOT upload work order await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await submitAndExpectError(page); }); test('TC-080-N04: Missing part operation', async ({ page }) => { await enterSearchName(page, 'TC-080-N04 Missing Part Op Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); // Do NOT upload part operation await submitAndExpectError(page); }); test('TC-080-N05: Missing minimum date', async ({ page }) => { await enterSearchName(page, 'TC-080-N05 Missing Min Date Test'); await selectSearchType(page, TYPE_80_NAME); // Only set max date, leave min date empty await setMaxDate(page, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await submitAndExpectError(page); }); test('TC-080-N06: Missing maximum date', async ({ page }) => { await enterSearchName(page, 'TC-080-N06 Missing Max Date Test'); await selectSearchType(page, TYPE_80_NAME); // Only set min date, leave max date empty await setMinDate(page, STANDARD_DATE_RANGE.min); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await submitAndExpectError(page); }); test('TC-080-N07: Invalid date range (min > max)', async ({ page }) => { await enterSearchName(page, 'TC-080-N07 Invalid Date Range Test'); await selectSearchType(page, TYPE_80_NAME); // Set min date after max date await setDateRange(page, INVALID_DATE_RANGE.min, INVALID_DATE_RANGE.max); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await submitAndExpectError(page); }); test('TC-080-N08: Missing both dates', async ({ page }) => { await enterSearchName(page, 'TC-080-N08 Missing Both Dates Test'); await selectSearchType(page, TYPE_80_NAME); // Do not set any dates await clearMinDate(page); await clearMaxDate(page); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await submitAndExpectError(page); }); test('TC-080-N09: Whitespace-only search name', async ({ page }) => { // Enter whitespace-only search name await enterSearchName(page, ' '); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); await submitAndExpectError(page); }); test('TC-080-N10: Missing all required filters', async ({ page }) => { await enterSearchName(page, 'TC-080-N10 No Filters Test'); await selectSearchType(page, TYPE_80_NAME); // Do not set any filters - leave dates, profit center, work order, and part operation empty await clearMinDate(page); await clearMaxDate(page); await submitAndExpectError(page); }); test('TC-080-N11: Empty work order file', async ({ page }) => { await enterSearchName(page, 'TC-080-N11 Empty WO File Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1AM'); // Upload empty file await uploadFile(page, workOrderConfig, getTestFile(TestFiles.EMPTY_FILE)); await uploadFile(page, partOperationConfig, getTestFile(TestFiles.SINGLE_OPERATION)); // Should either show error notification or validation error const hasError = await hasErrorNotification(page) || await hasValidationErrors(page); // If upload completes without immediate error, submit should fail if (!hasError) { await submitAndExpectError(page); } }); test('TC-080-N12: Empty part operation file', async ({ page }) => { await enterSearchName(page, 'TC-080-N12 Empty Part Op File Test'); await selectSearchType(page, TYPE_80_NAME); await setDateRange(page, STANDARD_DATE_RANGE.min, STANDARD_DATE_RANGE.max); await addProfitCenter(page, '1AM'); await uploadFile(page, workOrderConfig, getTestFile(TestFiles.SINGLE_WORKORDER)); // Upload empty file for part operations await uploadFile(page, partOperationConfig, getTestFile(TestFiles.EMPTY_FILE)); // Should either show error notification or validation error const hasError = await hasErrorNotification(page) || await hasValidationErrors(page); // If upload completes without immediate error, submit should fail if (!hasError) { await submitAndExpectError(page); } }); }); test.describe('Filter Panel Visibility', () => { test('TC-080-V01: All filter panels visible when search type selected', async ({ page }) => { await selectSearchType(page, TYPE_80_NAME); // Verify all four filter panels are visible await expect(page.locator('text=Filter by Time Span')).toBeVisible(); await expect(page.locator('text=Filter by Profit Center')).toBeVisible(); await expect(page.locator('text=Filter by Work Order')).toBeVisible(); await expect(page.locator('text=Filter By Item/Operation/MIS')).toBeVisible(); }); test('TC-080-V02: Date inputs are available in time span panel', async ({ page }) => { await selectSearchType(page, TYPE_80_NAME); // Verify date input fields exist const minDateInput = page.locator('input[name="MinimumDt"]'); const maxDateInput = page.locator('input[name="MaximumDt"]'); await expect(minDateInput).toBeVisible(); await expect(maxDateInput).toBeVisible(); }); test('TC-080-V03: Autocomplete available for profit center', async ({ page }) => { await selectSearchType(page, TYPE_80_NAME); // Verify autocomplete component exists in profit center panel const panel = page.locator(`.rz-card:has-text("${profitCenterConfig.panelHeader}")`); const autocomplete = panel.locator('.rz-autocomplete'); await expect(autocomplete).toBeVisible(); }); test('TC-080-V04: File upload available for work order', async ({ page }) => { await selectSearchType(page, TYPE_80_NAME); // Verify file input exists in work order panel const panel = page.locator(`.rz-card:has-text("${workOrderConfig.panelHeader}")`); const fileInput = panel.locator('input[type="file"]'); await expect(fileInput).toBeAttached(); }); test('TC-080-V05: File upload available for part operation', async ({ page }) => { await selectSearchType(page, TYPE_80_NAME); // Verify file input exists in part operation panel const panel = page.locator(`.rz-card:has-text("${partOperationConfig.panelHeader}")`); const fileInput = panel.locator('input[type="file"]'); await expect(fileInput).toBeAttached(); }); }); test.describe('Template Downloads', () => { test('TC-080-T01: Download work order template', async ({ page }) => { await selectSearchType(page, TYPE_80_NAME); const panel = page.locator(`.rz-card:has-text("${workOrderConfig.panelHeader}")`); const downloadPromise = page.waitForEvent('download'); await panel.locator('button:has-text("Download Template")').click(); const download = await downloadPromise; expect(download.suggestedFilename()).toContain('.xlsx'); }); test('TC-080-T02: Download part operation template', async ({ page }) => { await selectSearchType(page, TYPE_80_NAME); const panel = page.locator(`.rz-card:has-text("${partOperationConfig.panelHeader}")`); const downloadPromise = page.waitForEvent('download'); await panel.locator('button:has-text("Download Template")').click(); const download = await downloadPromise; expect(download.suggestedFilename()).toContain('.xlsx'); }); }); });