dev1/tests/e2e/45a-career-profile.spec.mjs

167 lines
7.1 KiB
JavaScript

// @ts-check
import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
// Grab the first input immediately following a label that matches text
// Use stable name-based locators (matches your component code)
const byName = (page, name) => page.locator(`input[name="${name}"]`).first();
// Fill a date input reliably (type=date => yyyy-mm-dd; else mm/dd/yyyy)
async function fillDateField(ctrl, iso = '2025-09-01', us = '09/01/2025') {
const type = (await ctrl.getAttribute('type')) || '';
if (type.toLowerCase() === 'date') {
await ctrl.fill(iso); // browser renders mm/dd/yyyy
} else {
await ctrl.scrollIntoViewIfNeeded();
await ctrl.click({ force: true });
await ctrl.fill('');
await ctrl.type(us, { delay: 15 });
}
}
test.describe('@p1 Profile — Career editor (44a)', () => {
test.setTimeout(20000);
test('Create new career from list → save → roadmap, then edit title', async ({ page }) => {
const u = loadTestUser();
// Premium gate
await page.route(
/\/api\/user-profile\?fields=.*(firstname|is_premium|is_pro_premium).*/i,
r => r.fulfill({ status: 200, contentType: 'application/json',
body: JSON.stringify({ firstname: 'Tester', is_premium: 1, is_pro_premium: 0 }) })
);
// Stub /api/careers/search to return a real career from your dataset (Compliance Managers)
await page.route(/\/api\/careers\/search\?*/i, async route => {
const url = new URL(route.request().url());
const q = (url.searchParams.get('query') || '').toLowerCase();
const list = q.includes('compliance')
? [{ title: 'Compliance Managers', soc_code: '11-9199.02', cip_codes: ['52.0304'] }]
: [];
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(list)
});
});
// List fetch on /profile/careers (allow real backend later, so don't pin to [])
await page.route('**/api/premium/career-profile/all', route => route.fallback());
// Accept POST save (UI create flow)
let createdId = null;
await page.route('**/api/premium/career-profile', async route => {
if (route.request().method() === 'POST') {
createdId = crypto.randomUUID();
await route.fulfill({
status: 200, contentType: 'application/json',
body: JSON.stringify({ career_profile_id: createdId })
});
} else {
await route.fallback();
}
});
// Sign in
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 });
// Go to list and click + New Career Profile
await page.goto('/profile/careers', { waitUntil: 'domcontentloaded' });
await page.getByRole('button', { name: '+ New Career Profile' }).click();
await page.waitForURL('**/profile/careers/new/edit', { timeout: 15000 });
await expect(page.getByRole('heading', { name: /^New Career Profile$/i }))
.toBeVisible({ timeout: 15000 });
await expect(page.locator('input[name="start_date"]').first())
.toBeVisible({ timeout: 15000 });
// Choose career (exact placeholder in new-form)
const search = page.getByPlaceholder('Start typing a career...', { exact: true });
await expect(search).toBeVisible({ timeout: 5000 });
await search.click();
const waitSearch = page.waitForResponse(r => /\/api\/careers\/search/i.test(r.url()));
await search.fill('Compliance Managers');
await waitSearch;
await page.keyboard.press('Enter'); // commit
await expect(page.locator('input[disabled]')).toHaveValue('Compliance Managers', { timeout: 3000 });
// Save → roadmap
await page.getByRole('button', { name: /^Save$/ }).click();
await expect(page).toHaveURL(/\/career-roadmap\/[a-z0-9-]+$/i, { timeout: 25000 });
// Navigate to edit that same profile and tweak title (proves list→edit wiring)
// Ensure future requests use real backend
await page.unroute('**/api/premium/career-profile/all').catch(() => {});
await page.goto('/profile/careers', { waitUntil: 'domcontentloaded' });
// wait for at least one row to appear then click its edit link
await expect.poll(async () => await page.getByRole('link', { name: /^edit$/i }).count(), { timeout: 5000 }).toBeGreaterThan(0);
await page.getByRole('link', { name: /^edit$/i }).first().click();
});
test('Edit title and save → roadmap', async ({ page, request }) => {
const u = loadTestUser();
// Stub the GET that edit page calls so fields populate deterministically
await page.route('**/api/premium/career-profile/*', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
scenario_title: 'Existing Scenario',
career_name: 'Data Analyst',
soc_code: '15-2051.00',
status: 'current',
start_date: '2025-09-01',
retirement_start_date: null,
college_enrollment_status: '',
career_goals: '',
desired_retirement_income_monthly: ''
})
});
});
// PremiumRoute gate
await page.route(
/\/api\/user-profile\?fields=.*(firstname|is_premium|is_pro_premium).*/i,
r => r.fulfill({ status: 200, contentType: 'application/json',
body: JSON.stringify({ firstname: 'Tester', is_premium: 1, is_pro_premium: 0 }) })
);
// Sign in
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: 15_000 });
// Seed a profile via backend, then go directly to its edit page
const resp = await request.post('/api/premium/career-profile', {
data: { career_name: 'Data Analyst', status: 'planned', start_date: '2025-09-01' }
});
const { career_profile_id } = await resp.json();
await page.goto(`/profile/careers/${career_profile_id}/edit`, { waitUntil: 'domcontentloaded' });
await expect(page.getByRole('heading', { name: /Edit Career Profile/i })).toBeVisible({ timeout: 15_000 });
// Title
const title = byName(page, 'scenario_title');
const prev = (await title.inputValue().catch(() => 'Scenario')) || 'Scenario';
await title.fill(prev + ' — test');
// Start date (supports type=date or text mm/dd/yyyy)
const start = byName(page, 'start_date');
if ((await start.count()) > 0) {
await fillDateField(start, '2025-09-01', '09/01/2025');
}
await page.getByRole('button', { name: /^Save$/ }).click();
await expect(page).toHaveURL(/\/career-roadmap\/[a-z0-9-]+$/i, { timeout: 25_000 });
});
});