- Add HVAC_Test_User_Factory class with: * User creation with specific roles * Multiple role support * Persona management system * Account cleanup integration - Create comprehensive test suite in HVAC_Test_User_Factory_Test.php - Update testing improvement plan documentation - Add implementation decisions to project memory bank - Restructure .gitignore with: * Whitelist approach for better file management * Explicit backup exclusions * Specific bin directory inclusions Part of the Account Management component from the testing framework improvement plan.
104 lines
No EOL
2.5 KiB
TypeScript
104 lines
No EOL
2.5 KiB
TypeScript
import { expect, type Page, type Locator } from '@playwright/test';
|
|
|
|
export class TestUtils {
|
|
readonly page: Page;
|
|
|
|
constructor(page: Page) {
|
|
this.page = page;
|
|
}
|
|
|
|
/**
|
|
* Wait for network requests to complete
|
|
*/
|
|
async waitForNetworkIdle() {
|
|
await this.page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
/**
|
|
* Custom expect extension for checking element visibility with timeout
|
|
*/
|
|
async expectToBeVisible(locator: Locator, timeoutMs = 5000) {
|
|
await expect(locator).toBeVisible({ timeout: timeoutMs });
|
|
}
|
|
|
|
/**
|
|
* Custom expect extension for checking element text content
|
|
*/
|
|
async expectToHaveText(locator: Locator, text: string, timeoutMs = 5000) {
|
|
await expect(locator).toHaveText(text, { timeout: timeoutMs });
|
|
}
|
|
|
|
/**
|
|
* Take a screenshot with a custom name
|
|
*/
|
|
async takeScreenshot(name: string) {
|
|
await this.page.screenshot({ path: `test-results/screenshots/${name}.png` });
|
|
}
|
|
|
|
/**
|
|
* Check if an element exists
|
|
*/
|
|
async elementExists(selector: string): Promise<boolean> {
|
|
const element = this.page.locator(selector);
|
|
return await element.count() > 0;
|
|
}
|
|
|
|
/**
|
|
* Wait for and click an element
|
|
*/
|
|
async clickWhenReady(selector: string, timeoutMs = 5000) {
|
|
const element = this.page.locator(selector);
|
|
await element.waitFor({ state: 'visible', timeout: timeoutMs });
|
|
await element.click();
|
|
}
|
|
|
|
/**
|
|
* Fill a form field when it becomes ready
|
|
*/
|
|
async fillWhenReady(selector: string, value: string, timeoutMs = 5000) {
|
|
const element = this.page.locator(selector);
|
|
await element.waitFor({ state: 'visible', timeout: timeoutMs });
|
|
await element.fill(value);
|
|
}
|
|
|
|
/**
|
|
* Get current URL path
|
|
*/
|
|
getCurrentPath(): string {
|
|
return new URL(this.page.url()).pathname;
|
|
}
|
|
|
|
/**
|
|
* Wait for navigation to complete
|
|
*/
|
|
async waitForNavigation(timeoutMs = 5000) {
|
|
await this.page.waitForNavigation({ timeout: timeoutMs });
|
|
}
|
|
}
|
|
|
|
// Custom matchers
|
|
expect.extend({
|
|
async toBeVisibleWithin(received: Locator, timeout: number) {
|
|
try {
|
|
await received.waitFor({ state: 'visible', timeout });
|
|
return {
|
|
message: () => 'Element is visible',
|
|
pass: true,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
message: () => `Element not visible within ${timeout}ms`,
|
|
pass: false,
|
|
};
|
|
}
|
|
},
|
|
});
|
|
|
|
// Declare custom matcher types
|
|
declare global {
|
|
namespace PlaywrightTest {
|
|
interface Matchers<R> {
|
|
toBeVisibleWithin(timeout: number): Promise<R>;
|
|
}
|
|
}
|
|
} |