238 lines
7.0 KiB
TypeScript
238 lines
7.0 KiB
TypeScript
import { Page, Download } from '@playwright/test';
|
|
import path from 'path';
|
|
|
|
/**
|
|
* Configuration for file upload filter panels
|
|
*/
|
|
export interface FileUploadConfig {
|
|
/** Panel header text to identify the panel */
|
|
panelHeader: string;
|
|
/** Template filename (for download verification) */
|
|
templateFilename: string;
|
|
/** Expected columns in the template */
|
|
expectedColumns: string[];
|
|
}
|
|
|
|
/**
|
|
* Work Order upload configuration
|
|
*/
|
|
export const workOrderConfig: FileUploadConfig = {
|
|
panelHeader: 'Filter by Work Order',
|
|
templateFilename: 'WorkOrderTemplate.xlsx',
|
|
expectedColumns: ['Work Order Number'],
|
|
};
|
|
|
|
/**
|
|
* Component Lot upload configuration
|
|
*/
|
|
export const componentLotConfig: FileUploadConfig = {
|
|
panelHeader: 'Filter By Component Lot',
|
|
templateFilename: 'ComponentLotTemplate.xlsx',
|
|
expectedColumns: ['Lot Number', 'Item Number'],
|
|
};
|
|
|
|
/**
|
|
* Item Number upload configuration
|
|
*/
|
|
export const itemNumberConfig: FileUploadConfig = {
|
|
panelHeader: 'Filter by Item Number',
|
|
templateFilename: 'ItemNumberTemplate.xlsx',
|
|
expectedColumns: ['Item Number'],
|
|
};
|
|
|
|
/**
|
|
* Part Operation (Item/Operation/MIS) upload configuration
|
|
*/
|
|
export const partOperationConfig: FileUploadConfig = {
|
|
panelHeader: 'Filter By Item/Operation/MIS',
|
|
templateFilename: 'PartOperationTemplate.xlsx',
|
|
expectedColumns: ['Item Number', 'Operation Number', 'MIS Number', 'MIS Revision'],
|
|
};
|
|
|
|
/**
|
|
* Upload a file to a filter panel
|
|
* @param page - Playwright page object
|
|
* @param config - File upload configuration
|
|
* @param filePath - Path to the file to upload
|
|
*/
|
|
export async function uploadFile(
|
|
page: Page,
|
|
config: FileUploadConfig,
|
|
filePath: string
|
|
): Promise<void> {
|
|
// Find the panel by its header
|
|
const panel = page.locator(`.rz-card:has-text("${config.panelHeader}")`);
|
|
|
|
// Find the file input (RadzenUpload creates a hidden input)
|
|
const fileInput = panel.locator('input[type="file"]');
|
|
|
|
// Set the file
|
|
await fileInput.setInputFiles(filePath);
|
|
|
|
// Wait for upload to complete
|
|
await page.waitForTimeout(2000);
|
|
}
|
|
|
|
/**
|
|
* Upload a file using the global file input (fallback for panels without specific locators)
|
|
* @param page - Playwright page object
|
|
* @param filePath - Path to the file to upload
|
|
*/
|
|
export async function uploadFileGlobal(page: Page, filePath: string): Promise<void> {
|
|
const fileInput = page.locator('input[type="file"]');
|
|
await fileInput.setInputFiles(filePath);
|
|
await page.waitForTimeout(2000);
|
|
}
|
|
|
|
/**
|
|
* Download a template file from a filter panel
|
|
* @param page - Playwright page object
|
|
* @param config - File upload configuration
|
|
* @returns The Download object
|
|
*/
|
|
export async function downloadTemplate(
|
|
page: Page,
|
|
config: FileUploadConfig
|
|
): Promise<Download> {
|
|
const panel = page.locator(`.rz-card:has-text("${config.panelHeader}")`);
|
|
|
|
// Start waiting for download before clicking
|
|
const downloadPromise = page.waitForEvent('download');
|
|
|
|
// Click the download template button
|
|
await panel.locator('button:has-text("Download Template")').click();
|
|
|
|
// Wait for download to complete
|
|
const download = await downloadPromise;
|
|
return download;
|
|
}
|
|
|
|
/**
|
|
* Clear uploaded data from a filter panel
|
|
* @param page - Playwright page object
|
|
* @param config - File upload configuration
|
|
*/
|
|
export async function clearUploadedData(
|
|
page: Page,
|
|
config: FileUploadConfig
|
|
): Promise<void> {
|
|
const panel = page.locator(`.rz-card:has-text("${config.panelHeader}")`);
|
|
const clearButton = panel.locator('button:has-text("Clear Data")');
|
|
|
|
if (await clearButton.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
await clearButton.click();
|
|
|
|
// Wait for and confirm the dialog
|
|
await page.waitForSelector('text=Confirm Clear', { timeout: 5000 });
|
|
// Click the Ok button inside the dialog
|
|
await page.locator('.rz-dialog-wrapper button').getByText('Ok', { exact: true }).click();
|
|
|
|
// Wait for the grid to update - either "No records" appears or grid rows are removed
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Wait for dialog to close
|
|
await page.waitForSelector('.rz-dialog-wrapper', { state: 'hidden', timeout: 5000 }).catch(() => {});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the count of uploaded items in a filter panel
|
|
* @param page - Playwright page object
|
|
* @param config - File upload configuration
|
|
* @returns Number of items in the grid
|
|
*/
|
|
export async function getUploadedItemCount(
|
|
page: Page,
|
|
config: FileUploadConfig
|
|
): Promise<number> {
|
|
const panel = page.locator(`.rz-card:has-text("${config.panelHeader}")`);
|
|
const grid = panel.locator('.rz-data-grid');
|
|
|
|
// Check for "No records" message
|
|
const noRecords = grid.locator('text=No records to display');
|
|
if (await noRecords.isVisible({ timeout: 1000 }).catch(() => false)) {
|
|
return 0;
|
|
}
|
|
|
|
const rows = grid.locator('tbody tr');
|
|
return await rows.count();
|
|
}
|
|
|
|
/**
|
|
* Check if the file upload panel is visible
|
|
* @param page - Playwright page object
|
|
* @param config - File upload configuration
|
|
* @returns true if panel is visible
|
|
*/
|
|
export async function isFileUploadPanelVisible(
|
|
page: Page,
|
|
config: FileUploadConfig
|
|
): Promise<boolean> {
|
|
const panel = page.locator(`text=${config.panelHeader}`);
|
|
return await panel.isVisible({ timeout: 2000 }).catch(() => false);
|
|
}
|
|
|
|
/**
|
|
* Verify the uploaded file shows items in the grid
|
|
* @param page - Playwright page object
|
|
* @param config - File upload configuration
|
|
* @returns true if grid has items
|
|
*/
|
|
export async function hasUploadedItems(
|
|
page: Page,
|
|
config: FileUploadConfig
|
|
): Promise<boolean> {
|
|
const count = await getUploadedItemCount(page, config);
|
|
return count > 0;
|
|
}
|
|
|
|
/**
|
|
* Get the test data directory path
|
|
*/
|
|
export function getTestDataDir(): string {
|
|
return path.join(__dirname, '..', 'test-data');
|
|
}
|
|
|
|
/**
|
|
* Get the path to a test data file
|
|
* @param filename - The filename in the test-data directory
|
|
*/
|
|
export function getTestDataPath(filename: string): string {
|
|
return path.join(getTestDataDir(), filename);
|
|
}
|
|
|
|
/**
|
|
* Standard test files available in test-data directory
|
|
*/
|
|
export const TestFiles = {
|
|
// Work Order files
|
|
SINGLE_WORKORDER: 'single_workorder.xlsx',
|
|
MULTIPLE_WORKORDERS: 'multiple_workorders.xlsx',
|
|
|
|
// Component Lot files
|
|
SINGLE_LOT: 'single_lot.xlsx',
|
|
MULTIPLE_LOTS: 'multiple_lots.xlsx',
|
|
|
|
// Item Number files
|
|
SINGLE_ITEM: 'single_item.xlsx',
|
|
MULTIPLE_ITEMS: 'multiple_items.xlsx',
|
|
|
|
// Part Operation files
|
|
SINGLE_OPERATION: 'single_operation.xlsx',
|
|
MULTIPLE_OPERATIONS: 'multiple_operations.xlsx',
|
|
|
|
// Invalid files (for negative testing)
|
|
INVALID_FORMAT: 'invalid_format.txt',
|
|
EMPTY_FILE: 'empty_file.xlsx',
|
|
INVALID_WORKORDERS: 'invalid_workorders.xlsx',
|
|
SPECIAL_CHARS_WORKORDERS: 'special_chars_workorders.xlsx',
|
|
} as const;
|
|
|
|
/**
|
|
* Get full path to a standard test file
|
|
* @param testFile - One of the TestFiles constants
|
|
*/
|
|
export function getTestFile(testFile: string): string {
|
|
return getTestDataPath(testFile);
|
|
}
|