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, clearDateRange, TestDateRanges } from '../helpers/date-picker.helper'; import { uploadFile, itemNumberConfig, getTestFile, TestFiles, getUploadedItemCount, clearUploadedData, isFileUploadPanelVisible } from '../helpers/file-upload.helper'; import { assertNoErrorNotification, hasSuccessNotification } from '../helpers/radzen.helper'; import { hasValidationErrors, submitAndExpectError, ValidationMessages } from '../helpers/validation.helper'; import path from 'path'; /** * Test suite for Search Type 140: Time Span + Item Number * * This search type allows users to search for work order data within * a specific date range, filtered by one or more item numbers. * * Filters Enabled: Timespan (Start Date, End Date), Item Number * * NOTE: Item numbers are uploaded via file (Excel), not autocomplete. * Valid item numbers are 11-character codes (e.g., 00003300100). */ test.describe('Search Type 140: Time Span + Item Number', () => { test.beforeEach(async ({ page }) => { await navigateToSearchPage(page); }); // Valid item number test data const validItemNumbers = [ '00003200700', '00003200800', '00003200900', '00003201500', '00003205000', '00003205100', '00003205200', '00003208800', '00003208900', '00003209000', '00003300100', '00003300200', '00003300300', '00003304900', '00003305000', ]; const TEST_DATA_DIR = path.join(__dirname, '..', 'test-data'); // ============================================================================ // POSITIVE TEST CASES // ============================================================================ test.describe('Positive Tests', () => { test('TC-140-P01: Single item number', async ({ page }) => { // Select search type await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); // Verify filter panels are visible await expect(page.locator('text=Filter by Item Number')).toBeVisible(); await expect(page.locator('text=Filter by Time Span')).toBeVisible(); // Enter search name await enterSearchName(page, 'TC-140-P01 Single Item'); // Set date range await setDateRange(page, '2019-01-01', '2020-09-01'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Verify item number appears in the list const itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBeGreaterThanOrEqual(1); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); // Verify no error notification await assertNoErrorNotification(page); }); test('TC-140-P02: Multiple item numbers', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P02 Multiple Items'); // Set date range await setDateRange(page, '2018-01-01', '2020-09-01'); // Upload multiple items file const testFile = path.join(TEST_DATA_DIR, 'multiple_items.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Verify items appear in the list const itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBeGreaterThanOrEqual(1); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P03: Many item numbers', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P03 Many Items'); // Set date range await setDateRange(page, '2018-01-01', '2019-12-31'); // Upload multiple items file const testFile = path.join(TEST_DATA_DIR, 'multiple_items.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Verify items appear const itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBeGreaterThanOrEqual(1); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P04: Recent date range', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P04 Recent Range'); // Set recent date range await setDateRange(page, TestDateRanges.RECENT.min, TestDateRanges.RECENT.max); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P05: Historical date range', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P05 Historical Range'); // Set historical date range await setDateRange(page, TestDateRanges.HISTORICAL.min, TestDateRanges.HISTORICAL.max); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P06: Narrow date range (single month)', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P06 Single Month'); // Set narrow date range (single month) await setDateRange(page, '2020-06-01', '2020-06-30'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P07: Same start and end date', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P07 Same Day'); // Set same day date range await setDateRange(page, '2020-05-15', '2020-05-15'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P08: Items from different series', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P08 Different Series'); // Set date range await setDateRange(page, '2018-01-01', '2020-09-01'); // Upload multiple items file (contains items from different series) const testFile = path.join(TEST_DATA_DIR, 'multiple_items.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Verify items appear const itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBeGreaterThanOrEqual(1); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P09: Boundary date - start of data range', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P09 Start Boundary'); // Set earliest date boundary await setDateRange(page, TestDateRanges.START_BOUNDARY.min, TestDateRanges.START_BOUNDARY.max); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P10: Boundary date - end of data range', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P10 End Boundary'); // Set latest date boundary await setDateRange(page, TestDateRanges.END_BOUNDARY.min, TestDateRanges.END_BOUNDARY.max); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Submit search await clickSubmitSearch(page); await confirmSubmitSearch(page); await assertNoErrorNotification(page); }); test('TC-140-P11: Download template', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); // Verify filter panel is visible await expect(page.locator('text=Filter by Item Number')).toBeVisible(); // Click download template const panel = page.locator(`.rz-card:has-text("${itemNumberConfig.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-140-P12: Clear data removes all entries', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-P12 Clear Data'); // Set date range await setDateRange(page, '2019-01-01', '2020-09-01'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Verify items uploaded let itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBeGreaterThanOrEqual(1); // Clear data await clearUploadedData(page, itemNumberConfig); // Verify list is empty itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBe(0); }); }); // ============================================================================ // NEGATIVE TEST CASES // ============================================================================ test.describe('Negative Tests', () => { test('TC-140-N01: Missing date range', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N01 No Dates'); // Do NOT set any dates // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N02: Missing start date only', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N02 No Start Date'); // Only set maximum date await setMaxDate(page, '2020-09-01'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N03: Missing end date only', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N03 No End Date'); // Only set minimum date await setMinDate(page, '2019-01-01'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N04: Missing item number', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N04 No Item'); // Set valid date range await setDateRange(page, '2019-01-01', '2020-09-01'); // Do NOT upload any items // Verify item list is empty const itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBe(0); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N05: Start date after end date', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N05 Invalid Date Range'); // Set invalid date range (start after end) await setDateRange(page, '2020-09-01', '2019-01-01'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N06: Empty item number value', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N06 Empty Item'); // Set valid date range await setDateRange(page, '2019-01-01', '2020-09-01'); // Try to upload empty file const testFile = path.join(TEST_DATA_DIR, 'empty_file.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Wait for upload processing await page.waitForTimeout(1000); // Verify item list is empty const itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBe(0); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N07: Missing search name', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); // Do NOT enter search name // Set valid date range await setDateRange(page, '2019-01-01', '2020-09-01'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Attempt to submit await submitAndExpectError(page); // Verify user remains on the page await expect(page.locator('text=Filter by Item Number')).toBeVisible(); }); test('TC-140-N08: Whitespace-only item number', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N08 Whitespace Item'); // Set valid date range await setDateRange(page, '2019-01-01', '2020-09-01'); // Note: For file uploads, whitespace-only entries would be in the file // This test verifies that empty/invalid uploads don't create valid entries // Don't upload any file - verify empty state const itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBe(0); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N09: No search type selected', async ({ page }) => { // Enter search name without selecting search type await enterSearchName(page, 'TC-140-N09 No Type'); // Verify filter panels are not visible const itemPanelVisible = await isFileUploadPanelVisible(page, itemNumberConfig); expect(itemPanelVisible).toBe(false); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N10: Invalid date format', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N10 Invalid Date Format'); // Try to enter invalid date format const minDateInput = page.locator('input[name="MinimumDt"]'); await minDateInput.fill('31-12-2019'); await setMaxDate(page, '2020-09-01'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N11: Future date range', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N11 Future Dates'); // Set future date range await setDateRange(page, TestDateRanges.FUTURE.min, TestDateRanges.FUTURE.max); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Submit search - may be accepted but will return no results await clickSubmitSearch(page); // Check if there's a validation error or if it proceeds const hasErrors = await hasValidationErrors(page); if (!hasErrors) { await confirmSubmitSearch(page); await assertNoErrorNotification(page); } }); test('TC-140-N12: Invalid file format', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N12 Invalid File Format'); // Set valid date range await setDateRange(page, '2019-01-01', '2020-09-01'); // Try to upload invalid format file const testFile = path.join(TEST_DATA_DIR, 'invalid_format.txt'); await uploadFile(page, itemNumberConfig, testFile); // Wait for upload processing await page.waitForTimeout(1000); // Either an error notification should appear or items should not be uploaded const itemCount = await getUploadedItemCount(page, itemNumberConfig); expect(itemCount).toBe(0); }); test('TC-140-N13: Whitespace-only search name', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); // Enter whitespace-only search name await enterSearchName(page, ' '); // Set valid date range await setDateRange(page, '2019-01-01', '2020-09-01'); // Upload item number file const testFile = path.join(TEST_DATA_DIR, 'single_item.xlsx'); await uploadFile(page, itemNumberConfig, testFile); // Attempt to submit await submitAndExpectError(page); }); test('TC-140-N14: Missing all required filters', async ({ page }) => { await selectSearchType(page, SearchTypes.TIMESPAN_ITEM); await enterSearchName(page, 'TC-140-N14 No Filters'); // Do NOT set any dates // Do NOT upload any items // Attempt to submit await submitAndExpectError(page); }); }); });