import type { Page } from 'playwright'; import { logger } from '../../utils/logger'; import { SELECTORS } from '../selectors'; import { acceptEulaEverywhere, acceptEulaUntilSettled } from './login'; /** * Navigate to the target assignment within a class. * * 1. Find and click the class by title * 2. Find and click the assignment (by title or first available) * 3. Return the assignment launch URL for later refresh */ export async function navigateToAssignment( page: Page, classTitle: string, assignmentTitle?: string | null, ): Promise { // ---- Open class ---- logger.info('Opening class', { classTitle }); if (!classTitle || classTitle.trim() === '') { throw new Error('Class title is empty - cannot navigate to assignment.'); } const escapedTitle = classTitle.replace(/"/g, '\\"'); const exact = page.locator(`a[title="${escapedTitle}"]`).first(); const fallback = page .locator(SELECTORS.class.classNameLink) .filter({ hasText: classTitle }) .first(); const link = (await exact.isVisible({ timeout: 5000 }).catch(() => false)) ? exact : fallback; await link.waitFor({ state: 'visible', timeout: 30000 }); await Promise.all([ page .waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 60000 }) .catch(() => null), link.click({ force: true }), ]); await page.waitForTimeout(1500); await acceptEulaEverywhere(page); const classUrl = page.url(); logger.info('Class page loaded', { classUrl }); // ---- Open assignment ---- // Ensure we're on the class page if (page.url() !== classUrl) { await page.goto(classUrl, { waitUntil: 'domcontentloaded', timeout: 60000, }); await page.waitForTimeout(1500); } await acceptEulaUntilSettled(page, 5000); let assignmentButton; if (assignmentTitle) { logger.info('Looking for assignment', { assignmentTitle }); assignmentButton = page .locator(SELECTORS.assignment.assignmentRow) .filter({ hasText: assignmentTitle }) .locator(SELECTORS.assignment.openButton) .first(); } else { logger.info('Looking for first available assignment'); assignmentButton = page .locator(SELECTORS.assignment.allOpenButtons) .first(); } await assignmentButton.waitFor({ state: 'visible', timeout: 30000 }); await Promise.all([ page .waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 60000 }) .catch(() => null), assignmentButton.click({ force: true }), ]); await page.waitForTimeout(5000); await acceptEulaUntilSettled(page, 7000); const assignmentLaunchUrl = page.url(); logger.info('Assignment page loaded', { assignmentLaunchUrl }); return assignmentLaunchUrl; }