Files
TOJE2E/pages/ChallengePage.ts
ChenKaiLiuG e139daa410 Initialize
2026-01-29 11:48:45 +08:00

143 lines
4.0 KiB
TypeScript

import { Page, Locator, expect } from '@playwright/test';
/**
* Page Object Model for Challenge Detail page
* URL: /chal/{id}
*/
export class ChallengePage {
readonly page: Page;
readonly verdictBadge: Locator;
readonly runtimeText: Locator;
readonly memoryText: Locator;
readonly scoreText: Locator;
readonly codeBlock: Locator;
readonly subtaskResults: Locator;
readonly testdataResults: Locator;
readonly messageText: Locator;
constructor(page: Page) {
this.page = page;
// Challenge result elements
this.verdictBadge = page.locator('.badge, [class*="verdict"], [class*="state"]').first();
this.runtimeText = page.locator('text=/Runtime|執行時間/');
this.memoryText = page.locator('text=/Memory|記憶體/');
this.scoreText = page.locator('text=/Score|分數|Rate/');
this.codeBlock = page.locator('pre, code, .code-block');
this.subtaskResults = page.locator('[class*="subtask"]');
this.testdataResults = page.locator('[class*="testdata"]');
this.messageText = page.locator('.message, [class*="response"]');
}
/**
* Navigate to a specific challenge page
*/
async goto(challengeId: number) {
await this.page.goto(`/chal/${challengeId}`);
await this.page.waitForLoadState('networkidle');
}
/**
* Get the verdict/state of the challenge
*/
async getVerdict(): Promise<string> {
// Wait for verdict to be not "Challenging" or "Not Started"
await this.page.waitForTimeout(1000); // Initial wait
const verdictElement = this.verdictBadge.or(
this.page.locator('text=/AC|WA|TLE|MLE|RE|CE|PE|IE/')
).first();
if (await verdictElement.count() === 0) {
return 'UNKNOWN';
}
const verdictText = await verdictElement.textContent();
return verdictText?.trim() || 'UNKNOWN';
}
/**
* Wait for challenge to finish judging
* @param timeoutMs Maximum time to wait in milliseconds
* @param pollIntervalMs How often to check the verdict
*/
async waitForJudgeComplete(timeoutMs: number = 60000, pollIntervalMs: number = 2000) {
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
const verdict = await this.getVerdict();
// Check if judging is complete
if (verdict !== 'Challenging' && verdict !== 'Not Started' && verdict !== 'UNKNOWN') {
return verdict;
}
// Wait before next poll
await this.page.waitForTimeout(pollIntervalMs);
// Reload the page to get updated status
await this.page.reload({ waitUntil: 'networkidle' });
}
throw new Error(`Challenge did not complete within ${timeoutMs}ms`);
}
/**
* Get runtime and memory usage
*/
async getPerformanceMetrics() {
const metrics = {
runtime: '',
memory: '',
};
if (await this.runtimeText.isVisible()) {
const runtimeFull = await this.runtimeText.textContent();
metrics.runtime = runtimeFull?.match(/\d+/)?.[0] || '';
}
if (await this.memoryText.isVisible()) {
const memoryFull = await this.memoryText.textContent();
metrics.memory = memoryFull?.match(/\d+/)?.[0] || '';
}
return metrics;
}
/**
* Get the score/rate
*/
async getScore(): Promise<string> {
if (await this.scoreText.isVisible()) {
const scoreText = await this.scoreText.textContent();
const scoreMatch = scoreText?.match(/\d+/);
return scoreMatch?.[0] || '0';
}
return '0';
}
/**
* Verify we are on the challenge page
*/
async verifyOnPage(challengeId: number) {
await expect(this.page).toHaveURL(new RegExp(`/chal/${challengeId}`));
}
/**
* Check if subtask results are visible
*/
async hasSubtaskResults(): Promise<boolean> {
return await this.subtaskResults.count() > 0;
}
/**
* Get challenge message (for errors, etc.)
*/
async getMessage(): Promise<string | null> {
if (await this.messageText.isVisible()) {
return await this.messageText.textContent();
}
return null;
}
}