This commit: - Creates comprehensive CERTIFICATE_TESTING_GUIDE.md to document certificate testing - Updates TRAINER_JOURNEY_TEST_SUMMARY.md to include certificate functionality - Updates main README.md with certificate testing information - Creates a centralized Config.ts utility for consistent configuration - Updates CertificatePage.ts and other page objects for consistency - Creates a guided manual test script (run-certificate-tests.sh) - Archives outdated certificate test files - Improves documentation organization and consistency
270 lines
No EOL
11 KiB
TypeScript
270 lines
No EOL
11 KiB
TypeScript
import { Page, expect } from '@playwright/test';
|
|
import { BasePage } from './BasePage';
|
|
import { Config } from '../utils/Config';
|
|
|
|
export class CertificatePage extends BasePage {
|
|
// Generate Certificates page selectors
|
|
private readonly generateCertificatesTitle = 'h1:has-text("Generate Certificates")';
|
|
private readonly eventSelector = 'select[name="event_id"]';
|
|
private readonly eventSearchInput = 'input[name="event_search"]';
|
|
private readonly selectAllCheckbox = 'input[name="select_all"]';
|
|
private readonly attendeeCheckboxes = 'input[name="attendees[]"]';
|
|
private readonly generateButton = 'button:has-text("Generate Certificates")';
|
|
private readonly previewButton = 'button:has-text("Preview Certificate")';
|
|
private readonly successMessage = '.hvac-success-message';
|
|
private readonly errorMessage = '.hvac-error-message';
|
|
private readonly attendeeList = '.hvac-attendee-list';
|
|
private readonly attendeeItem = '.hvac-attendee-item';
|
|
private readonly checkinStatusAttribute = 'data-checkin-status';
|
|
private readonly loadingIndicator = '.hvac-loading';
|
|
|
|
// Certificate Reports page selectors
|
|
private readonly certificateReportsTitle = 'h1:has-text("Certificate Reports")';
|
|
private readonly certificateFilterInput = 'input[name="certificate_search"]';
|
|
private readonly certificateTable = '.hvac-certificate-table';
|
|
private readonly certificateTableRows = '.hvac-certificate-table tbody tr';
|
|
private readonly viewCertificateButton = 'button:has-text("View")';
|
|
private readonly emailCertificateButton = 'button:has-text("Email")';
|
|
private readonly revokeCertificateButton = 'button:has-text("Revoke")';
|
|
private readonly certificatePagination = '.hvac-pagination';
|
|
private readonly certificateModal = '.hvac-certificate-modal';
|
|
private readonly certificatePreview = '.hvac-certificate-preview';
|
|
private readonly closeModalButton = '.hvac-modal-close';
|
|
private readonly confirmRevocationButton = 'button:has-text("Confirm Revocation")';
|
|
private readonly confirmEmailButton = 'button:has-text("Send Email")';
|
|
private readonly previousPageButton = '.hvac-pagination-prev';
|
|
private readonly nextPageButton = '.hvac-pagination-next';
|
|
|
|
constructor(page: Page) {
|
|
super(page);
|
|
}
|
|
|
|
// Common methods
|
|
async navigateToGenerateCertificates(): Promise<void> {
|
|
await this.page.goto(Config.generateCertificatesUrl);
|
|
await this.page.waitForLoadState('networkidle');
|
|
await this.screenshot('generate-certificates-page');
|
|
}
|
|
|
|
async navigateToCertificateReports(): Promise<void> {
|
|
await this.page.goto(Config.certificateReportsUrl);
|
|
await this.page.waitForLoadState('networkidle');
|
|
await this.screenshot('certificate-reports-page');
|
|
}
|
|
|
|
// Generate Certificates page methods
|
|
async isGenerateCertificatesPageVisible(): Promise<boolean> {
|
|
return await this.isVisible(this.generateCertificatesTitle);
|
|
}
|
|
|
|
async selectEvent(eventName: string): Promise<void> {
|
|
// If there's a search input, try using it
|
|
if (await this.isVisible(this.eventSearchInput)) {
|
|
await this.fill(this.eventSearchInput, eventName);
|
|
await this.page.waitForTimeout(Config.shortWait);
|
|
}
|
|
|
|
// Select the event from dropdown
|
|
await this.page.selectOption(this.eventSelector, { label: eventName });
|
|
await this.page.waitForTimeout(Config.shortWait);
|
|
|
|
// Wait for loading indicator to disappear if it's present
|
|
const loadingElement = this.page.locator(this.loadingIndicator);
|
|
if (await loadingElement.isVisible()) {
|
|
await loadingElement.waitFor({ state: 'hidden', timeout: Config.defaultTimeout });
|
|
}
|
|
|
|
await this.screenshot('event-selected');
|
|
}
|
|
|
|
async getAttendeeCount(): Promise<number> {
|
|
return await this.page.locator(this.attendeeItem).count();
|
|
}
|
|
|
|
async getCheckedInAttendeeCount(): Promise<number> {
|
|
let checkedInCount = 0;
|
|
const attendees = this.page.locator(this.attendeeItem);
|
|
const count = await attendees.count();
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const status = await attendees.nth(i).getAttribute(this.checkinStatusAttribute);
|
|
if (status === 'checked-in') {
|
|
checkedInCount++;
|
|
}
|
|
}
|
|
|
|
return checkedInCount;
|
|
}
|
|
|
|
async selectAllAttendees(): Promise<void> {
|
|
await this.click(this.selectAllCheckbox);
|
|
await this.screenshot('all-attendees-selected');
|
|
}
|
|
|
|
async selectCheckedInAttendees(): Promise<void> {
|
|
// Deselect "Select All" if it's checked
|
|
const selectAllChecked = await this.page.isChecked(this.selectAllCheckbox);
|
|
if (selectAllChecked) {
|
|
await this.click(this.selectAllCheckbox);
|
|
}
|
|
|
|
// Select only checked-in attendees
|
|
const attendees = this.page.locator(this.attendeeItem);
|
|
const count = await attendees.count();
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const status = await attendees.nth(i).getAttribute(this.checkinStatusAttribute);
|
|
if (status === 'checked-in') {
|
|
const checkbox = attendees.nth(i).locator('input[type="checkbox"]');
|
|
await checkbox.check();
|
|
}
|
|
}
|
|
|
|
await this.screenshot('checked-in-attendees-selected');
|
|
}
|
|
|
|
async selectNonCheckedInAttendees(): Promise<void> {
|
|
// Deselect "Select All" if it's checked
|
|
const selectAllChecked = await this.page.isChecked(this.selectAllCheckbox);
|
|
if (selectAllChecked) {
|
|
await this.click(this.selectAllCheckbox);
|
|
}
|
|
|
|
// Select only non-checked-in attendees
|
|
const attendees = this.page.locator(this.attendeeItem);
|
|
const count = await attendees.count();
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const status = await attendees.nth(i).getAttribute(this.checkinStatusAttribute);
|
|
if (status !== 'checked-in') {
|
|
const checkbox = attendees.nth(i).locator('input[type="checkbox"]');
|
|
await checkbox.check();
|
|
}
|
|
}
|
|
|
|
await this.screenshot('non-checked-in-attendees-selected');
|
|
}
|
|
|
|
async generateCertificates(): Promise<void> {
|
|
await this.click(this.generateButton);
|
|
|
|
// Wait for loading indicator to disappear if it's present
|
|
const loadingElement = this.page.locator(this.loadingIndicator);
|
|
if (await loadingElement.isVisible()) {
|
|
await loadingElement.waitFor({ state: 'hidden', timeout: Config.longWait });
|
|
}
|
|
|
|
await this.page.waitForTimeout(Config.mediumWait); // Additional wait for any post-processing
|
|
await this.screenshot('certificates-generated');
|
|
}
|
|
|
|
async previewCertificate(): Promise<void> {
|
|
await this.click(this.previewButton);
|
|
|
|
// Wait for the preview modal to appear
|
|
await this.waitForElement(this.certificateModal);
|
|
await this.screenshot('certificate-preview');
|
|
}
|
|
|
|
async closePreview(): Promise<void> {
|
|
if (await this.isVisible(this.closeModalButton)) {
|
|
await this.click(this.closeModalButton);
|
|
await this.page.waitForTimeout(Config.shortWait); // Wait for modal to close
|
|
}
|
|
}
|
|
|
|
async isSuccessMessageVisible(): Promise<boolean> {
|
|
return await this.isVisible(this.successMessage);
|
|
}
|
|
|
|
async isErrorMessageVisible(): Promise<boolean> {
|
|
return await this.isVisible(this.errorMessage);
|
|
}
|
|
|
|
async getSuccessMessage(): Promise<string> {
|
|
return await this.getText(this.successMessage);
|
|
}
|
|
|
|
async getErrorMessage(): Promise<string> {
|
|
return await this.getText(this.errorMessage);
|
|
}
|
|
|
|
// Certificate Reports page methods
|
|
async isCertificateReportsPageVisible(): Promise<boolean> {
|
|
return await this.isVisible(this.certificateReportsTitle);
|
|
}
|
|
|
|
async searchCertificates(query: string): Promise<void> {
|
|
await this.fill(this.certificateFilterInput, query);
|
|
await this.page.waitForTimeout(Config.shortWait); // Wait for search results
|
|
|
|
// Wait for loading indicator to disappear if it's present
|
|
const loadingElement = this.page.locator(this.loadingIndicator);
|
|
if (await loadingElement.isVisible()) {
|
|
await loadingElement.waitFor({ state: 'hidden', timeout: Config.defaultTimeout });
|
|
}
|
|
|
|
await this.screenshot('certificate-search');
|
|
}
|
|
|
|
async getCertificateCount(): Promise<number> {
|
|
return await this.page.locator(this.certificateTableRows).count();
|
|
}
|
|
|
|
async viewCertificate(index: number = 0): Promise<void> {
|
|
const viewButtons = this.page.locator(this.viewCertificateButton);
|
|
await viewButtons.nth(index).click();
|
|
|
|
// Wait for the preview modal to appear
|
|
await this.waitForElement(this.certificateModal);
|
|
await this.screenshot('view-certificate');
|
|
}
|
|
|
|
async emailCertificate(index: number = 0): Promise<void> {
|
|
const emailButtons = this.page.locator(this.emailCertificateButton);
|
|
await emailButtons.nth(index).click();
|
|
|
|
// Wait for the email confirmation dialog
|
|
if (await this.isVisible(this.confirmEmailButton)) {
|
|
await this.click(this.confirmEmailButton);
|
|
await this.page.waitForTimeout(Config.mediumWait); // Wait for email to be sent
|
|
}
|
|
|
|
await this.screenshot('email-certificate');
|
|
}
|
|
|
|
async revokeCertificate(index: number = 0): Promise<void> {
|
|
const revokeButtons = this.page.locator(this.revokeCertificateButton);
|
|
await revokeButtons.nth(index).click();
|
|
|
|
// Wait for the revocation confirmation dialog
|
|
if (await this.isVisible(this.confirmRevocationButton)) {
|
|
await this.click(this.confirmRevocationButton);
|
|
await this.page.waitForTimeout(Config.mediumWait); // Wait for revocation to complete
|
|
}
|
|
|
|
await this.screenshot('revoke-certificate');
|
|
}
|
|
|
|
async isPaginationVisible(): Promise<boolean> {
|
|
return await this.isVisible(this.certificatePagination);
|
|
}
|
|
|
|
async goToNextPage(): Promise<boolean> {
|
|
if (await this.isVisible(this.nextPageButton)) {
|
|
await this.click(this.nextPageButton);
|
|
await this.page.waitForTimeout(Config.shortWait);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async goToPreviousPage(): Promise<boolean> {
|
|
if (await this.isVisible(this.previousPageButton)) {
|
|
await this.click(this.previousPageButton);
|
|
await this.page.waitForTimeout(Config.shortWait);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
} |