import { Page, Locator, expect } from '@playwright/test'; /** * Page Object Model for Problem Set page * URL: /proset */ export class ProblemSetPage { readonly page: Page; readonly problemLinks: Locator; readonly searchInput: Locator; readonly proClassSelect: Locator; constructor(page: Page) { this.page = page; // Problem list elements this.problemLinks = page.locator('a[href^="/pro/"]'); this.searchInput = page.locator('input[placeholder*="搜尋"], input[placeholder*="Search"]'); this.proClassSelect = page.locator('select'); } /** * Navigate to the problem set page */ async goto() { await this.page.goto('/proset'); await this.page.waitForLoadState('networkidle'); } /** * Get all visible problems */ async getVisibleProblems() { return await this.problemLinks.all(); } /** * Click on a problem by its ID */ async clickProblem(problemId: number) { await this.page.click(`a[href="/pro/${problemId}"]`); await this.page.waitForLoadState('networkidle'); } /** * Search for problems by name */ async searchProblem(searchText: string) { if (await this.searchInput.isVisible()) { await this.searchInput.fill(searchText); await this.page.keyboard.press('Enter'); await this.page.waitForLoadState('networkidle'); } } /** * Select a ProClass filter */ async selectProClass(proClassName: string) { if (await this.proClassSelect.isVisible()) { await this.proClassSelect.selectOption({ label: proClassName }); await this.page.waitForLoadState('networkidle'); } } /** * Verify we are on the problem set page */ async verifyOnPage() { await expect(this.page).toHaveURL(/\/proset/); } /** * Get the first problem ID from the list */ async getFirstProblemId(): Promise { const firstProblem = this.problemLinks.first(); if (await firstProblem.count() === 0) { return null; } const href = await firstProblem.getAttribute('href'); if (!href) return null; const match = href.match(/\/pro\/(\d+)/); return match ? parseInt(match[1]) : null; } }