dev1/tests/e2e/46-profile-editors.spec.mjs

240 lines
9.3 KiB
JavaScript
Raw 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-check
import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p1 Profile Editors — Career, Financial, and College', () => {
test.setTimeout(90_000);
test.beforeEach(async ({ page }) => {
const u = loadTestUser();
// Premium gate that App.js / PremiumRoute actually uses
await page.route(
/\/api\/user-profile\?fields=.*(firstname|is_premium|is_pro_premium).*/i,
async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ firstname: 'Tester', is_premium: 1, is_pro_premium: 0 })
});
}
);
// Real sign-in (single session per test)
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 });
});
// -------- Career profile (edit) --------
test('CareerProfileList → edit first profile title and save', async ({ page }) => {
// Ensure at least one
await page.request.post('/api/premium/career-profile', {
data: { career_name: 'QA Scenario', status: 'planned', start_date: '2025-09-01' }
});
await page.goto('/profile/careers', { waitUntil: 'domcontentloaded' });
await expect(page.getByRole('heading', { name: 'Career Profiles' })).toBeVisible();
const editLinks = page.locator('a', { hasText: /^edit$/i });
await editLinks.first().click();
await expect(page.getByRole('heading', { name: /Edit Career Profile|New Career Profile/i }))
.toBeVisible({ timeout: 15_000 });
const title = page.getByLabel(/Scenario Title/i);
const prev = await title.inputValue().catch(() => 'Scenario');
await title.fill((prev || 'Scenario') + ' — test');
await page.getByRole('button', { name: /^Save$/ }).click();
await expect(page).toHaveURL(/\/career-roadmap\/[a-z0-9-]+$/i, { timeout: 20_000 });
});
// -------- Financial profile (save) --------
test('FinancialProfileForm saves and returns', async ({ page }) => {
await page.goto('/profile/financial', { waitUntil: 'domcontentloaded' });
await expect(page.getByRole('heading', { name: /Financial Profile/i })).toBeVisible({ timeout: 10_000 });
await page.getByLabel(/Current.*Annual.*Salary/i).fill('55000');
await page.getByLabel(/Monthly.*Living.*Expenses/i).fill('1800');
await page.getByLabel(/Monthly.*Debt.*Payments/i).fill('200');
await page.getByLabel(/To.*Emergency.*\(%\)/i).fill('40'); // 60 to retirement auto-computed
await page.getByRole('button', { name: /^Save$/ }).click();
await expect(page).not.toHaveURL(/\/profile\/financial$/i, { timeout: 15_000 });
});
// Helper: commit autosuggest that sometimes needs two selects
async function commitAutosuggest(page, input, text) {
await input.click();
await input.fill(text);
await page.keyboard.press('ArrowDown').catch(() => {});
await page.keyboard.press('Enter').catch(() => {});
await input.blur();
const v1 = (await input.inputValue()).trim();
if (v1.toLowerCase() === text.toLowerCase()) {
// Some nested lists require a second confirmation
await input.click();
await page.keyboard.press('ArrowDown').catch(() => {});
await page.keyboard.press('Enter').catch(() => {});
await input.blur();
}
}
// ---- College helpers (put near the top of the file) ----
async function commitAutosuggest(page, input, text) {
// Type and try first commit
await input.click();
await input.fill(text);
await page.keyboard.press('ArrowDown').catch(() => {});
await page.keyboard.press('Enter').catch(() => {});
await input.blur();
// If typed value still equals exactly what we wrote, some lists need a second confirm
const v1 = (await input.inputValue()).trim();
if (v1.toLowerCase() === text.toLowerCase()) {
await input.click();
await page.keyboard.press('ArrowDown').catch(() => {});
await page.keyboard.press('Enter').catch(() => {});
await input.blur();
}
}
async function pickDegree(page) {
const select = page.getByLabel(/Degree Type/i);
try {
await select.selectOption({ label: "Bachelor's Degree" });
return;
} catch {}
const second = select.locator('option').nth(1); // skip placeholder
await second.waitFor({ state: 'attached', timeout: 10_000 });
const val = await second.getAttribute('value');
if (val) await select.selectOption(val);
}
// Known-good school/program pair from your dataset
const SCHOOL = 'Alabama A & M University';
const PROGRAM = 'Agriculture, General.';
// ---- CollegeProfileForm — create NEW plan (autosuggests must appear, then save) ----
test('CollegeProfileForm — create new plan (autosuggests + degree + save)', async ({ page, request }) => {
// Fail the test if any unexpected alert shows
page.on('dialog', d => {
throw new Error(`Unexpected dialog: "${d.message()}"`);
});
// Create a scenario to attach the college plan
const scen = await request.post('/api/premium/career-profile', {
data: { career_name: 'QA College Plan (new)', status: 'planned', start_date: '2025-09-01' }
});
const { career_profile_id } = await scen.json();
await page.goto(`/profile/college/${career_profile_id}/new`, { waitUntil: 'domcontentloaded' });
await expect(page.getByRole('heading', { name: /College Plan|Edit College Plan|New College/i }))
.toBeVisible({ timeout: 15_000 });
const SCHOOL = 'Alabama A & M University';
const PROGRAM = 'Agriculture, General.';
const schoolBox = page.getByLabel(/School Name/i);
const programBox = page.getByLabel(/Major.*Program.*Name/i);
// Type partial and assert autosuggest options exist (prove the dropdown is presented)
await schoolBox.fill('Alabama A &');
await expect.poll(async () => {
return await page.locator('#school-suggestions option').count();
}, { timeout: 5000 }).toBeGreaterThan(0);
// Commit school programmatically with double-confirm helper
await commitAutosuggest(page, schoolBox, SCHOOL);
// Program autosuggest must be present too
await programBox.fill('Agri');
await expect.poll(async () => {
return await page.locator('#school-suggestions option').count();
}, { timeout: 5000 }).toBeGreaterThan(0);
// Commit program
await commitAutosuggest(page, programBox, PROGRAM);
// Pick degree + set expected graduation
await pickDegree(page);
const grad = page.getByLabel(/Expected Graduation Date/i);
if (await grad.isVisible().catch(() => false)) await grad.fill('2027-06-01');
// Save
await page.getByRole('button', { name: /^Save$/i }).click();
// Should not remain stuck on /new (and no alerts were raised)
await expect(page).not.toHaveURL(/\/new$/i, { timeout: 10_000 });
});
// ---- CollegeProfileForm — EDIT existing plan (autosuggests + degree + save) ----
test('CollegeProfileForm — edit existing plan (autosuggests + degree + save)', async ({ page, request }) => {
// Fail the test if any unexpected alert shows
page.on('dialog', d => {
throw new Error(`Unexpected dialog: "${d.message()}"`);
});
// Create a scenario and seed a minimal college plan
const scen = await request.post('/api/premium/career-profile', {
data: { career_name: 'QA College Plan (edit)', status: 'planned', start_date: '2025-09-01' }
});
const { career_profile_id } = await scen.json();
const SCHOOL = 'Alabama A & M University';
const PROGRAM = 'Agriculture, General.';
await request.post('/api/premium/college-profile', {
data: {
career_profile_id,
selected_school: SCHOOL,
selected_program: PROGRAM,
program_type: "Bachelor's Degree",
expected_graduation: '2027-06-01',
academic_calendar: 'semester',
credit_hours_per_year: 30,
interest_rate: 5.5,
loan_term: 10
}
});
await page.goto(`/profile/college/${career_profile_id}/edit`, { waitUntil: 'domcontentloaded' });
await expect(page.getByRole('heading', { name: /College Plan|Edit College Plan/i }))
.toBeVisible({ timeout: 15_000 });
const schoolBox = page.getByLabel(/School Name/i);
const programBox = page.getByLabel(/Major.*Program.*Name/i);
// Assert autosuggest presents options on partial typing
await schoolBox.fill('Alabama A &');
await expect.poll(async () => {
return await page.locator('#school-suggestions option').count();
}, { timeout: 5000 }).toBeGreaterThan(0);
// Recommit the same school/program (exercise autosuggest again)
await commitAutosuggest(page, schoolBox, SCHOOL);
await programBox.fill('Agri');
await expect.poll(async () => {
return await page.locator('#school-suggestions option').count();
}, { timeout: 5000 }).toBeGreaterThan(0);
await commitAutosuggest(page, programBox, PROGRAM);
await pickDegree(page);
const grad = page.getByLabel(/Expected Graduation Date/i);
if (await grad.isVisible().catch(() => false)) await grad.fill('2028-05-01');
await page.getByRole('button', { name: /^Save$/i }).click();
// No error popup was allowed; we just assert were not showing the error text
await expect(page.getByText(/Please pick a school from the list/i))
.toHaveCount(0, { timeout: 2000 })
.catch(() => {});
});
});