// @ts-nocheck import { test, expect } from '@playwright/test'; import { loadTestUser } from '../utils/testUser.js'; const j = (o) => JSON.stringify(o); test.describe('@p1 CareerRoadmap — core panels + coach (47)', () => { test.setTimeout(20000); test('Loads coach UI, salary/econ panels, and projection chart', async ({ page, request }) => { const u = loadTestUser(); // ── Premium gate (include area/state for salary/econ) await page.route( /\/api\/user-profile\?fields=.*(firstname|is_premium|is_pro_premium|area|state).*/i, r => r.fulfill({ status: 200, contentType: 'application/json', body: j({ firstname: 'Tester', is_premium: 1, is_pro_premium: 0, area: 'Atlanta-Sandy Springs-Roswell, GA', state: 'GA' }) }) ); // ── Sign in (real form) await page.context().clearCookies(); await page.goto('/signin', { waitUntil: 'domcontentloaded' }); 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 a scenario; capture id const scen = await request.post('/api/premium/career-profile', { data: { career_name: 'Software Developers', status: 'planned', start_date: '2024-01-01' } }); const { career_profile_id } = await scen.json(); // ── Deterministic reads CareerRoadmap expects await page.route(/\/api\/premium\/financial-profile$/i, r => r.fulfill({ status: 200, contentType: 'application/json', body: j({ current_salary: 85000, additional_income: 0, monthly_expenses: 2500, monthly_debt_payments: 300, retirement_savings: 12000, emergency_fund: 4000, retirement_contribution: 300, emergency_contribution: 200, extra_cash_emergency_pct: 50, extra_cash_retirement_pct: 50 }) })); // Scenario row: include soc_code to skip /api/careers/resolve await page.route(new RegExp(`/api/premium/career-profile/${career_profile_id}$`, 'i'), r => r.fulfill({ status: 200, contentType: 'application/json', body: j({ id: career_profile_id, career_name: 'Software Developers', scenario_title: 'Software Developers', soc_code: '15-1252.00', start_date: '2024-01-01', college_enrollment_status: 'not_enrolled' }) })); // College profile — return explicit values to guarantee projection data await page.route(new RegExp(`/api/premium/college-profile\\?careerProfileId=${career_profile_id}$`, 'i'), r => r.fulfill({ status: 200, contentType: 'application/json', body: j({ college_enrollment_status: 'not_enrolled', existing_college_debt: 8000, interest_rate: 5.5, loan_term: 10, loan_deferral_until_graduation: 0, academic_calendar: 'monthly', annual_financial_aid: 0, tuition: 0, extra_payment: 0, expected_salary: 90000 }) }) ); // Salary (regional + national) await page.route(/\/api\/salary\?*/i, r => r.fulfill({ status: 200, contentType: 'application/json', body: j({ regional: { regional_PCT10: 60000, regional_MEDIAN: 100000, regional_PCT90: 160000 }, national: { national_PCT10: 55000, national_MEDIAN: 98000, national_PCT90: 155000 } }) })); // Economic projections (state + national) await page.route(/\/api\/projections\/15-1252\?state=.*/i, r => r.fulfill({ status: 200, contentType: 'application/json', body: j({ state: { area: 'Georgia', baseYear: 2022, projectedYear: 2032, base: 45000, projection: 52000, change: 7000, annualOpenings: 3800, occupationName: 'Software Developers' }, national: { area: 'United States', baseYear: 2022, projectedYear: 2032, base: 1630000, projection: 1810000, change: 180000, annualOpenings: 153000, occupationName: 'Software Developers' } }) })); // ── Navigate to roadmap await page.goto(`/career-roadmap/${career_profile_id}`, { waitUntil: 'domcontentloaded' }); // 1) Coach UI mounted (intro text is async; don’t assert it) await expect(page.getByRole('button', { name: 'Networking Plan' })).toBeVisible({ timeout: 6000 }); await expect(page.getByRole('button', { name: 'Job-Search Plan' })).toBeVisible({ timeout: 6000 }); await expect(page.getByRole('button', { name: 'Interview Help' })).toBeVisible({ timeout: 6000 }); await expect(page.getByRole('button', { name: /Grow Career with AI/i })).toBeVisible({ timeout: 6000 }); await expect(page.getByRole('button', { name: /Edit Goals/i })).toBeVisible({ timeout: 6000 }); const sendBtn = page.getByRole('button', { name: /^Send$/ }); await expect(sendBtn).toBeVisible({ timeout: 6000 }); const coachInput = sendBtn.locator('..').getByRole('textbox').first(); await expect(coachInput).toBeVisible({ timeout: 6000 }); // 2) Context section + target career await expect(page.getByRole('heading', { name: /Where you are now and where you are going/i })) .toBeVisible({ timeout: 6000 }); await expect(page.getByText(/Target Career:\s*Software Developers/i)).toBeVisible({ timeout: 6000 }); // 3) Salary panels — scope to each card to avoid duplicate “Median $…” const regionalHeader = page.getByText(/Regional Salary Data/i).first(); await expect(regionalHeader).toBeVisible({ timeout: 6000 }); const regionalCard = regionalHeader.locator('..'); await expect(regionalCard.getByText(/Median:\s*\$100,000/i)).toBeVisible({ timeout: 6000 }); const nationalHeader = page.getByText(/National Salary Data/i).first(); await expect(nationalHeader).toBeVisible({ timeout: 6000 }); const nationalCard = nationalHeader.locator('..'); await expect(nationalCard.getByText(/Median:\s*\$98,000/i)).toBeVisible({ timeout: 6000 }); // 4) Economic projections — two bars visible await expect(page.getByText(/^Georgia$/)).toBeVisible({ timeout: 6000 }); await expect(page.getByText(/^United States$/)).toBeVisible({ timeout: 6000 }); // 5) Financial Projection — header + the chart’s canvas in that section const fpHeader = page.getByRole('heading', { name: /Financial Projection/i }).first(); await fpHeader.scrollIntoViewIfNeeded(); // ensure below-the-fold section is in view await expect(fpHeader).toBeVisible({ timeout: 6000 }); // Nearest card container → canvas inside (ChartJS) const fpCard = fpHeader.locator('xpath=ancestor::div[contains(@class,"bg-white")][1]'); // Give a moment for projection build after college-profile resolves await expect.poll(async () => await fpCard.locator('canvas').count(), { timeout: 9000 }).toBeGreaterThan(0); await expect(fpCard.locator('canvas').first()).toBeVisible({ timeout: 6000 }); // 6) Milestones header visible (scroll + relaxed name to allow tooltip text) const msHeader = page.getByRole('heading', { name: /Milestones/i }).first(); await msHeader.scrollIntoViewIfNeeded(); await expect(msHeader).toBeVisible({ timeout: 6000 }); }); });