173 lines
7.4 KiB
JavaScript
173 lines
7.4 KiB
JavaScript
// @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 });
|
||
});
|
||
});
|