Files

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);
}