dev1/tests/e2e/47-career-roadmap-and-coach.spec.mjs
2025-09-18 13:26:16 +00:00

173 lines
7.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @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; dont 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 charts 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 });
});
});