Files
jdescopingtool/TestScripts/playwright/tests/timespan-profit-center.spec.ts
T
Joseph Doherty ee044d03e0 feat: add health check endpoint, file upload result handling, and Playwright E2E tests
- Add /health endpoint with anonymous access for monitoring
- Add FileUploadResult<T> model and PostMultipartForFileResultAsync for proper upload response handling
- Add ApiResult.Success() factory method for interface types
- Refactor Login.razor for cleaner code
- Add comprehensive Playwright E2E test suite with fixtures and helpers
2026-01-30 07:12:20 -05:00

411 lines
15 KiB
TypeScript

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 {
addProfitCenter,
addProfitCenters,
clearAutocompleteItems,
profitCenterConfig,
getAutocompleteItemCount,
removeAutocompleteItem,
isAutocompletePanelVisible,
} from '../helpers/autocomplete.helper';
import { assertNoErrorNotification, hasSuccessNotification } from '../helpers/radzen.helper';
import { hasValidationErrors, submitAndExpectError, ValidationMessages } from '../helpers/validation.helper';
/**
* Test suite for Search Type 30: Time Span + Profit Center
*
* This search type allows users to search by a date range combined with
* one or more profit center (branch) codes.
*
* Filters Enabled: Timespan, Profit Center
*/
test.describe('Search Type 30: Time Span + Profit Center', () => {
test.beforeEach(async ({ page }) => {
await navigateToSearchPage(page);
});
// ============================================================================
// POSITIVE TEST CASES
// ============================================================================
test.describe('Positive Tests', () => {
test('TC-030-P01: Single profit center with standard date range', async ({ page }) => {
// Select search type
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
// Verify filter panel is visible
await expect(page.locator('text=Filter by Profit Center')).toBeVisible();
await expect(page.locator('text=Filter by Time Span')).toBeVisible();
// Enter search name
await enterSearchName(page, 'TC-030-P01 Single Profit Center');
// Set date range
await setDateRange(page, '2020-01-01', '2020-09-01');
// Add profit center
await addProfitCenter(page, '1AM');
// Verify profit center appears in the list
const itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(1);
// Submit search
await clickSubmitSearch(page);
await confirmSubmitSearch(page);
// Verify no error notification
await assertNoErrorNotification(page);
});
test('TC-030-P02: Multiple profit centers search', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-P02 Multiple Profit Centers');
// Set date range
await setDateRange(page, '2019-01-01', '2019-12-31');
// Add multiple profit centers
await addProfitCenters(page, ['1AM', '1PM', '2DM']);
// Verify all profit centers appear in the list
const itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(3);
// Submit search
await clickSubmitSearch(page);
await confirmSubmitSearch(page);
await assertNoErrorNotification(page);
});
test('TC-030-P03: All profit centers search', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-P03 All Profit Centers');
// Set date range
await setDateRange(page, '2018-01-01', '2018-12-31');
// Add all profit centers
const allProfitCenters = ['1AM', '1BM', '1CM', '1PM', '2DM', '2SM', '3TM', '4IM', '5SM'];
await addProfitCenters(page, allProfitCenters);
// Verify all profit centers appear in the list
const itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(9);
// Submit search
await clickSubmitSearch(page);
await confirmSubmitSearch(page);
await assertNoErrorNotification(page);
});
test('TC-030-P04: Minimum date range (same day)', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-P04 Same Day Range');
// Set same day date range
await setDateRange(page, TestDateRanges.SAME_DAY.min, TestDateRanges.SAME_DAY.max);
// Add profit center
await addProfitCenter(page, '1PM');
// Submit search
await clickSubmitSearch(page);
await confirmSubmitSearch(page);
await assertNoErrorNotification(page);
});
test('TC-030-P05: Boundary date - start of data range', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-P05 Start Boundary');
// Set earliest date boundary (1905-01-20 to 1905-12-31)
await setDateRange(page, TestDateRanges.START_BOUNDARY.min, TestDateRanges.START_BOUNDARY.max);
// Add profit center
await addProfitCenter(page, '1AM');
// Submit search
await clickSubmitSearch(page);
await confirmSubmitSearch(page);
await assertNoErrorNotification(page);
});
test('TC-030-P06: Boundary date - end of data range', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-P06 End Boundary');
// Set latest date boundary (2020-08-01 to 2020-09-01)
await setDateRange(page, TestDateRanges.END_BOUNDARY.min, TestDateRanges.END_BOUNDARY.max);
// Add profit center
await addProfitCenter(page, '1CM');
// Submit search
await clickSubmitSearch(page);
await confirmSubmitSearch(page);
await assertNoErrorNotification(page);
});
test('TC-030-P07: Historical date range search', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-P07 Historical Search');
// Set historical date range (2016-01-01 to 2017-12-31)
await setDateRange(page, TestDateRanges.HISTORICAL.min, TestDateRanges.HISTORICAL.max);
// Add profit center
await addProfitCenter(page, '2SM');
// Submit search
await clickSubmitSearch(page);
await confirmSubmitSearch(page);
await assertNoErrorNotification(page);
});
test('TC-030-P08: Profit center remove and re-add', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-P08 PC Remove Re-add');
// Set date range
await setDateRange(page, '2019-01-01', '2019-12-31');
// Add profit centers
await addProfitCenter(page, '1AM');
await addProfitCenter(page, '1BM');
// Verify both are added
let itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(2);
// Remove first profit center (1AM)
await removeAutocompleteItem(page, profitCenterConfig, 0);
// Verify only one remains
itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(1);
// Add another profit center
await addProfitCenter(page, '1CM');
// Verify two profit centers in list (1BM and 1CM)
itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(2);
// Submit search
await clickSubmitSearch(page);
await confirmSubmitSearch(page);
await assertNoErrorNotification(page);
});
});
// ============================================================================
// NEGATIVE TEST CASES
// ============================================================================
test.describe('Negative Tests', () => {
test('TC-030-N01: Missing search name', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
// Do NOT enter search name
// Set valid date range
await setDateRange(page, '2020-01-01', '2020-09-01');
// Add profit center
await addProfitCenter(page, '1AM');
// Attempt to submit
await submitAndExpectError(page);
// Verify user remains on the page
await expect(page.locator('text=Filter by Profit Center')).toBeVisible();
});
test('TC-030-N02: No search type selected', async ({ page }) => {
// Enter search name without selecting search type
await enterSearchName(page, 'TC-030-N02 No Type');
// Verify filter panels are not visible (search type not selected)
const profitCenterPanelVisible = await isAutocompletePanelVisible(page, profitCenterConfig);
expect(profitCenterPanelVisible).toBe(false);
// Attempt to submit
await submitAndExpectError(page);
});
test('TC-030-N03: Missing minimum date', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-N03 Missing Min Date');
// Only set maximum date
await setMaxDate(page, '2020-09-01');
// Add profit center
await addProfitCenter(page, '1AM');
// Attempt to submit
await submitAndExpectError(page);
});
test('TC-030-N04: Missing maximum date', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-N04 Missing Max Date');
// Only set minimum date
await setMinDate(page, '2020-01-01');
// Add profit center
await addProfitCenter(page, '1AM');
// Attempt to submit
await submitAndExpectError(page);
});
test('TC-030-N05: Empty profit center list', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-N05 Empty Profit Centers');
// Set valid date range
await setDateRange(page, '2020-01-01', '2020-09-01');
// Do NOT add any profit centers
// Verify profit center list is empty
const itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(0);
// Attempt to submit
await submitAndExpectError(page);
});
test('TC-030-N06: Invalid date range (min > max)', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-N06 Invalid Date Range');
// Set invalid date range (min date after max date)
await setDateRange(page, TestDateRanges.INVALID_REVERSED.min, TestDateRanges.INVALID_REVERSED.max);
// Add profit center
await addProfitCenter(page, '1AM');
// Attempt to submit
await submitAndExpectError(page);
});
test('TC-030-N07: Invalid profit center code', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-N07 Invalid PC Code');
// Set valid date range
await setDateRange(page, '2020-01-01', '2020-09-01');
// Try to add invalid profit center
// The autocomplete should not find any matches for "INVALID"
const panel = page.locator(`.rz-card:has-text("${profitCenterConfig.panelHeader}")`);
const autocomplete = panel.locator('.rz-autocomplete input');
await autocomplete.fill('INVALID');
// Wait for autocomplete to search
await page.waitForTimeout(500);
// Verify no autocomplete suggestions appear
const dropdown = page.locator('.rz-autocomplete-list');
const dropdownVisible = await dropdown.isVisible({ timeout: 2000 }).catch(() => false);
// If dropdown is not visible or empty, the invalid code is rejected
if (dropdownVisible) {
const items = dropdown.locator('.rz-autocomplete-list-item');
const count = await items.count();
expect(count).toBe(0);
}
// Verify profit center list is still empty
const itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(0);
});
test('TC-030-N08: Future date range', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-N08 Future Dates');
// Set future date range
await setDateRange(page, TestDateRanges.FUTURE.min, TestDateRanges.FUTURE.max);
// Add profit center
await addProfitCenter(page, '1AM');
// Submit search - may be accepted but will return no results
// or may show validation warning
await clickSubmitSearch(page);
// Check if there's a validation error or if it proceeds
const hasErrors = await hasValidationErrors(page);
if (!hasErrors) {
// If accepted, confirm the submission
await confirmSubmitSearch(page);
await assertNoErrorNotification(page);
}
// If there are validation errors, that's also acceptable behavior
});
test('TC-030-N09: Invalid date format', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-N09 Invalid Date Format');
// Try to enter invalid date format directly
// The Radzen date picker should prevent or reject invalid formats
const minDateInput = page.locator('input[name="MinimumDt"]');
await minDateInput.fill('31-12-2020'); // Invalid format
await setMaxDate(page, '2020-09-01');
// Add profit center
await addProfitCenter(page, '1AM');
// Attempt to submit - should fail validation
await submitAndExpectError(page);
});
test('TC-030-N10: Profit center with special characters', async ({ page }) => {
await selectSearchType(page, SearchTypes.TIMESPAN_PROFIT_CENTER);
await enterSearchName(page, 'TC-030-N10 PC Special Chars');
// Set valid date range
await setDateRange(page, '2020-01-01', '2020-09-01');
// Try to add profit center with special characters
const panel = page.locator(`.rz-card:has-text("${profitCenterConfig.panelHeader}")`);
const autocomplete = panel.locator('.rz-autocomplete input');
await autocomplete.fill('1AM!@#');
// Wait for autocomplete to search
await page.waitForTimeout(500);
// Verify no autocomplete suggestions appear for invalid input
const dropdown = page.locator('.rz-autocomplete-list');
const dropdownVisible = await dropdown.isVisible({ timeout: 2000 }).catch(() => false);
if (dropdownVisible) {
const items = dropdown.locator('.rz-autocomplete-list-item');
const count = await items.count();
expect(count).toBe(0);
}
// Verify profit center list is still empty
const itemCount = await getAutocompleteItemCount(page, profitCenterConfig);
expect(itemCount).toBe(0);
});
});
});