205 lines
7.5 KiB
TypeScript
205 lines
7.5 KiB
TypeScript
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 });
|
|
});
|
|
});
|