// @ts-check import { test, expect } from '@playwright/test'; import { loadTestUser } from '../utils/testUser.js'; test.describe('@p1 Educational Programs — sorting (tuition vs distance)', () => { test.setTimeout(120000); test('sort toggles change ordering; list remains populated', async ({ page }) => { const u = loadTestUser(); // Sign in await page.context().clearCookies(); await page.goto('/signin', { waitUntil: 'networkidle' }); await page.getByPlaceholder('Username', { exact: true }).fill(u.username); await page.getByPlaceholder('Password', { exact: true }).fill(u.password); await page.getByRole('button', { name: /^Sign In$/ }).click(); await page.waitForURL('**/signin-landing**', { timeout: 15000 }); // Seed selectedCareer for programs fetch (UI does not render SOC/CIP) await page.evaluate(() => { localStorage.setItem('selectedCareer', JSON.stringify({ title: 'Data Analyst', soc_code: '15-2051.00', cip_code: ['11.0802', '04.0201'] })); }); await page.goto('/educational-programs', { waitUntil: 'networkidle' }); // Wait for at least one card to render (match the card structure used in the component) const card = page.locator('div.rounded.border.p-3.text-sm').first(); await expect(card).toBeVisible({ timeout: 30000 }); // Helper to read numbers from card list async function readTuitionList() { return await page.locator('div.rounded.border.p-3.text-sm').evaluateAll(nodes => nodes.map(n => { const line = [...n.querySelectorAll('p')] .map(p => p.textContent || '') .find(t => /^In-State Tuition:\s*\$/.test(t)); if (!line) return NaN; const match = line.replace(/,/g, '').match(/In-State Tuition:\s*\$(\d+(?:\.\d+)?)/i); return match ? parseFloat(match[1]) : NaN; }).filter(x => Number.isFinite(x)) ); } async function readDistanceList() { return await page.locator('div.rounded.border.p-3.text-sm').evaluateAll(nodes => nodes.map(n => { const line = [...n.querySelectorAll('p')] .map(p => p.textContent || '') .find(t => /^Distance:\s*/i.test(t)); if (!line) return NaN; const match = line.match(/Distance:\s*(\d+(?:\.\d+)?)\s*mi/i); return match ? parseFloat(match[1]) : NaN; // NaN if "N/A" }).filter(x => Number.isFinite(x)) ); } const isNonDecreasing = (arr) => arr.every((v, i) => i === 0 || arr[i - 1] <= v); // --- Default sort = Tuition --- const tuitionBefore = await readTuitionList(); if (tuitionBefore.length >= 2) { expect(isNonDecreasing(tuitionBefore)).toBeTruthy(); } else { // At least the list is populated with cards const cardCount = await page.locator('div.rounded.border.p-3.text-sm').count(); expect(cardCount).toBeGreaterThan(0); } // Switch Sort to Distance (the select is inside its label) const sortSelect = page.locator('label:has-text("Sort")').locator('select'); await expect(sortSelect).toBeVisible(); await sortSelect.selectOption('distance'); // Wait for re-render by observing that either distances appear or first card block text changes const distances = await readDistanceList(); if (distances.length >= 2) { expect(isNonDecreasing(distances)).toBeTruthy(); } else { // If distances are N/A (no ZIP), at least ensure the list remains populated const cardCount = await page.locator('div.rounded.border.p-3.text-sm').count(); expect(cardCount).toBeGreaterThan(0); } }); });