111 lines
3.1 KiB
TypeScript
111 lines
3.1 KiB
TypeScript
import { Page, Locator, expect } from '@playwright/test';
|
|
|
|
/**
|
|
* Page Object Model for Problem Submission page
|
|
* URL: /submit/{id}
|
|
*/
|
|
export class SubmitPage {
|
|
readonly page: Page;
|
|
readonly compilerSelect: Locator;
|
|
readonly codeTextarea: Locator;
|
|
readonly submitButton: Locator;
|
|
readonly errorMessage: Locator;
|
|
|
|
constructor(page: Page) {
|
|
this.page = page;
|
|
|
|
// Submit form elements
|
|
this.compilerSelect = page.locator('select[name="compiler"], #compiler-select, select').first();
|
|
this.codeTextarea = page.locator('textarea[name="code"], textarea#code, .CodeMirror textarea, textarea').first();
|
|
this.submitButton = page.locator('button:has-text("提交"), button:has-text("Submit"), input[type="submit"]').first();
|
|
this.errorMessage = page.locator('.error, .alert-danger, [class*="error"]');
|
|
}
|
|
|
|
/**
|
|
* Navigate to submit page for a specific problem
|
|
*/
|
|
async goto(problemId: number) {
|
|
await this.page.goto(`/submit/${problemId}`);
|
|
await this.page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
/**
|
|
* Select a compiler
|
|
*/
|
|
async selectCompiler(compiler: string) {
|
|
await this.compilerSelect.selectOption({ label: compiler });
|
|
}
|
|
|
|
/**
|
|
* Fill in the code
|
|
* Note: This might need adjustment if the site uses CodeMirror or Monaco editor
|
|
*/
|
|
async fillCode(code: string) {
|
|
// Check if CodeMirror is being used
|
|
const codeMirror = this.page.locator('.CodeMirror');
|
|
if (await codeMirror.count() > 0) {
|
|
// CodeMirror editor - need to use execCommand
|
|
await codeMirror.click();
|
|
await this.page.evaluate((codeText) => {
|
|
const cm = (window as any).CodeMirror;
|
|
if (cm && cm.instances && cm.instances.length > 0) {
|
|
cm.instances[0].setValue(codeText);
|
|
}
|
|
}, code);
|
|
} else {
|
|
// Regular textarea
|
|
await this.codeTextarea.fill(code);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Submit the code
|
|
*/
|
|
async submit() {
|
|
await this.submitButton.click();
|
|
|
|
// Wait for navigation or response
|
|
try {
|
|
await this.page.waitForLoadState('networkidle', { timeout: 5000 });
|
|
} catch (e) {
|
|
// Ignore timeout if page doesn't navigate
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Submit code with compiler selection
|
|
*/
|
|
async submitCode(compiler: string, code: string) {
|
|
await this.selectCompiler(compiler);
|
|
await this.fillCode(code);
|
|
await this.submit();
|
|
}
|
|
|
|
/**
|
|
* Verify we are on the submit page
|
|
*/
|
|
async verifyOnPage(problemId: number) {
|
|
await expect(this.page).toHaveURL(new RegExp(`/submit/${problemId}`));
|
|
}
|
|
|
|
/**
|
|
* Check if there's an error message
|
|
*/
|
|
async getErrorMessage(): Promise<string | null> {
|
|
if (await this.errorMessage.isVisible()) {
|
|
return await this.errorMessage.textContent();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Wait for submission cooldown message to disappear
|
|
*/
|
|
async waitForCooldown(timeoutMs: number = 31000) {
|
|
const cooldownMessage = this.page.locator('text=/冷卻|cooldown|請稍候/i');
|
|
if (await cooldownMessage.isVisible()) {
|
|
await cooldownMessage.waitFor({ state: 'hidden', timeout: timeoutMs });
|
|
}
|
|
}
|
|
}
|