diff --git a/.build.hash b/.build.hash
index 51c0d17..2d5e863 100644
--- a/.build.hash
+++ b/.build.hash
@@ -1 +1 @@
-24c4644c626acf48ddca3964105cd9bfa267d82a-372bcf506971f56c4911b429b9f5de5bc37ed008-e9eccd451b778829eb2f2c9752c670b707e1268b
+720d57c0d6d787c629f53d47689088a50b9e9b5b-372bcf506971f56c4911b429b9f5de5bc37ed008-e9eccd451b778829eb2f2c9752c670b707e1268b
diff --git a/playwright-report/index.html b/playwright-report/index.html
deleted file mode 100644
index ded81cc..0000000
--- a/playwright-report/index.html
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
-
-
-
-
- Playwright Test Report
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playwright.config.mjs b/playwright.config.mjs
index 41dc5a5..0742ebd 100644
--- a/playwright.config.mjs
+++ b/playwright.config.mjs
@@ -1,15 +1,27 @@
- import { defineConfig } from '@playwright/test';
-
+import { defineConfig, devices } from '@playwright/test';
+
export default defineConfig({
// Limit Playwright to E2E specs only
testDir: '/home/jcoakley/aptiva-dev1-app/tests/e2e',
testMatch: /.*\.spec\.(?:mjs|js|ts)$/,
- use: {
- baseURL: process.env.PW_BASE_URL || 'http://localhost:3000',
- trace: 'on-first-retry',
- screenshot: 'only-on-failure',
- video: 'retain-on-failure',
- },
- retries: 1,
- reporter: [['list'], ['html', { open: 'never' }]],
- });
\ No newline at end of file
+
+ use: {
+ baseURL: process.env.PW_BASE_URL || 'http://localhost:3000',
+ trace: 'on-first-retry',
+ screenshot: 'only-on-failure',
+ video: 'retain-on-failure',
+ },
+
+ retries: 0,
+ // Add 'blob' so Playwright persists failures for --last-failed
+ reporter: [['list'], ['html', { open: 'never' }], ['blob']],
+
+ // Make Edge the default (you asked earlier)
+ projects: [
+ {
+ name: 'edge',
+ use: { ...devices['Desktop Edge'] },
+ },
+ ],
+ });
+
diff --git a/src/components/CollegeProfileForm.js b/src/components/CollegeProfileForm.js
index 18da6a7..a41f801 100644
--- a/src/components/CollegeProfileForm.js
+++ b/src/components/CollegeProfileForm.js
@@ -2,6 +2,8 @@ import React, { useEffect, useState, useRef } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import apiFetch from '../auth/apiFetch.js';
import moment from 'moment/moment.js';
+import Modal from './ui/modal.js';
+import FinancialAidWizard from './FinancialAidWizard.js';
const authFetch = apiFetch; // keep local name, new implementation
/** -----------------------------------------------------------
@@ -62,9 +64,11 @@ export default function CollegeProfileForm() {
const [graduationTouched, setGraduationTouched] = useState(false);
const [programLengthTouched, setProgramLengthTouched] = useState(false);
const [selectedUnitId, setSelectedUnitId] = useState(null);
+ const [showAidWizard, setShowAidWizard] = useState(false);
const schoolPrevRef = useRef('');
const programPrevRef = useRef('');
const lastSchoolText = useRef('');
+ const canonicalSchoolName = useRef(''); // the exact, server-known school name for selectedUnitId
const [form, setForm] = useState({
@@ -130,7 +134,10 @@ const onSchoolInput = async (e) => {
selected_program: value ? prev.selected_program : '', // clear if user erased school
program_type: ''
}));
- lastSchoolText.current = value;
+ setSelectedUnitId(null);
+ canonicalSchoolName.current = '';
+ setSchoolValid(value.trim() === ''); // empty = neutral, any text = invalid until validated
+ lastSchoolText.current = value;
}
if (!value.trim()) { setSchoolSug([]); schoolPrevRef.current = ''; return; }
const it = e?.nativeEvent?.inputType; // 'insertReplacementText' on datalist pick
@@ -199,8 +206,10 @@ const onProgramInput = async (e) => {
})();
}, [form.selected_school, form.selected_program]);
- // When selected_school changes (after commit/blur), reset program suggestions/types
useEffect(() => {
+ // Only clear when the change came from the user's typing (onSchoolInput sets lastSchoolText)
+ const typed = (form.selected_school || '').trim() === (lastSchoolText.current || '').trim();
+ if (!typed) return; // programmatic changes (initial load, API normalization) → keep UNITID & types
setProgSug([]);
setTypes([]);
setSelectedUnitId(null);
@@ -228,7 +237,8 @@ const onProgramInput = async (e) => {
const n = Number(normalized.tuition);
setAutoTuition(Number.isFinite(n) ? n : 0);
}
- if (normalized.unit_id) setSelectedUnitId(normalized.unit_id);
+ if (normalized.unit_id) setSelectedUnitId(normalized.unit_id);
+ if (normalized.selected_school) canonicalSchoolName.current = normalized.selected_school;
// If profile came with school+program, load types so Degree Type select is populated
if ((normalized.selected_school || '') && (normalized.selected_program || '')) {
try {
@@ -245,50 +255,104 @@ const onProgramInput = async (e) => {
}, [careerId, id]);
- async function handleSave(){
- try{
-
- // Compute chosen tuition exactly like Onboarding (manual override wins; blank => auto)
+ async function handleSave() {
+ try {
+ // Compute chosen tuition exactly like Onboarding (manual override wins; blank => auto)
const chosenTuition =
(manualTuition.trim() === '')
? autoTuition
: (Number.isFinite(parseFloat(manualTuition)) ? parseFloat(manualTuition) : autoTuition);
- // Confirm user actually picked from list (one alert, on Save only)
- const school = (form.selected_school || '').trim().toLowerCase();
- const prog = (form.selected_program || '').trim().toLowerCase();
- // validate against current server suggestions (not local files)
- const exactSchool = school && schoolSug.find(o =>
- (o.name || '').toLowerCase() === school
- );
+ const schoolText = (form.selected_school || '').trim();
+ const progText = (form.selected_program || '').trim();
+ const school = schoolText.toLowerCase();
+ const prog = progText.toLowerCase();
- if (school && !exactSchool) {
- setSchoolValid(false);
- alert('Please pick a school from the list.');
- return;
- }
- const exactProgram = prog && progSug.find(p =>
- (p.program || '').toLowerCase() === prog
- );
- if (prog && !exactProgram) {
- setProgramValid(false);
- alert('Please pick a program from the list.');
- return;
- }
- const body = normalisePayload({ ...form, tuition: chosenTuition, career_profile_id: careerId, unit_id: selectedUnitId ?? null });
- const res = await authFetch('/api/premium/college-profile',{
- method:'POST',
- headers:{'Content-Type':'application/json'},
- body:JSON.stringify(body)
- });
- if(!res.ok) throw new Error(await res.text());
- alert('Saved!');
- setForm(p => ({ ...p, tuition: chosenTuition }));
- setManualTuition(String(chosenTuition));
- nav(-1);
- }catch(err){ console.error(err); alert(err.message);}
+ // ---- SCHOOL validation ----
+const exactSchoolLocal = school && schoolSug.find(o => (o.name || '').toLowerCase() === school);
+const hasCanonical = !!selectedUnitId &&
+ canonicalSchoolName.current &&
+ canonicalSchoolName.current.toLowerCase() === school;
+
+let exactSchool = exactSchoolLocal;
+if (school && !exactSchool && !hasCanonical) {
+ // one-shot server confirm
+ try {
+ const resp = await authFetch(`/api/schools/suggest?query=${encodeURIComponent(schoolText)}&limit=50`);
+ const arr = resp.ok ? await resp.json() : [];
+ exactSchool = Array.isArray(arr)
+ ? arr.find(o => (o.name || '').toLowerCase() === school)
+ : null;
+ if (exactSchool && !selectedUnitId) {
+ setSelectedUnitId(exactSchool.unitId ?? null);
+ canonicalSchoolName.current = exactSchool.name || schoolText;
+ }
+ } catch {}
}
+if (school && !exactSchool && !hasCanonical) {
+ setSchoolValid(false);
+ alert('Please pick a school from the list.');
+ return;
+}
+
+
+ // ---- PROGRAM validation ----
+const progIsEmpty = progText === '';
+if (progIsEmpty) {
+ setProgramValid(false);
+ alert('Please pick a program from the list.');
+ return;
+}
+
+let exactProgram = prog && progSug.find(p => (p.program || '').toLowerCase() === prog);
+
+// One-shot server confirm if suggestions are empty/stale
+if (!exactProgram) {
+ try {
+ const resp = await authFetch(
+ `/api/programs/suggest?school=${encodeURIComponent(schoolText)}&query=${encodeURIComponent(progText)}&limit=50`
+ );
+ const arr = resp.ok ? await resp.json() : [];
+ exactProgram = Array.isArray(arr)
+ ? arr.find(p => (p.program || '').toLowerCase() === prog)
+ : null;
+ } catch {}
+}
+
+if (!exactProgram) {
+ setProgramValid(false);
+ alert('Please pick a program from the list.');
+ return;
+}
+
+
+ // ---- SAVE ----
+ const body = normalisePayload({
+ ...form,
+ tuition: chosenTuition,
+ career_profile_id: careerId,
+ unit_id: selectedUnitId ?? null
+ });
+
+ const res = await authFetch('/api/premium/college-profile', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(body)
+ });
+ if (!res.ok) throw new Error(await res.text());
+
+ alert('Saved!');
+ setForm(p => ({ ...p, tuition: chosenTuition }));
+ setManualTuition(String(chosenTuition));
+ nav(-1);
+ } catch (err) {
+ console.error(err);
+ alert(err.message);
+ }
+}
+
+
useEffect(() => {
const sch = (form.selected_school || '').trim();
@@ -329,7 +393,9 @@ const onProgramInput = async (e) => {
useEffect(() => {
(async () => {
const chpy = Number(form.credit_hours_per_year);
- if (!selectedUnitId ||
+ const hasCanon = canonicalSchoolName.current &&
+ canonicalSchoolName.current.toLowerCase() === (form.selected_school || '').trim().toLowerCase();
+ if (!selectedUnitId || !hasCanon ||
!form.program_type ||
!Number.isFinite(chpy) ||
chpy <= 0) {
@@ -355,6 +421,7 @@ useEffect(() => {
})();
}, [
selectedUnitId,
+ form.selected_school,
form.program_type,
form.credit_hours_per_year,
form.is_in_state,
@@ -482,9 +549,13 @@ return (
setForm(prev => ({ ...prev, selected_school: exact.name }));
}
if (!selectedUnitId) setSelectedUnitId(exact.unitId ?? null);
+ canonicalSchoolName.current = exact.name;
+ setSchoolValid(true);
+ return;
}
// Valid if empty (still choosing) OR exact chosen
- setSchoolValid(trimmed === '' || !!exact);
+ // Empty = neutral; any other text is invalid until committed from list
+ setSchoolValid(trimmed === '');
}}
list="school-suggestions"
className={`w-full border rounded p-2 ${
@@ -513,7 +584,7 @@ return (
if (exact && form.selected_program !== exact.program) {
setForm(prev => ({ ...prev, selected_program: exact.program }));
}
- setProgramValid(prog === '' || !!exact);
+ setProgramValid(prog === '' || !!exact);
}}
list="program-suggestions"
placeholder="Start typing and choose…"
@@ -639,14 +710,26 @@ return (
className="w-full border rounded p-2"
/>
- Annual Aid
-
+
+
+
+
+
+
+
{/* 8 │ Existing debt */}
@@ -746,15 +829,26 @@ return (
>
← Back
-
+
+ {showAidWizard && (
+ setShowAidWizard(false)}>
+ {
+ setForm(prev => ({ ...prev, annual_financial_aid: estimate }));
+ }}
+ onClose={() => setShowAidWizard(false)}
+ />
+
+ )}
+
);
diff --git a/test-results/.last-run.json b/test-results/.last-run.json
index 53f2505..8df0857 100644
--- a/test-results/.last-run.json
+++ b/test-results/.last-run.json
@@ -1,6 +1,21 @@
{
"status": "failed",
"failedTests": [
- "d94173b0fe5d7002a306-47f9b330456659f0f977"
+ "912b0a42e830d5eb471e-760b803445f71997ff15",
+ "adebddef88bcf3522d03-5564093ce53787bc37f1",
+ "e2a1f72bade9c08182fe-6f621548b19be1d1c340",
+ "04d7e1cfdd54807256b0-d6ea376eb6511af71058",
+ "31db8689401acd273032-cab17a91a741a429f82d",
+ "1c59337757c0db6c5b5a-c3a2d557647a05580ec2",
+ "929c2cc6ba4f564b24fc-946b201d1d2ce3bcc831",
+ "929c2cc6ba4f564b24fc-bb3dcb00b3273979b065",
+ "d94173b0fe5d7002a306-4787dc08bfe1459dba5b",
+ "a5366403b9bfbbbe283e-0f6aea13931c9f9dd89f",
+ "a5366403b9bfbbbe283e-8723b5b1a3f4093effb0",
+ "c167e95522508c1da576-f44184408ded1c898957",
+ "37ddad175c38e79b0f15-93462299db1b1756eedc",
+ "37ddad175c38e79b0f15-50f35c78cf5a1a8b2635",
+ "ed5b94c6fed68d1ded5e-79dac3b701e35033b644",
+ "a22878cb937d50857944-f3a10ac1ede1d0305bc5"
]
}
\ No newline at end of file
diff --git a/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/error-context.md b/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/error-context.md
new file mode 100644
index 0000000..6079791
--- /dev/null
+++ b/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/error-context.md
@@ -0,0 +1,253 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - navigation [ref=e6]:
+ - button "Find Your Career" [ref=e8] [cursor=pointer]
+ - button "Preparing & UpSkilling for Your Career" [ref=e10] [cursor=pointer]
+ - button "Enhancing Your Career(Premium)" [ref=e12] [cursor=pointer]:
+ - text: Enhancing Your Career
+ - generic [ref=e13] [cursor=pointer]: (Premium)
+ - button "Retirement Planning (beta)(Premium)" [ref=e15] [cursor=pointer]:
+ - text: Retirement Planning (beta)
+ - generic [ref=e16] [cursor=pointer]: (Premium)
+ - button "Profile" [ref=e18] [cursor=pointer]
+ - generic [ref=e19]:
+ - button "Upgrade to Premium" [ref=e20] [cursor=pointer]
+ - button "Support" [ref=e21] [cursor=pointer]
+ - button "Logout" [ref=e22] [cursor=pointer]
+ - main [ref=e23]:
+ - generic [ref=e24]:
+ - generic [ref=e25]:
+ - heading "Explore Careers - use these tools to find your best fit" [level=2] [ref=e26]
+ - generic [ref=e27]:
+ - generic [ref=e28]:
+ - text: Search for Career
+ - generic [ref=e29]: "*"
+ - combobox "Start typing a career..." [ref=e31]
+ - paragraph [ref=e32]: Please pick from the dropdown when performing search. Our database is very comprehensive but can’t accommodate every job title—choose the closest match to what you’re searching for.
+ - generic [ref=e33]:
+ - heading "Career Comparison" [level=2] [ref=e34]
+ - button "Edit priorities" [ref=e35] [cursor=pointer]
+ - paragraph [ref=e36]: No careers added to comparison.
+ - generic [ref=e37]:
+ - combobox [ref=e38]:
+ - option "All Preparation Levels" [selected]
+ - option "Little or No Preparation"
+ - option "Some Preparation Needed"
+ - option "Medium Preparation Needed"
+ - option "Considerable Preparation Needed"
+ - option "Extensive Preparation Needed"
+ - combobox [ref=e39]:
+ - option "All Fit Levels" [selected]
+ - option "Best - Very Strong Match"
+ - option "Great - Strong Match"
+ - option "Good - Less Strong Match"
+ - button "Reload Career Suggestions" [active] [ref=e40] [cursor=pointer]
+ - generic [ref=e41]:
+ - generic [ref=e42]: ⚠️
+ - generic [ref=e43]: = May have limited data for this career path
+ - generic [ref=e44]:
+ - button "Amusement & Recreation Attendants ⚠️" [ref=e45] [cursor=pointer]:
+ - generic [ref=e46] [cursor=pointer]: Amusement & Recreation Attendants
+ - generic [ref=e47] [cursor=pointer]: ⚠️
+ - button "Baristas ⚠️" [ref=e48] [cursor=pointer]:
+ - generic [ref=e49] [cursor=pointer]: Baristas
+ - generic [ref=e50] [cursor=pointer]: ⚠️
+ - button "Bus Drivers, School" [ref=e51] [cursor=pointer]:
+ - generic [ref=e52] [cursor=pointer]: Bus Drivers, School
+ - button "Childcare Workers" [ref=e53] [cursor=pointer]:
+ - generic [ref=e54] [cursor=pointer]: Childcare Workers
+ - button "Coaches & Scouts" [ref=e55] [cursor=pointer]:
+ - generic [ref=e56] [cursor=pointer]: Coaches & Scouts
+ - button "Concierges" [ref=e57] [cursor=pointer]:
+ - generic [ref=e58] [cursor=pointer]: Concierges
+ - button "Exercise Trainers & Group Fitness Instructors" [ref=e59] [cursor=pointer]:
+ - generic [ref=e60] [cursor=pointer]: Exercise Trainers & Group Fitness Instructors
+ - button "Food Servers, Nonrestaurant ⚠️" [ref=e61] [cursor=pointer]:
+ - generic [ref=e62] [cursor=pointer]: Food Servers, Nonrestaurant
+ - generic [ref=e63] [cursor=pointer]: ⚠️
+ - button "Funeral Attendants ⚠️" [ref=e64] [cursor=pointer]:
+ - generic [ref=e65] [cursor=pointer]: Funeral Attendants
+ - generic [ref=e66] [cursor=pointer]: ⚠️
+ - button "Home Health Aides ⚠️" [ref=e67] [cursor=pointer]:
+ - generic [ref=e68] [cursor=pointer]: Home Health Aides
+ - generic [ref=e69] [cursor=pointer]: ⚠️
+ - button "Hosts & Hostesses, Restaurant, Lounge, & Coffee Shop ⚠️" [ref=e70] [cursor=pointer]:
+ - generic [ref=e71] [cursor=pointer]: Hosts & Hostesses, Restaurant, Lounge, & Coffee Shop
+ - generic [ref=e72] [cursor=pointer]: ⚠️
+ - button "Locker Room, Coatroom, & Dressing Room Attendants ⚠️" [ref=e73] [cursor=pointer]:
+ - generic [ref=e74] [cursor=pointer]: Locker Room, Coatroom, & Dressing Room Attendants
+ - generic [ref=e75] [cursor=pointer]: ⚠️
+ - button "Nannies" [ref=e76] [cursor=pointer]:
+ - generic [ref=e77] [cursor=pointer]: Nannies
+ - button "Nursing Assistants" [ref=e78] [cursor=pointer]:
+ - generic [ref=e79] [cursor=pointer]: Nursing Assistants
+ - button "Occupational Therapy Aides" [ref=e80] [cursor=pointer]:
+ - generic [ref=e81] [cursor=pointer]: Occupational Therapy Aides
+ - button "Passenger Attendants ⚠️" [ref=e82] [cursor=pointer]:
+ - generic [ref=e83] [cursor=pointer]: Passenger Attendants
+ - generic [ref=e84] [cursor=pointer]: ⚠️
+ - button "Personal Care Aides ⚠️" [ref=e85] [cursor=pointer]:
+ - generic [ref=e86] [cursor=pointer]: Personal Care Aides
+ - generic [ref=e87] [cursor=pointer]: ⚠️
+ - button "Physical Therapist Aides" [ref=e88] [cursor=pointer]:
+ - generic [ref=e89] [cursor=pointer]: Physical Therapist Aides
+ - button "Recreation Workers" [ref=e90] [cursor=pointer]:
+ - generic [ref=e91] [cursor=pointer]: Recreation Workers
+ - button "Residential Advisors" [ref=e92] [cursor=pointer]:
+ - generic [ref=e93] [cursor=pointer]: Residential Advisors
+ - button "School Bus Monitors ⚠️" [ref=e94] [cursor=pointer]:
+ - generic [ref=e95] [cursor=pointer]: School Bus Monitors
+ - generic [ref=e96] [cursor=pointer]: ⚠️
+ - button "Substitute Teachers, Short-Term ⚠️" [ref=e97] [cursor=pointer]:
+ - generic [ref=e98] [cursor=pointer]: Substitute Teachers, Short-Term
+ - generic [ref=e99] [cursor=pointer]: ⚠️
+ - button "Teaching Assistants, Preschool, Elementary, Middle, & Secondary School ⚠️" [ref=e100] [cursor=pointer]:
+ - generic [ref=e101] [cursor=pointer]: Teaching Assistants, Preschool, Elementary, Middle, & Secondary School
+ - generic [ref=e102] [cursor=pointer]: ⚠️
+ - button "Teaching Assistants, Special Education ⚠️" [ref=e103] [cursor=pointer]:
+ - generic [ref=e104] [cursor=pointer]: Teaching Assistants, Special Education
+ - generic [ref=e105] [cursor=pointer]: ⚠️
+ - button "Tour Guides & Escorts ⚠️" [ref=e106] [cursor=pointer]:
+ - generic [ref=e107] [cursor=pointer]: Tour Guides & Escorts
+ - generic [ref=e108] [cursor=pointer]: ⚠️
+ - button "Ushers, Lobby Attendants, & Ticket Takers ⚠️" [ref=e109] [cursor=pointer]:
+ - generic [ref=e110] [cursor=pointer]: Ushers, Lobby Attendants, & Ticket Takers
+ - generic [ref=e111] [cursor=pointer]: ⚠️
+ - button "Waiters & Waitresses ⚠️" [ref=e112] [cursor=pointer]:
+ - generic [ref=e113] [cursor=pointer]: Waiters & Waitresses
+ - generic [ref=e114] [cursor=pointer]: ⚠️
+ - button "Adapted Physical Education Specialists" [ref=e115] [cursor=pointer]:
+ - generic [ref=e116] [cursor=pointer]: Adapted Physical Education Specialists
+ - button "Adult Basic Education, Adult Secondary Education, & English as a Second Language Instructors" [ref=e117] [cursor=pointer]:
+ - generic [ref=e118] [cursor=pointer]: Adult Basic Education, Adult Secondary Education, & English as a Second Language Instructors
+ - button "Athletes & Sports Competitors" [ref=e119] [cursor=pointer]:
+ - generic [ref=e120] [cursor=pointer]: Athletes & Sports Competitors
+ - button "Baggage Porters & Bellhops ⚠️" [ref=e121] [cursor=pointer]:
+ - generic [ref=e122] [cursor=pointer]: Baggage Porters & Bellhops
+ - generic [ref=e123] [cursor=pointer]: ⚠️
+ - button "Barbers" [ref=e124] [cursor=pointer]:
+ - generic [ref=e125] [cursor=pointer]: Barbers
+ - button "Bartenders" [ref=e126] [cursor=pointer]:
+ - generic [ref=e127] [cursor=pointer]: Bartenders
+ - button "Bus Drivers, Transit & Intercity" [ref=e128] [cursor=pointer]:
+ - generic [ref=e129] [cursor=pointer]: Bus Drivers, Transit & Intercity
+ - button "Career/Technical Education Teachers, Middle School" [ref=e130] [cursor=pointer]:
+ - generic [ref=e131] [cursor=pointer]: Career/Technical Education Teachers, Middle School
+ - button "Career/Technical Education Teachers, Secondary School" [ref=e132] [cursor=pointer]:
+ - generic [ref=e133] [cursor=pointer]: Career/Technical Education Teachers, Secondary School
+ - button "Clergy" [ref=e134] [cursor=pointer]:
+ - generic [ref=e135] [cursor=pointer]: Clergy
+ - button "Cooks, Private Household" [ref=e136] [cursor=pointer]:
+ - generic [ref=e137] [cursor=pointer]: Cooks, Private Household
+ - button "Correctional Officers & Jailers" [ref=e138] [cursor=pointer]:
+ - generic [ref=e139] [cursor=pointer]: Correctional Officers & Jailers
+ - button "Dietetic Technicians" [ref=e140] [cursor=pointer]:
+ - generic [ref=e141] [cursor=pointer]: Dietetic Technicians
+ - button "Dining Room & Cafeteria Attendants & Bartender Helpers ⚠️" [ref=e142] [cursor=pointer]:
+ - generic [ref=e143] [cursor=pointer]: Dining Room & Cafeteria Attendants & Bartender Helpers
+ - generic [ref=e144] [cursor=pointer]: ⚠️
+ - button "Elementary School Teachers" [ref=e145] [cursor=pointer]:
+ - generic [ref=e146] [cursor=pointer]: Elementary School Teachers
+ - button "Fast Food & Counter Workers ⚠️" [ref=e147] [cursor=pointer]:
+ - generic [ref=e148] [cursor=pointer]: Fast Food & Counter Workers
+ - generic [ref=e149] [cursor=pointer]: ⚠️
+ - button "Fitness & Wellness Coordinators" [ref=e150] [cursor=pointer]:
+ - generic [ref=e151] [cursor=pointer]: Fitness & Wellness Coordinators
+ - button "Flight Attendants" [ref=e152] [cursor=pointer]:
+ - generic [ref=e153] [cursor=pointer]: Flight Attendants
+ - button "Hairdressers, Hairstylists, & Cosmetologists" [ref=e154] [cursor=pointer]:
+ - generic [ref=e155] [cursor=pointer]: Hairdressers, Hairstylists, & Cosmetologists
+ - button "Hotel, Motel, & Resort Desk Clerks ⚠️" [ref=e156] [cursor=pointer]:
+ - generic [ref=e157] [cursor=pointer]: Hotel, Motel, & Resort Desk Clerks
+ - generic [ref=e158] [cursor=pointer]: ⚠️
+ - button "Kindergarten Teachers" [ref=e159] [cursor=pointer]:
+ - generic [ref=e160] [cursor=pointer]: Kindergarten Teachers
+ - button "Licensed Practical & Licensed Vocational Nurses" [ref=e161] [cursor=pointer]:
+ - generic [ref=e162] [cursor=pointer]: Licensed Practical & Licensed Vocational Nurses
+ - button "Middle School Teachers" [ref=e163] [cursor=pointer]:
+ - generic [ref=e164] [cursor=pointer]: Middle School Teachers
+ - button "Midwives" [ref=e165] [cursor=pointer]:
+ - generic [ref=e166] [cursor=pointer]: Midwives
+ - button "Morticians, Undertakers, & Funeral Arrangers" [ref=e167] [cursor=pointer]:
+ - generic [ref=e168] [cursor=pointer]: Morticians, Undertakers, & Funeral Arrangers
+ - button "Occupational Therapy Assistants" [ref=e169] [cursor=pointer]:
+ - generic [ref=e170] [cursor=pointer]: Occupational Therapy Assistants
+ - button "Orderlies ⚠️" [ref=e171] [cursor=pointer]:
+ - generic [ref=e172] [cursor=pointer]: Orderlies
+ - generic [ref=e173] [cursor=pointer]: ⚠️
+ - button "Physical Therapist Assistants" [ref=e174] [cursor=pointer]:
+ - generic [ref=e175] [cursor=pointer]: Physical Therapist Assistants
+ - button "Preschool Teachers" [ref=e176] [cursor=pointer]:
+ - generic [ref=e177] [cursor=pointer]: Preschool Teachers
+ - button "Psychiatric Aides" [ref=e178] [cursor=pointer]:
+ - generic [ref=e179] [cursor=pointer]: Psychiatric Aides
+ - button "Reservation & Transportation Ticket Agents & Travel Clerks ⚠️" [ref=e180] [cursor=pointer]:
+ - generic [ref=e181] [cursor=pointer]: Reservation & Transportation Ticket Agents & Travel Clerks
+ - generic [ref=e182] [cursor=pointer]: ⚠️
+ - button "Secondary School Teachers" [ref=e183] [cursor=pointer]:
+ - generic [ref=e184] [cursor=pointer]: Secondary School Teachers
+ - button "Self-Enrichment Teachers" [ref=e185] [cursor=pointer]:
+ - generic [ref=e186] [cursor=pointer]: Self-Enrichment Teachers
+ - button "Shampooers" [ref=e187] [cursor=pointer]:
+ - generic [ref=e188] [cursor=pointer]: Shampooers
+ - button "Skincare Specialists" [ref=e189] [cursor=pointer]:
+ - generic [ref=e190] [cursor=pointer]: Skincare Specialists
+ - button "Social & Human Service Assistants" [ref=e191] [cursor=pointer]:
+ - generic [ref=e192] [cursor=pointer]: Social & Human Service Assistants
+ - button "Teaching Assistants, Postsecondary" [ref=e193] [cursor=pointer]:
+ - generic [ref=e194] [cursor=pointer]: Teaching Assistants, Postsecondary
+ - button "Telephone Operators ⚠️" [ref=e195] [cursor=pointer]:
+ - generic [ref=e196] [cursor=pointer]: Telephone Operators
+ - generic [ref=e197] [cursor=pointer]: ⚠️
+ - button "Travel Guides ⚠️" [ref=e198] [cursor=pointer]:
+ - generic [ref=e199] [cursor=pointer]: Travel Guides
+ - generic [ref=e200] [cursor=pointer]: ⚠️
+ - button "Cooks, Fast Food ⚠️" [ref=e201] [cursor=pointer]:
+ - generic [ref=e202] [cursor=pointer]: Cooks, Fast Food
+ - generic [ref=e203] [cursor=pointer]: ⚠️
+ - button "Dishwashers ⚠️" [ref=e204] [cursor=pointer]:
+ - generic [ref=e205] [cursor=pointer]: Dishwashers
+ - generic [ref=e206] [cursor=pointer]: ⚠️
+ - button "Door-to-Door Sales Workers, News & Street Vendors, & Related Workers ⚠️" [ref=e207] [cursor=pointer]:
+ - generic [ref=e208] [cursor=pointer]: Door-to-Door Sales Workers, News & Street Vendors, & Related Workers
+ - generic [ref=e209] [cursor=pointer]: ⚠️
+ - button "Educational, Guidance, & Career Counselors & Advisors" [ref=e210] [cursor=pointer]:
+ - generic [ref=e211] [cursor=pointer]: Educational, Guidance, & Career Counselors & Advisors
+ - button "Helpers--Painters, Paperhangers, Plasterers, & Stucco Masons ⚠️" [ref=e212] [cursor=pointer]:
+ - generic [ref=e213] [cursor=pointer]: Helpers--Painters, Paperhangers, Plasterers, & Stucco Masons
+ - generic [ref=e214] [cursor=pointer]: ⚠️
+ - button "Hospitalists" [ref=e215] [cursor=pointer]:
+ - generic [ref=e216] [cursor=pointer]: Hospitalists
+ - button "Low Vision Therapists, Orientation & Mobility Specialists, & Vision Rehabilitation Therapists" [ref=e217] [cursor=pointer]:
+ - generic [ref=e218] [cursor=pointer]: Low Vision Therapists, Orientation & Mobility Specialists, & Vision Rehabilitation Therapists
+ - button "Maids & Housekeeping Cleaners ⚠️" [ref=e219] [cursor=pointer]:
+ - generic [ref=e220] [cursor=pointer]: Maids & Housekeeping Cleaners
+ - generic [ref=e221] [cursor=pointer]: ⚠️
+ - button "Nurse Midwives" [ref=e222] [cursor=pointer]:
+ - generic [ref=e223] [cursor=pointer]: Nurse Midwives
+ - button "Special Education Teachers, Preschool" [ref=e224] [cursor=pointer]:
+ - generic [ref=e225] [cursor=pointer]: Special Education Teachers, Preschool
+ - button "Substance Abuse & Behavioral Disorder Counselors ⚠️" [ref=e226] [cursor=pointer]:
+ - generic [ref=e227] [cursor=pointer]: Substance Abuse & Behavioral Disorder Counselors
+ - generic [ref=e228] [cursor=pointer]: ⚠️
+ - generic [ref=e229]:
+ - text: This page includes information from
+ - link "O*NET OnLine" [ref=e230] [cursor=pointer]:
+ - /url: https://www.onetcenter.org
+ - text: by the U.S. Department of Labor, Employment & Training Administration (USDOL/ETA). Used under the
+ - link "CC BY 4.0 license" [ref=e231] [cursor=pointer]:
+ - /url: https://creativecommons.org/licenses/by/4.0/
+ - text: . **O*NET®** is a trademark of USDOL/ETA. Salary and employment data are enriched with resources from the
+ - link "Bureau of Labor Statistics" [ref=e232] [cursor=pointer]:
+ - /url: https://www.bls.gov
+ - text: and program information from the
+ - link "National Center for Education Statistics" [ref=e233] [cursor=pointer]:
+ - /url: https://nces.ed.gov
+ - text: .
+ - button "Open chat" [ref=e234] [cursor=pointer]:
+ - img [ref=e235] [cursor=pointer]
+```
\ No newline at end of file
diff --git a/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/test-failed-1.png b/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/test-failed-1.png
new file mode 100644
index 0000000..766cf40
Binary files /dev/null and b/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/test-failed-1.png differ
diff --git a/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/video.webm b/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/video.webm
new file mode 100644
index 0000000..48d5601
Binary files /dev/null and b/test-results/04-career-explorer.core--p-126be-to-comparison-event-bridge--edge/video.webm differ
diff --git a/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/error-context.md b/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/error-context.md
new file mode 100644
index 0000000..a67c01b
--- /dev/null
+++ b/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/error-context.md
@@ -0,0 +1,253 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - navigation [ref=e6]:
+ - button "Find Your Career" [ref=e8] [cursor=pointer]
+ - button "Preparing & UpSkilling for Your Career" [ref=e10] [cursor=pointer]
+ - button "Enhancing Your Career(Premium)" [ref=e12] [cursor=pointer]:
+ - text: Enhancing Your Career
+ - generic [ref=e13] [cursor=pointer]: (Premium)
+ - button "Retirement Planning (beta)(Premium)" [ref=e15] [cursor=pointer]:
+ - text: Retirement Planning (beta)
+ - generic [ref=e16] [cursor=pointer]: (Premium)
+ - button "Profile" [ref=e18] [cursor=pointer]
+ - generic [ref=e19]:
+ - button "Upgrade to Premium" [ref=e20] [cursor=pointer]
+ - button "Support" [ref=e21] [cursor=pointer]
+ - button "Logout" [ref=e22] [cursor=pointer]
+ - main [ref=e23]:
+ - generic [ref=e24]:
+ - generic [ref=e25]:
+ - heading "Explore Careers - use these tools to find your best fit" [level=2] [ref=e26]
+ - generic [ref=e27]:
+ - generic [ref=e28]:
+ - text: Search for Career
+ - generic [ref=e29]: "*"
+ - combobox "Start typing a career..." [ref=e31]
+ - paragraph [ref=e32]: Please pick from the dropdown when performing search. Our database is very comprehensive but can’t accommodate every job title—choose the closest match to what you’re searching for.
+ - generic [ref=e33]:
+ - heading "Career Comparison" [level=2] [ref=e34]
+ - button "Edit priorities" [ref=e35] [cursor=pointer]
+ - paragraph [ref=e36]: No careers added to comparison.
+ - generic [ref=e37]:
+ - combobox [ref=e38]:
+ - option "All Preparation Levels" [selected]
+ - option "Little or No Preparation"
+ - option "Some Preparation Needed"
+ - option "Medium Preparation Needed"
+ - option "Considerable Preparation Needed"
+ - option "Extensive Preparation Needed"
+ - combobox [ref=e39]:
+ - option "All Fit Levels" [selected]
+ - option "Best - Very Strong Match"
+ - option "Great - Strong Match"
+ - option "Good - Less Strong Match"
+ - button "Reload Career Suggestions" [ref=e40] [cursor=pointer]
+ - generic [ref=e41]:
+ - generic [ref=e42]: ⚠️
+ - generic [ref=e43]: = May have limited data for this career path
+ - generic [ref=e44]:
+ - button "Amusement & Recreation Attendants ⚠️" [ref=e45] [cursor=pointer]:
+ - generic [ref=e46] [cursor=pointer]: Amusement & Recreation Attendants
+ - generic [ref=e47] [cursor=pointer]: ⚠️
+ - button "Baristas ⚠️" [ref=e48] [cursor=pointer]:
+ - generic [ref=e49] [cursor=pointer]: Baristas
+ - generic [ref=e50] [cursor=pointer]: ⚠️
+ - button "Bus Drivers, School" [ref=e51] [cursor=pointer]:
+ - generic [ref=e52] [cursor=pointer]: Bus Drivers, School
+ - button "Childcare Workers" [ref=e53] [cursor=pointer]:
+ - generic [ref=e54] [cursor=pointer]: Childcare Workers
+ - button "Coaches & Scouts" [ref=e55] [cursor=pointer]:
+ - generic [ref=e56] [cursor=pointer]: Coaches & Scouts
+ - button "Concierges" [ref=e57] [cursor=pointer]:
+ - generic [ref=e58] [cursor=pointer]: Concierges
+ - button "Exercise Trainers & Group Fitness Instructors" [ref=e59] [cursor=pointer]:
+ - generic [ref=e60] [cursor=pointer]: Exercise Trainers & Group Fitness Instructors
+ - button "Food Servers, Nonrestaurant ⚠️" [ref=e61] [cursor=pointer]:
+ - generic [ref=e62] [cursor=pointer]: Food Servers, Nonrestaurant
+ - generic [ref=e63] [cursor=pointer]: ⚠️
+ - button "Funeral Attendants ⚠️" [ref=e64] [cursor=pointer]:
+ - generic [ref=e65] [cursor=pointer]: Funeral Attendants
+ - generic [ref=e66] [cursor=pointer]: ⚠️
+ - button "Home Health Aides ⚠️" [ref=e67] [cursor=pointer]:
+ - generic [ref=e68] [cursor=pointer]: Home Health Aides
+ - generic [ref=e69] [cursor=pointer]: ⚠️
+ - button "Hosts & Hostesses, Restaurant, Lounge, & Coffee Shop ⚠️" [ref=e70] [cursor=pointer]:
+ - generic [ref=e71] [cursor=pointer]: Hosts & Hostesses, Restaurant, Lounge, & Coffee Shop
+ - generic [ref=e72] [cursor=pointer]: ⚠️
+ - button "Locker Room, Coatroom, & Dressing Room Attendants ⚠️" [ref=e73] [cursor=pointer]:
+ - generic [ref=e74] [cursor=pointer]: Locker Room, Coatroom, & Dressing Room Attendants
+ - generic [ref=e75] [cursor=pointer]: ⚠️
+ - button "Nannies" [ref=e76] [cursor=pointer]:
+ - generic [ref=e77] [cursor=pointer]: Nannies
+ - button "Nursing Assistants" [ref=e78] [cursor=pointer]:
+ - generic [ref=e79] [cursor=pointer]: Nursing Assistants
+ - button "Occupational Therapy Aides" [ref=e80] [cursor=pointer]:
+ - generic [ref=e81] [cursor=pointer]: Occupational Therapy Aides
+ - button "Passenger Attendants ⚠️" [ref=e82] [cursor=pointer]:
+ - generic [ref=e83] [cursor=pointer]: Passenger Attendants
+ - generic [ref=e84] [cursor=pointer]: ⚠️
+ - button "Personal Care Aides ⚠️" [ref=e85] [cursor=pointer]:
+ - generic [ref=e86] [cursor=pointer]: Personal Care Aides
+ - generic [ref=e87] [cursor=pointer]: ⚠️
+ - button "Physical Therapist Aides" [ref=e88] [cursor=pointer]:
+ - generic [ref=e89] [cursor=pointer]: Physical Therapist Aides
+ - button "Recreation Workers" [ref=e90] [cursor=pointer]:
+ - generic [ref=e91] [cursor=pointer]: Recreation Workers
+ - button "Residential Advisors" [ref=e92] [cursor=pointer]:
+ - generic [ref=e93] [cursor=pointer]: Residential Advisors
+ - button "School Bus Monitors ⚠️" [ref=e94] [cursor=pointer]:
+ - generic [ref=e95] [cursor=pointer]: School Bus Monitors
+ - generic [ref=e96] [cursor=pointer]: ⚠️
+ - button "Substitute Teachers, Short-Term ⚠️" [ref=e97] [cursor=pointer]:
+ - generic [ref=e98] [cursor=pointer]: Substitute Teachers, Short-Term
+ - generic [ref=e99] [cursor=pointer]: ⚠️
+ - button "Teaching Assistants, Preschool, Elementary, Middle, & Secondary School ⚠️" [ref=e100] [cursor=pointer]:
+ - generic [ref=e101] [cursor=pointer]: Teaching Assistants, Preschool, Elementary, Middle, & Secondary School
+ - generic [ref=e102] [cursor=pointer]: ⚠️
+ - button "Teaching Assistants, Special Education ⚠️" [ref=e103] [cursor=pointer]:
+ - generic [ref=e104] [cursor=pointer]: Teaching Assistants, Special Education
+ - generic [ref=e105] [cursor=pointer]: ⚠️
+ - button "Tour Guides & Escorts ⚠️" [ref=e106] [cursor=pointer]:
+ - generic [ref=e107] [cursor=pointer]: Tour Guides & Escorts
+ - generic [ref=e108] [cursor=pointer]: ⚠️
+ - button "Ushers, Lobby Attendants, & Ticket Takers ⚠️" [ref=e109] [cursor=pointer]:
+ - generic [ref=e110] [cursor=pointer]: Ushers, Lobby Attendants, & Ticket Takers
+ - generic [ref=e111] [cursor=pointer]: ⚠️
+ - button "Waiters & Waitresses ⚠️" [ref=e112] [cursor=pointer]:
+ - generic [ref=e113] [cursor=pointer]: Waiters & Waitresses
+ - generic [ref=e114] [cursor=pointer]: ⚠️
+ - button "Adapted Physical Education Specialists" [ref=e115] [cursor=pointer]:
+ - generic [ref=e116] [cursor=pointer]: Adapted Physical Education Specialists
+ - button "Adult Basic Education, Adult Secondary Education, & English as a Second Language Instructors" [ref=e117] [cursor=pointer]:
+ - generic [ref=e118] [cursor=pointer]: Adult Basic Education, Adult Secondary Education, & English as a Second Language Instructors
+ - button "Athletes & Sports Competitors" [ref=e119] [cursor=pointer]:
+ - generic [ref=e120] [cursor=pointer]: Athletes & Sports Competitors
+ - button "Baggage Porters & Bellhops ⚠️" [ref=e121] [cursor=pointer]:
+ - generic [ref=e122] [cursor=pointer]: Baggage Porters & Bellhops
+ - generic [ref=e123] [cursor=pointer]: ⚠️
+ - button "Barbers" [ref=e124] [cursor=pointer]:
+ - generic [ref=e125] [cursor=pointer]: Barbers
+ - button "Bartenders" [ref=e126] [cursor=pointer]:
+ - generic [ref=e127] [cursor=pointer]: Bartenders
+ - button "Bus Drivers, Transit & Intercity" [ref=e128] [cursor=pointer]:
+ - generic [ref=e129] [cursor=pointer]: Bus Drivers, Transit & Intercity
+ - button "Career/Technical Education Teachers, Middle School" [ref=e130] [cursor=pointer]:
+ - generic [ref=e131] [cursor=pointer]: Career/Technical Education Teachers, Middle School
+ - button "Career/Technical Education Teachers, Secondary School" [ref=e132] [cursor=pointer]:
+ - generic [ref=e133] [cursor=pointer]: Career/Technical Education Teachers, Secondary School
+ - button "Clergy" [ref=e134] [cursor=pointer]:
+ - generic [ref=e135] [cursor=pointer]: Clergy
+ - button "Cooks, Private Household" [ref=e136] [cursor=pointer]:
+ - generic [ref=e137] [cursor=pointer]: Cooks, Private Household
+ - button "Correctional Officers & Jailers" [ref=e138] [cursor=pointer]:
+ - generic [ref=e139] [cursor=pointer]: Correctional Officers & Jailers
+ - button "Dietetic Technicians" [ref=e140] [cursor=pointer]:
+ - generic [ref=e141] [cursor=pointer]: Dietetic Technicians
+ - button "Dining Room & Cafeteria Attendants & Bartender Helpers ⚠️" [ref=e142] [cursor=pointer]:
+ - generic [ref=e143] [cursor=pointer]: Dining Room & Cafeteria Attendants & Bartender Helpers
+ - generic [ref=e144] [cursor=pointer]: ⚠️
+ - button "Elementary School Teachers" [ref=e145] [cursor=pointer]:
+ - generic [ref=e146] [cursor=pointer]: Elementary School Teachers
+ - button "Fast Food & Counter Workers ⚠️" [ref=e147] [cursor=pointer]:
+ - generic [ref=e148] [cursor=pointer]: Fast Food & Counter Workers
+ - generic [ref=e149] [cursor=pointer]: ⚠️
+ - button "Fitness & Wellness Coordinators" [ref=e150] [cursor=pointer]:
+ - generic [ref=e151] [cursor=pointer]: Fitness & Wellness Coordinators
+ - button "Flight Attendants" [ref=e152] [cursor=pointer]:
+ - generic [ref=e153] [cursor=pointer]: Flight Attendants
+ - button "Hairdressers, Hairstylists, & Cosmetologists" [ref=e154] [cursor=pointer]:
+ - generic [ref=e155] [cursor=pointer]: Hairdressers, Hairstylists, & Cosmetologists
+ - button "Hotel, Motel, & Resort Desk Clerks ⚠️" [ref=e156] [cursor=pointer]:
+ - generic [ref=e157] [cursor=pointer]: Hotel, Motel, & Resort Desk Clerks
+ - generic [ref=e158] [cursor=pointer]: ⚠️
+ - button "Kindergarten Teachers" [ref=e159] [cursor=pointer]:
+ - generic [ref=e160] [cursor=pointer]: Kindergarten Teachers
+ - button "Licensed Practical & Licensed Vocational Nurses" [ref=e161] [cursor=pointer]:
+ - generic [ref=e162] [cursor=pointer]: Licensed Practical & Licensed Vocational Nurses
+ - button "Middle School Teachers" [ref=e163] [cursor=pointer]:
+ - generic [ref=e164] [cursor=pointer]: Middle School Teachers
+ - button "Midwives" [ref=e165] [cursor=pointer]:
+ - generic [ref=e166] [cursor=pointer]: Midwives
+ - button "Morticians, Undertakers, & Funeral Arrangers" [ref=e167] [cursor=pointer]:
+ - generic [ref=e168] [cursor=pointer]: Morticians, Undertakers, & Funeral Arrangers
+ - button "Occupational Therapy Assistants" [ref=e169] [cursor=pointer]:
+ - generic [ref=e170] [cursor=pointer]: Occupational Therapy Assistants
+ - button "Orderlies ⚠️" [ref=e171] [cursor=pointer]:
+ - generic [ref=e172] [cursor=pointer]: Orderlies
+ - generic [ref=e173] [cursor=pointer]: ⚠️
+ - button "Physical Therapist Assistants" [ref=e174] [cursor=pointer]:
+ - generic [ref=e175] [cursor=pointer]: Physical Therapist Assistants
+ - button "Preschool Teachers" [ref=e176] [cursor=pointer]:
+ - generic [ref=e177] [cursor=pointer]: Preschool Teachers
+ - button "Psychiatric Aides" [ref=e178] [cursor=pointer]:
+ - generic [ref=e179] [cursor=pointer]: Psychiatric Aides
+ - button "Reservation & Transportation Ticket Agents & Travel Clerks ⚠️" [ref=e180] [cursor=pointer]:
+ - generic [ref=e181] [cursor=pointer]: Reservation & Transportation Ticket Agents & Travel Clerks
+ - generic [ref=e182] [cursor=pointer]: ⚠️
+ - button "Secondary School Teachers" [ref=e183] [cursor=pointer]:
+ - generic [ref=e184] [cursor=pointer]: Secondary School Teachers
+ - button "Self-Enrichment Teachers" [ref=e185] [cursor=pointer]:
+ - generic [ref=e186] [cursor=pointer]: Self-Enrichment Teachers
+ - button "Shampooers" [ref=e187] [cursor=pointer]:
+ - generic [ref=e188] [cursor=pointer]: Shampooers
+ - button "Skincare Specialists" [ref=e189] [cursor=pointer]:
+ - generic [ref=e190] [cursor=pointer]: Skincare Specialists
+ - button "Social & Human Service Assistants" [ref=e191] [cursor=pointer]:
+ - generic [ref=e192] [cursor=pointer]: Social & Human Service Assistants
+ - button "Teaching Assistants, Postsecondary" [ref=e193] [cursor=pointer]:
+ - generic [ref=e194] [cursor=pointer]: Teaching Assistants, Postsecondary
+ - button "Telephone Operators ⚠️" [ref=e195] [cursor=pointer]:
+ - generic [ref=e196] [cursor=pointer]: Telephone Operators
+ - generic [ref=e197] [cursor=pointer]: ⚠️
+ - button "Travel Guides ⚠️" [ref=e198] [cursor=pointer]:
+ - generic [ref=e199] [cursor=pointer]: Travel Guides
+ - generic [ref=e200] [cursor=pointer]: ⚠️
+ - button "Cooks, Fast Food ⚠️" [ref=e201] [cursor=pointer]:
+ - generic [ref=e202] [cursor=pointer]: Cooks, Fast Food
+ - generic [ref=e203] [cursor=pointer]: ⚠️
+ - button "Dishwashers ⚠️" [ref=e204] [cursor=pointer]:
+ - generic [ref=e205] [cursor=pointer]: Dishwashers
+ - generic [ref=e206] [cursor=pointer]: ⚠️
+ - button "Door-to-Door Sales Workers, News & Street Vendors, & Related Workers ⚠️" [ref=e207] [cursor=pointer]:
+ - generic [ref=e208] [cursor=pointer]: Door-to-Door Sales Workers, News & Street Vendors, & Related Workers
+ - generic [ref=e209] [cursor=pointer]: ⚠️
+ - button "Educational, Guidance, & Career Counselors & Advisors" [ref=e210] [cursor=pointer]:
+ - generic [ref=e211] [cursor=pointer]: Educational, Guidance, & Career Counselors & Advisors
+ - button "Helpers--Painters, Paperhangers, Plasterers, & Stucco Masons ⚠️" [ref=e212] [cursor=pointer]:
+ - generic [ref=e213] [cursor=pointer]: Helpers--Painters, Paperhangers, Plasterers, & Stucco Masons
+ - generic [ref=e214] [cursor=pointer]: ⚠️
+ - button "Hospitalists" [ref=e215] [cursor=pointer]:
+ - generic [ref=e216] [cursor=pointer]: Hospitalists
+ - button "Low Vision Therapists, Orientation & Mobility Specialists, & Vision Rehabilitation Therapists" [ref=e217] [cursor=pointer]:
+ - generic [ref=e218] [cursor=pointer]: Low Vision Therapists, Orientation & Mobility Specialists, & Vision Rehabilitation Therapists
+ - button "Maids & Housekeeping Cleaners ⚠️" [ref=e219] [cursor=pointer]:
+ - generic [ref=e220] [cursor=pointer]: Maids & Housekeeping Cleaners
+ - generic [ref=e221] [cursor=pointer]: ⚠️
+ - button "Nurse Midwives" [ref=e222] [cursor=pointer]:
+ - generic [ref=e223] [cursor=pointer]: Nurse Midwives
+ - button "Special Education Teachers, Preschool" [ref=e224] [cursor=pointer]:
+ - generic [ref=e225] [cursor=pointer]: Special Education Teachers, Preschool
+ - button "Substance Abuse & Behavioral Disorder Counselors ⚠️" [ref=e226] [cursor=pointer]:
+ - generic [ref=e227] [cursor=pointer]: Substance Abuse & Behavioral Disorder Counselors
+ - generic [ref=e228] [cursor=pointer]: ⚠️
+ - generic [ref=e229]:
+ - text: This page includes information from
+ - link "O*NET OnLine" [ref=e230] [cursor=pointer]:
+ - /url: https://www.onetcenter.org
+ - text: by the U.S. Department of Labor, Employment & Training Administration (USDOL/ETA). Used under the
+ - link "CC BY 4.0 license" [ref=e231] [cursor=pointer]:
+ - /url: https://creativecommons.org/licenses/by/4.0/
+ - text: . **O*NET®** is a trademark of USDOL/ETA. Salary and employment data are enriched with resources from the
+ - link "Bureau of Labor Statistics" [ref=e232] [cursor=pointer]:
+ - /url: https://www.bls.gov
+ - text: and program information from the
+ - link "National Center for Education Statistics" [ref=e233] [cursor=pointer]:
+ - /url: https://nces.ed.gov
+ - text: .
+ - button "Open chat" [ref=e234] [cursor=pointer]:
+ - img [ref=e235] [cursor=pointer]
+```
\ No newline at end of file
diff --git a/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/test-failed-1.png b/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/test-failed-1.png
new file mode 100644
index 0000000..6ab5028
Binary files /dev/null and b/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/test-failed-1.png differ
diff --git a/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/video.webm b/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/video.webm
new file mode 100644
index 0000000..9019e99
Binary files /dev/null and b/test-results/09-comparison-duplicate-re-10630-uplicate-remove-and-persist-edge/video.webm differ
diff --git a/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/error-context.md b/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/error-context.md
new file mode 100644
index 0000000..a34d051
--- /dev/null
+++ b/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/error-context.md
@@ -0,0 +1,34 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - navigation [ref=e6]:
+ - button "Find Your Career" [ref=e8] [cursor=pointer]
+ - button "Preparing & UpSkilling for Your Career" [ref=e10] [cursor=pointer]
+ - button "Enhancing Your Career(Premium)" [ref=e12] [cursor=pointer]:
+ - text: Enhancing Your Career
+ - generic [ref=e13] [cursor=pointer]: (Premium)
+ - button "Retirement Planning (beta)(Premium)" [ref=e15] [cursor=pointer]:
+ - text: Retirement Planning (beta)
+ - generic [ref=e16] [cursor=pointer]: (Premium)
+ - button "Profile" [ref=e18] [cursor=pointer]
+ - generic [ref=e19]:
+ - button "Upgrade to Premium" [ref=e20] [cursor=pointer]
+ - button "Support" [ref=e21] [cursor=pointer]
+ - button "Logout" [ref=e22] [cursor=pointer]
+ - main [ref=e23]:
+ - generic [ref=e24]:
+ - heading "Educational Programs" [level=2] [ref=e25]
+ - paragraph [ref=e26]: "You have not selected a career yet. Please search for one below:"
+ - generic [ref=e27]:
+ - generic [ref=e28]:
+ - text: Search for Career
+ - generic [ref=e29]: "*"
+ - combobox "Start typing a career..." [ref=e31]
+ - paragraph [ref=e32]: Please pick from the dropdown when performing search. Our database is very comprehensive but can’t accommodate every job title—choose the closest match to what you’re searching for.
+ - paragraph [ref=e33]: After you pick a career, we’ll display matching educational programs.
+ - button "Open chat" [ref=e34] [cursor=pointer]:
+ - img [ref=e35] [cursor=pointer]
+```
\ No newline at end of file
diff --git a/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/test-failed-1.png b/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/test-failed-1.png
new file mode 100644
index 0000000..3f0736c
Binary files /dev/null and b/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/test-failed-1.png differ
diff --git a/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/video.webm b/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/video.webm
new file mode 100644
index 0000000..a12f390
Binary files /dev/null and b/test-results/22-educational-programs--p-85b9a-chool-cards-survives-reload-edge/video.webm differ
diff --git a/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/error-context.md b/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/error-context.md
new file mode 100644
index 0000000..a34d051
--- /dev/null
+++ b/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/error-context.md
@@ -0,0 +1,34 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - navigation [ref=e6]:
+ - button "Find Your Career" [ref=e8] [cursor=pointer]
+ - button "Preparing & UpSkilling for Your Career" [ref=e10] [cursor=pointer]
+ - button "Enhancing Your Career(Premium)" [ref=e12] [cursor=pointer]:
+ - text: Enhancing Your Career
+ - generic [ref=e13] [cursor=pointer]: (Premium)
+ - button "Retirement Planning (beta)(Premium)" [ref=e15] [cursor=pointer]:
+ - text: Retirement Planning (beta)
+ - generic [ref=e16] [cursor=pointer]: (Premium)
+ - button "Profile" [ref=e18] [cursor=pointer]
+ - generic [ref=e19]:
+ - button "Upgrade to Premium" [ref=e20] [cursor=pointer]
+ - button "Support" [ref=e21] [cursor=pointer]
+ - button "Logout" [ref=e22] [cursor=pointer]
+ - main [ref=e23]:
+ - generic [ref=e24]:
+ - heading "Educational Programs" [level=2] [ref=e25]
+ - paragraph [ref=e26]: "You have not selected a career yet. Please search for one below:"
+ - generic [ref=e27]:
+ - generic [ref=e28]:
+ - text: Search for Career
+ - generic [ref=e29]: "*"
+ - combobox "Start typing a career..." [ref=e31]
+ - paragraph [ref=e32]: Please pick from the dropdown when performing search. Our database is very comprehensive but can’t accommodate every job title—choose the closest match to what you’re searching for.
+ - paragraph [ref=e33]: After you pick a career, we’ll display matching educational programs.
+ - button "Open chat" [ref=e34] [cursor=pointer]:
+ - img [ref=e35] [cursor=pointer]
+```
\ No newline at end of file
diff --git a/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/test-failed-1.png b/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/test-failed-1.png
new file mode 100644
index 0000000..3f0736c
Binary files /dev/null and b/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/test-failed-1.png differ
diff --git a/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/video.webm b/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/video.webm
new file mode 100644
index 0000000..6afee64
Binary files /dev/null and b/test-results/23-educational-programs-se-a20f2-ted-to-paywall-non-premium--edge/video.webm differ
diff --git a/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/error-context.md b/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/error-context.md
new file mode 100644
index 0000000..6d29ef0
--- /dev/null
+++ b/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/error-context.md
@@ -0,0 +1,92 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - navigation [ref=e6]:
+ - button "Find Your Career" [ref=e8] [cursor=pointer]
+ - button "Preparing & UpSkilling for Your Career" [ref=e10] [cursor=pointer]
+ - button "Enhancing Your Career(Premium)" [ref=e12] [cursor=pointer]:
+ - text: Enhancing Your Career
+ - generic [ref=e13] [cursor=pointer]: (Premium)
+ - button "Retirement Planning (beta)(Premium)" [ref=e15] [cursor=pointer]:
+ - text: Retirement Planning (beta)
+ - generic [ref=e16] [cursor=pointer]: (Premium)
+ - button "Profile" [ref=e18] [cursor=pointer]
+ - generic [ref=e19]:
+ - button "Upgrade to Premium" [ref=e20] [cursor=pointer]
+ - button "Support" [ref=e21] [cursor=pointer]
+ - button "Logout" [ref=e22] [cursor=pointer]
+ - main [ref=e23]:
+ - generic [ref=e24]:
+ - generic [ref=e25]:
+ - heading "Explore Careers - use these tools to find your best fit" [level=2] [ref=e26]
+ - generic [ref=e27]:
+ - generic [ref=e28]:
+ - text: Search for Career
+ - generic [ref=e29]: "*"
+ - combobox "Start typing a career..." [active] [ref=e31]: cu
+ - paragraph [ref=e32]: Please pick from the dropdown when performing search. Our database is very comprehensive but can’t accommodate every job title—choose the closest match to what you’re searching for.
+ - generic [ref=e33]:
+ - heading "Career Comparison" [level=2] [ref=e34]
+ - button "Edit priorities" [ref=e35] [cursor=pointer]
+ - table [ref=e36]:
+ - rowgroup [ref=e37]:
+ - row "Career interests meaning stability growth balance recognition Match Actions" [ref=e38]:
+ - cell "Career" [ref=e39]
+ - cell "interests" [ref=e40]
+ - cell "meaning" [ref=e41]
+ - cell "stability" [ref=e42]
+ - cell "growth" [ref=e43]
+ - cell "balance" [ref=e44]
+ - cell "recognition" [ref=e45]
+ - cell "Match" [ref=e46]
+ - cell "Actions" [ref=e47]
+ - rowgroup [ref=e48]:
+ - row "Amusement & Recreation Attendants 5 3 1 3 3 3 53.8% Remove Plan your Education/Skills" [ref=e49]:
+ - cell "Amusement & Recreation Attendants" [ref=e50]
+ - cell "5" [ref=e51]
+ - cell "3" [ref=e52]
+ - cell "1" [ref=e53]
+ - cell "3" [ref=e54]
+ - cell "3" [ref=e55]
+ - cell "3" [ref=e56]
+ - cell "53.8%" [ref=e57]
+ - cell "Remove Plan your Education/Skills" [ref=e58]:
+ - button "Remove" [ref=e59] [cursor=pointer]
+ - button "Plan your Education/Skills" [ref=e60] [cursor=pointer]
+ - generic [ref=e61]:
+ - combobox [ref=e62]:
+ - option "All Preparation Levels" [selected]
+ - option "Little or No Preparation"
+ - option "Some Preparation Needed"
+ - option "Medium Preparation Needed"
+ - option "Considerable Preparation Needed"
+ - option "Extensive Preparation Needed"
+ - combobox [ref=e63]:
+ - option "All Fit Levels" [selected]
+ - option "Best - Very Strong Match"
+ - option "Great - Strong Match"
+ - option "Good - Less Strong Match"
+ - button "Reload Career Suggestions" [ref=e64] [cursor=pointer]
+ - generic [ref=e65]:
+ - generic [ref=e66]: ⚠️
+ - generic [ref=e67]: = May have limited data for this career path
+ - generic [ref=e69]:
+ - text: This page includes information from
+ - link "O*NET OnLine" [ref=e70] [cursor=pointer]:
+ - /url: https://www.onetcenter.org
+ - text: by the U.S. Department of Labor, Employment & Training Administration (USDOL/ETA). Used under the
+ - link "CC BY 4.0 license" [ref=e71] [cursor=pointer]:
+ - /url: https://creativecommons.org/licenses/by/4.0/
+ - text: . **O*NET®** is a trademark of USDOL/ETA. Salary and employment data are enriched with resources from the
+ - link "Bureau of Labor Statistics" [ref=e72] [cursor=pointer]:
+ - /url: https://www.bls.gov
+ - text: and program information from the
+ - link "National Center for Education Statistics" [ref=e73] [cursor=pointer]:
+ - /url: https://nces.ed.gov
+ - text: .
+ - button "Open chat" [ref=e74] [cursor=pointer]:
+ - img [ref=e75] [cursor=pointer]
+```
\ No newline at end of file
diff --git a/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/test-failed-1.png b/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/test-failed-1.png
new file mode 100644
index 0000000..21d5041
Binary files /dev/null and b/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/test-failed-1.png differ
diff --git a/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/video.webm b/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/video.webm
new file mode 100644
index 0000000..ed2e9ae
Binary files /dev/null and b/test-results/35-careersearech-arrowdown-4e4dd--suggestion-and-opens-modal-edge/video.webm differ
diff --git a/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/error-context.md b/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/error-context.md
new file mode 100644
index 0000000..76ed1da
--- /dev/null
+++ b/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/error-context.md
@@ -0,0 +1,22 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e8]:
+ - heading "Sign In" [level=1] [ref=e9]
+ - paragraph [ref=e10]: Unexpected token 'T', "Too many r"... is not valid JSON
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]: test_u202509111029199314
+ - textbox "Password" [ref=e13]: P@ssw0rd!9314
+ - button "Sign In" [active] [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/test-failed-1.png b/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/test-failed-1.png
new file mode 100644
index 0000000..9073b58
Binary files /dev/null and b/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/test-failed-1.png differ
diff --git a/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/video.webm b/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/video.webm
new file mode 100644
index 0000000..653191d
Binary files /dev/null and b/test-results/42-billing-refresh-status--3afec-eads-and-updates-plan-label-edge/video.webm differ
diff --git a/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/error-context.md b/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/error-context.md
new file mode 100644
index 0000000..bbde334
--- /dev/null
+++ b/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/error-context.md
@@ -0,0 +1,23 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e7]:
+ - generic [ref=e8]: Your session has expired. Please sign in again.
+ - generic [ref=e9]:
+ - heading "Sign In" [level=1] [ref=e10]
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]
+ - textbox "Password" [ref=e13]
+ - button "Sign In" [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/test-failed-1.png b/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/test-failed-1.png
new file mode 100644
index 0000000..a9552fe
Binary files /dev/null and b/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/test-failed-1.png differ
diff --git a/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/video.webm b/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/video.webm
new file mode 100644
index 0000000..4f25d40
Binary files /dev/null and b/test-results/43-billing-paywall-Billing-42b46-yload-no-redirect-on-non-OK-edge/video.webm differ
diff --git a/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/error-context.md b/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/error-context.md
new file mode 100644
index 0000000..bbde334
--- /dev/null
+++ b/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/error-context.md
@@ -0,0 +1,23 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e7]:
+ - generic [ref=e8]: Your session has expired. Please sign in again.
+ - generic [ref=e9]:
+ - heading "Sign In" [level=1] [ref=e10]
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]
+ - textbox "Password" [ref=e13]
+ - button "Sign In" [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/test-failed-1.png b/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/test-failed-1.png
new file mode 100644
index 0000000..a9552fe
Binary files /dev/null and b/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/test-failed-1.png differ
diff --git a/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/video.webm b/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/video.webm
new file mode 100644
index 0000000..a3492f7
Binary files /dev/null and b/test-results/43-billing-paywall-Billing-c23dd-g-anchors-render-Back-works-edge/video.webm differ
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/error-context.md b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/error-context.md
new file mode 100644
index 0000000..76ed1da
--- /dev/null
+++ b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/error-context.md
@@ -0,0 +1,22 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e8]:
+ - heading "Sign In" [level=1] [ref=e9]
+ - paragraph [ref=e10]: Unexpected token 'T', "Too many r"... is not valid JSON
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]: test_u202509111029199314
+ - textbox "Password" [ref=e13]: P@ssw0rd!9314
+ - button "Sign In" [active] [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/test-failed-1.png b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/test-failed-1.png
new file mode 100644
index 0000000..9073b58
Binary files /dev/null and b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/test-failed-1.png differ
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/video.webm b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/video.webm
new file mode 100644
index 0000000..f52013e
Binary files /dev/null and b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-edge/video.webm differ
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/error-context.md b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/error-context.md
deleted file mode 100644
index bc73311..0000000
--- a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/error-context.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Page snapshot
-
-```yaml
-- generic [active] [ref=e1]:
- - generic [ref=e2]:
- - navigation "Navigation Bar" [ref=e3]:
- - generic [ref=e4]:
- - link "Home" [ref=e5] [cursor=pointer]:
- - /url: /
- - img [ref=e6] [cursor=pointer]
- - link "Explore" [ref=e7] [cursor=pointer]:
- - /url: /explore/repos
- - link "Help" [ref=e8] [cursor=pointer]:
- - /url: https://docs.gitea.com
- - generic [ref=e9]:
- - link "Register" [ref=e10] [cursor=pointer]:
- - /url: /user/sign_up
- - img [ref=e11] [cursor=pointer]
- - text: Register
- - link "Sign In" [ref=e13] [cursor=pointer]:
- - /url: /user/login?redirect_to=%2fsignin
- - img [ref=e14] [cursor=pointer]
- - text: Sign In
- - main "Page Not Found" [ref=e16]:
- - generic [ref=e17]:
- - img "404" [ref=e18]
- - paragraph [ref=e19]:
- - text: The page you are trying to reach either
- - strong [ref=e20]: does not exist
- - text: or
- - strong [ref=e21]: you are not authorized
- - text: to view it.
- - group "Footer" [ref=e22]:
- - contentinfo "About Software" [ref=e23]:
- - link "Powered by Gitea" [ref=e24] [cursor=pointer]:
- - /url: https://about.gitea.com
- - text: "Version: 1.22.6 Page:"
- - strong [ref=e25]: 2ms
- - text: "Template:"
- - strong [ref=e26]: 1ms
- - group "Links" [ref=e27]:
- - menu [ref=e28] [cursor=pointer]:
- - generic [ref=e29] [cursor=pointer]:
- - img [ref=e30] [cursor=pointer]
- - text: English
- - link "Licenses" [ref=e32] [cursor=pointer]:
- - /url: /assets/licenses.txt
- - link "API" [ref=e33] [cursor=pointer]:
- - /url: /api/swagger
-```
\ No newline at end of file
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/test-failed-1.png b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/test-failed-1.png
deleted file mode 100644
index 302c688..0000000
Binary files a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/test-failed-1.png and /dev/null differ
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/trace.zip b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/trace.zip
deleted file mode 100644
index 4971870..0000000
Binary files a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/trace.zip and /dev/null differ
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/video.webm b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/video.webm
deleted file mode 100644
index 80a0d46..0000000
Binary files a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id-retry1/video.webm and /dev/null differ
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/error-context.md b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/error-context.md
deleted file mode 100644
index b01d5d3..0000000
--- a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/error-context.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Page snapshot
-
-```yaml
-- generic [active] [ref=e1]:
- - generic [ref=e2]:
- - navigation "Navigation Bar" [ref=e3]:
- - generic [ref=e4]:
- - link "Home" [ref=e5] [cursor=pointer]:
- - /url: /
- - img [ref=e6] [cursor=pointer]
- - link "Explore" [ref=e7] [cursor=pointer]:
- - /url: /explore/repos
- - link "Help" [ref=e8] [cursor=pointer]:
- - /url: https://docs.gitea.com
- - generic [ref=e9]:
- - link "Register" [ref=e10] [cursor=pointer]:
- - /url: /user/sign_up
- - img [ref=e11] [cursor=pointer]
- - text: Register
- - link "Sign In" [ref=e13] [cursor=pointer]:
- - /url: /user/login?redirect_to=%2fsignin
- - img [ref=e14] [cursor=pointer]
- - text: Sign In
- - main "Page Not Found" [ref=e16]:
- - generic [ref=e17]:
- - img "404" [ref=e18]
- - paragraph [ref=e19]:
- - text: The page you are trying to reach either
- - strong [ref=e20]: does not exist
- - text: or
- - strong [ref=e21]: you are not authorized
- - text: to view it.
- - group "Footer" [ref=e22]:
- - contentinfo "About Software" [ref=e23]:
- - link "Powered by Gitea" [ref=e24] [cursor=pointer]:
- - /url: https://about.gitea.com
- - text: "Version: 1.22.6 Page:"
- - strong [ref=e25]: 8ms
- - text: "Template:"
- - strong [ref=e26]: 6ms
- - group "Links" [ref=e27]:
- - menu [ref=e28] [cursor=pointer]:
- - generic [ref=e29] [cursor=pointer]:
- - img [ref=e30] [cursor=pointer]
- - text: English
- - link "Licenses" [ref=e32] [cursor=pointer]:
- - /url: /assets/licenses.txt
- - link "API" [ref=e33] [cursor=pointer]:
- - /url: /api/swagger
-```
\ No newline at end of file
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/test-failed-1.png b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/test-failed-1.png
deleted file mode 100644
index e976e33..0000000
Binary files a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/test-failed-1.png and /dev/null differ
diff --git a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/video.webm b/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/video.webm
deleted file mode 100644
index 188f947..0000000
Binary files a/test-results/44-onboarding-e2e--p0-Onbo-00a6c--lands-on-career-roadmap-id/video.webm and /dev/null differ
diff --git a/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/error-context.md b/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/error-context.md
new file mode 100644
index 0000000..76ed1da
--- /dev/null
+++ b/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/error-context.md
@@ -0,0 +1,22 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e8]:
+ - heading "Sign In" [level=1] [ref=e9]
+ - paragraph [ref=e10]: Unexpected token 'T', "Too many r"... is not valid JSON
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]: test_u202509111029199314
+ - textbox "Password" [ref=e13]: P@ssw0rd!9314
+ - button "Sign In" [active] [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/test-failed-1.png b/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/test-failed-1.png
new file mode 100644
index 0000000..9073b58
Binary files /dev/null and b/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/test-failed-1.png differ
diff --git a/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/video.webm b/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/video.webm
new file mode 100644
index 0000000..c73793a
Binary files /dev/null and b/test-results/45a-career-profile--p1-Pro-0c766-it-title-and-save-→-roadmap-edge/video.webm differ
diff --git a/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/error-context.md b/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/error-context.md
new file mode 100644
index 0000000..76ed1da
--- /dev/null
+++ b/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/error-context.md
@@ -0,0 +1,22 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e8]:
+ - heading "Sign In" [level=1] [ref=e9]
+ - paragraph [ref=e10]: Unexpected token 'T', "Too many r"... is not valid JSON
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]: test_u202509111029199314
+ - textbox "Password" [ref=e13]: P@ssw0rd!9314
+ - button "Sign In" [active] [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/test-failed-1.png b/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/test-failed-1.png
new file mode 100644
index 0000000..9073b58
Binary files /dev/null and b/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/test-failed-1.png differ
diff --git a/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/video.webm b/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/video.webm
new file mode 100644
index 0000000..59c59ea
Binary files /dev/null and b/test-results/45a-career-profile--p1-Pro-0f340-e-→-roadmap-then-edit-title-edge/video.webm differ
diff --git a/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/error-context.md b/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/error-context.md
new file mode 100644
index 0000000..76ed1da
--- /dev/null
+++ b/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/error-context.md
@@ -0,0 +1,22 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e8]:
+ - heading "Sign In" [level=1] [ref=e9]
+ - paragraph [ref=e10]: Unexpected token 'T', "Too many r"... is not valid JSON
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]: test_u202509111029199314
+ - textbox "Password" [ref=e13]: P@ssw0rd!9314
+ - button "Sign In" [active] [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/test-failed-1.png b/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/test-failed-1.png
new file mode 100644
index 0000000..9073b58
Binary files /dev/null and b/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/test-failed-1.png differ
diff --git a/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/video.webm b/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/video.webm
new file mode 100644
index 0000000..7993308
Binary files /dev/null and b/test-results/45b-financial-profile--p1--ff592--accept-alert-→-leaves-page-edge/video.webm differ
diff --git a/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/error-context.md b/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/error-context.md
new file mode 100644
index 0000000..76ed1da
--- /dev/null
+++ b/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/error-context.md
@@ -0,0 +1,22 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e8]:
+ - heading "Sign In" [level=1] [ref=e9]
+ - paragraph [ref=e10]: Unexpected token 'T', "Too many r"... is not valid JSON
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]: test_u202509111029199314
+ - textbox "Password" [ref=e13]: P@ssw0rd!9314
+ - button "Sign In" [active] [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/test-failed-1.png b/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/test-failed-1.png
new file mode 100644
index 0000000..9073b58
Binary files /dev/null and b/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/test-failed-1.png differ
diff --git a/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/video.webm b/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/video.webm
new file mode 100644
index 0000000..77d07c5
Binary files /dev/null and b/test-results/45c-college-profile--p1-Co-0f842-lan-autosuggest-degree-save-edge/video.webm differ
diff --git a/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/error-context.md b/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/error-context.md
new file mode 100644
index 0000000..76ed1da
--- /dev/null
+++ b/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/error-context.md
@@ -0,0 +1,22 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e8]:
+ - heading "Sign In" [level=1] [ref=e9]
+ - paragraph [ref=e10]: Unexpected token 'T', "Too many r"... is not valid JSON
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]: test_u202509111029199314
+ - textbox "Password" [ref=e13]: P@ssw0rd!9314
+ - button "Sign In" [active] [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/test-failed-1.png b/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/test-failed-1.png
new file mode 100644
index 0000000..9073b58
Binary files /dev/null and b/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/test-failed-1.png differ
diff --git a/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/video.webm b/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/video.webm
new file mode 100644
index 0000000..f04cf9d
Binary files /dev/null and b/test-results/45c-college-profile--p1-Co-82149-ink-to-career-prompt-→-save-edge/video.webm differ
diff --git a/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/error-context.md b/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/error-context.md
new file mode 100644
index 0000000..76ed1da
--- /dev/null
+++ b/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/error-context.md
@@ -0,0 +1,22 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - main [ref=e6]:
+ - generic [ref=e8]:
+ - heading "Sign In" [level=1] [ref=e9]
+ - paragraph [ref=e10]: Unexpected token 'T', "Too many r"... is not valid JSON
+ - generic [ref=e11]:
+ - textbox "Username" [ref=e12]: test_u202509111029199314
+ - textbox "Password" [ref=e13]: P@ssw0rd!9314
+ - button "Sign In" [active] [ref=e14] [cursor=pointer]
+ - generic [ref=e15]:
+ - paragraph [ref=e16]:
+ - text: Don’t have an account?
+ - link "Sign Up" [ref=e17] [cursor=pointer]:
+ - /url: /signup
+ - link "Forgot your password?" [ref=e19] [cursor=pointer]:
+ - /url: /forgot-password
+```
\ No newline at end of file
diff --git a/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/test-failed-1.png b/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/test-failed-1.png
new file mode 100644
index 0000000..9073b58
Binary files /dev/null and b/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/test-failed-1.png differ
diff --git a/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/video.webm b/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/video.webm
new file mode 100644
index 0000000..4c7ad7f
Binary files /dev/null and b/test-results/47-career-roadmap-and-coac-d94d5-panels-and-projection-chart-edge/video.webm differ
diff --git a/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/error-context.md b/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/error-context.md
new file mode 100644
index 0000000..2afc7bd
--- /dev/null
+++ b/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/error-context.md
@@ -0,0 +1,28 @@
+# Page snapshot
+
+```yaml
+- generic [ref=e3]:
+ - banner [ref=e4]:
+ - heading "AptivaAI - Career Guidance Platform" [level=1] [ref=e5]
+ - navigation [ref=e6]:
+ - button "Find Your Career" [ref=e8] [cursor=pointer]
+ - button "Preparing & UpSkilling for Your Career" [ref=e10] [cursor=pointer]
+ - button "Enhancing Your Career(Premium)" [ref=e12] [cursor=pointer]:
+ - text: Enhancing Your Career
+ - generic [ref=e13] [cursor=pointer]: (Premium)
+ - button "Retirement Planning (beta)(Premium)" [ref=e15] [cursor=pointer]:
+ - text: Retirement Planning (beta)
+ - generic [ref=e16] [cursor=pointer]: (Premium)
+ - button "Profile" [ref=e18] [cursor=pointer]
+ - generic [ref=e19]:
+ - button "Support" [ref=e20] [cursor=pointer]
+ - button "Logout" [ref=e21] [cursor=pointer]
+ - main [ref=e22]:
+ - generic [ref=e23]:
+ - 'heading "Your plan: Premium" [level=2] [ref=e24]'
+ - paragraph [ref=e25]: Manage payment method, invoices or cancel anytime.
+ - button "Manage subscription" [ref=e26] [cursor=pointer]
+ - button "Back to app" [ref=e27] [cursor=pointer]
+ - button "Open chat" [ref=e28] [cursor=pointer]:
+ - img [ref=e29] [cursor=pointer]
+```
\ No newline at end of file
diff --git a/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/test-failed-1.png b/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/test-failed-1.png
new file mode 100644
index 0000000..efdbbd3
Binary files /dev/null and b/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/test-failed-1.png differ
diff --git a/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/video.webm b/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/video.webm
new file mode 100644
index 0000000..5a67765
Binary files /dev/null and b/test-results/52-premium-routes-unlock---aaf4b-reer-roadmap-are-accessible-edge/video.webm differ
diff --git a/tests/e2e/01-signup.spec.mjs b/tests/e2e/01-signup.spec.mjs
index cef9512..9a6cb70 100644
--- a/tests/e2e/01-signup.spec.mjs
+++ b/tests/e2e/01-signup.spec.mjs
@@ -8,7 +8,7 @@ function uniq() {
}
test.describe('@p0 SignUp → Journey select → Route', () => {
- test.setTimeout(15000); // allow for slower first load + areas fetch
+ test.setTimeout(10000); // allow for slower first load + areas fetch
test('create a new user via UI and persist creds for later specs', async ({ page }) => {
const u = uniq();
diff --git a/tests/e2e/02-signin-landing.spec.mjs b/tests/e2e/02-signin-landing.spec.mjs
index f2b7985..ac69a4c 100644
--- a/tests/e2e/02-signin-landing.spec.mjs
+++ b/tests/e2e/02-signin-landing.spec.mjs
@@ -4,7 +4,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 SignIn → Landing', () => {
- test.setTimeout(20000);
+ test.setTimeout(10000);
test('signs in with persisted user and reaches SignInLanding', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/06-career-explorer.search.spec.mjs b/tests/e2e/06-career-explorer.search.spec.mjs
index 33fc15f..77a7674 100644
--- a/tests/e2e/06-career-explorer.search.spec.mjs
+++ b/tests/e2e/06-career-explorer.search.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p1 Career Explorer — CareerSearch datalist', () => {
- test.setTimeout(30000);
+ test.setTimeout(20000);
test('datalist commit opens modal; Change resets input', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/09-comparison-duplicate-remove.spec.mjs b/tests/e2e/09-comparison-duplicate-remove.spec.mjs
index 81e8c56..105729b 100644
--- a/tests/e2e/09-comparison-duplicate-remove.spec.mjs
+++ b/tests/e2e/09-comparison-duplicate-remove.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Comparison: add → duplicate blocked → remove → persists', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('add one, block duplicate, remove and persist', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/10-priorities-modal.spec.mjs b/tests/e2e/10-priorities-modal.spec.mjs
index 6fdbf2e..953ad38 100644
--- a/tests/e2e/10-priorities-modal.spec.mjs
+++ b/tests/e2e/10-priorities-modal.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Priorities modal — save & persist (current UI)', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('open → choose answers → Save Answers → reload → same answers present', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/11-forgot-password-ui.spec.mjs b/tests/e2e/11-forgot-password-ui.spec.mjs
index 94d3643..8ab3233 100644
--- a/tests/e2e/11-forgot-password-ui.spec.mjs
+++ b/tests/e2e/11-forgot-password-ui.spec.mjs
@@ -4,7 +4,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Forgot Password — exact UI flow', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('SignIn → Forgot → invalid email blocked → valid submit shows success copy', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/12-reset-password.spec.mjs b/tests/e2e/12-reset-password.spec.mjs
index 6c8aba6..e5314a6 100644
--- a/tests/e2e/12-reset-password.spec.mjs
+++ b/tests/e2e/12-reset-password.spec.mjs
@@ -2,7 +2,7 @@
import { test, expect } from '@playwright/test';
test.describe('@p0 Reset Password — exact UI flow', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('mismatch passwords → inline error, no request', async ({ page }) => {
// Route with a token param (the component checks presence only)
diff --git a/tests/e2e/13-premium-route-guard.spec.mjs b/tests/e2e/13-premium-route-guard.spec.mjs
index d2a8ffe..0c779af 100644
--- a/tests/e2e/13-premium-route-guard.spec.mjs
+++ b/tests/e2e/13-premium-route-guard.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 PremiumRoute guard', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('non-premium user is redirected to /paywall from premium routes', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/14-session-expired-handler.spec.mjs b/tests/e2e/14-session-expired-handler.spec.mjs
index a86a2c8..e186b55 100644
--- a/tests/e2e/14-session-expired-handler.spec.mjs
+++ b/tests/e2e/14-session-expired-handler.spec.mjs
@@ -2,7 +2,7 @@
import { test, expect } from '@playwright/test';
test.describe('@p0 SessionExpiredHandler', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('unauth → protected route → /signin?session=expired + banner', async ({ page }) => {
await page.context().clearCookies();
diff --git a/tests/e2e/15-support-modal.spec.mjs b/tests/e2e/15-support-modal.spec.mjs
index f3c1453..3c9066f 100644
--- a/tests/e2e/15-support-modal.spec.mjs
+++ b/tests/e2e/15-support-modal.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Support modal — open/close', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('header Support opens modal and can be closed', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/16-paywall-cta.spec.mjs b/tests/e2e/16-paywall-cta.spec.mjs
index c4f77bc..fa58be2 100644
--- a/tests/e2e/16-paywall-cta.spec.mjs
+++ b/tests/e2e/16-paywall-cta.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Paywall CTA', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('Upgrade to Premium visible on non-premium pages and navigates to /paywall', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/17-support-submit.spec.mjs b/tests/e2e/17-support-submit.spec.mjs
index ff47059..4e604c5 100644
--- a/tests/e2e/17-support-submit.spec.mjs
+++ b/tests/e2e/17-support-submit.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Support — submit ticket', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('open → fill form → submit → success (network or UI)', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/18-nav-menus.spec.mjs b/tests/e2e/18-nav-menus.spec.mjs
index 817028a..3e922f3 100644
--- a/tests/e2e/18-nav-menus.spec.mjs
+++ b/tests/e2e/18-nav-menus.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Header nav menus', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('Find Your Career menu → Career Explorer & Interest Inventory', async ({ page }) => {
const u = loadTestUser();
diff --git a/tests/e2e/19-profile-gating.spec.mjs b/tests/e2e/19-profile-gating.spec.mjs
index 4c68ee6..81b3ec2 100644
--- a/tests/e2e/19-profile-gating.spec.mjs
+++ b/tests/e2e/19-profile-gating.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Profile menu gating (non-premium)', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('Career/College Profiles show disabled labels when user is not premium', async ({ page }) => {
const u = loadTestUser();
diff --git a/tests/e2e/20-support-rate-limit.spec.mjs b/tests/e2e/20-support-rate-limit.spec.mjs
index 47d9de3..60d8b3c 100644
--- a/tests/e2e/20-support-rate-limit.spec.mjs
+++ b/tests/e2e/20-support-rate-limit.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Support — burst rate limit', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('rapid submissions eventually return 429 Too Many Requests', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/21-support-auth-dedupe.spec.mjs b/tests/e2e/21-support-auth-dedupe.spec.mjs
index 4e2f91d..e70c875 100644
--- a/tests/e2e/21-support-auth-dedupe.spec.mjs
+++ b/tests/e2e/21-support-auth-dedupe.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Support — auth + dedupe', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('unauthenticated request is rejected (401)', async ({ page }) => {
await page.context().clearCookies();
diff --git a/tests/e2e/22-educational-programs.spec.mjs b/tests/e2e/22-educational-programs.spec.mjs
index 9fb682d..e2292c2 100644
--- a/tests/e2e/22-educational-programs.spec.mjs
+++ b/tests/e2e/22-educational-programs.spec.mjs
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { loadTestUser } from '../utils/testUser.js';
test.describe('@p0 Educational Programs — handoff & page render', () => {
- test.setTimeout(20000);
+ test.setTimeout(15000);
test('handoff carries selectedCareer; page shows career title, KSA header and school cards; survives reload', async ({ page }) => {
const user = loadTestUser();
diff --git a/tests/e2e/45a-career-profile.spec.mjs b/tests/e2e/45a-career-profile.spec.mjs
new file mode 100644
index 0000000..a41c68d
--- /dev/null
+++ b/tests/e2e/45a-career-profile.spec.mjs
@@ -0,0 +1,166 @@
+// @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 });
+ });
+});
diff --git a/tests/e2e/45b-financial-profile.spec.mjs b/tests/e2e/45b-financial-profile.spec.mjs
new file mode 100644
index 0000000..19c83a5
--- /dev/null
+++ b/tests/e2e/45b-financial-profile.spec.mjs
@@ -0,0 +1,82 @@
+// tests/e2e/45b-financial-profile.spec.mjs
+// @ts-check
+import { test, expect } from '@playwright/test';
+import { loadTestUser } from '../utils/testUser.js';
+
+const byName = (page, n) => page.locator(`input[name="${n}"]`).first();
+// Label → first following input (works even without for/id)
+const inputAfterLabel = (page, labelRe) =>
+ page.locator('label').filter({ hasText: labelRe }).first()
+ .locator('xpath=following::input[1]').first();
+
+test.describe('@p1 Profile — Financial editor (45b)', () => {
+ test.setTimeout(45000);
+
+ test('Save financial profile (accept alert) → leaves page', 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 })
+ })
+ );
+
+ // Deterministic GET/POST for the form
+ await page.route('**/api/premium/financial-profile', async route => {
+ if (route.request().method() === 'GET') {
+ await route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ body: JSON.stringify({
+ current_salary: 0,
+ additional_income: 0,
+ monthly_expenses: 0,
+ monthly_debt_payments: 0,
+ retirement_savings: 0,
+ emergency_fund: 0,
+ retirement_contribution: 0,
+ emergency_contribution: 0,
+ extra_cash_emergency_pct: 50,
+ extra_cash_retirement_pct: 50
+ })
+ });
+ } else if (route.request().method() === 'POST') {
+ await route.fulfill({ status: 200, body: '{}' });
+ } 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: 15_000 });
+
+ // Go to form
+ await page.goto('/financial-profile', { waitUntil: 'domcontentloaded' });
+ await expect(page.getByRole('heading', { name: /Edit\s*Your\s*Financial\s*Profile/i }))
+ .toBeVisible({ timeout: 15_000 });
+
+ // Fill minimal fields
+ await byName(page, 'currentSalary').fill('55000');
+
+ // This field has NO name attribute → select by label then first input
+ await inputAfterLabel(page, /Monthly\s*Living\s*Expenses/i).fill('1800');
+
+ await byName(page, 'monthlyDebtPayments').fill('200');
+ await byName(page, 'extraCashEmergencyPct').fill('40'); // complements to 60
+
+ // Accept expected alert then leave page
+ page.once('dialog', d => d.accept().catch(() => {}));
+ await page.getByRole('button', { name: /^Save$/ }).click();
+
+ await expect(page).not.toHaveURL(/\/financial-profile$/i, { timeout: 20_000 });
+ });
+});
diff --git a/tests/e2e/45c-college-profile.spec.mjs b/tests/e2e/45c-college-profile.spec.mjs
new file mode 100644
index 0000000..f60a94e
--- /dev/null
+++ b/tests/e2e/45c-college-profile.spec.mjs
@@ -0,0 +1,262 @@
+// @ts-check
+import { test, expect } from '@playwright/test';
+import { loadTestUser } from '../utils/testUser.js';
+
+const SCHOOL = 'Alabama A & M University';
+const PROGRAM = 'Agriculture, General.';
+const DEGREE = "Bachelor's Degree";
+
+async function fillDateField(ctrl, iso = '2027-06-01', us = '06/01/2027') {
+ const type = (await ctrl.getAttribute('type')) || '';
+ if (type.toLowerCase() === 'date') {
+ await ctrl.fill(iso);
+ } else {
+ await ctrl.scrollIntoViewIfNeeded();
+ await ctrl.click({ force: true });
+ await ctrl.fill('');
+ await ctrl.type(us, { delay: 15 });
+ }
+}
+
+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();
+}
+
+test.describe('@p1 College-Profile (45c)', () => {
+ test.setTimeout(20000);
+
+ test('Create new college plan from list (with link-to-career prompt) → save', async ({ page, request }) => {
+ 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 }) })
+ );
+
+ // Seed a minimal Career Profile (needed for the prompt dropdown)
+ const scen = await request.post('/api/premium/career-profile', {
+ data: { career_name: 'Teaching Assistants, Postsecondary', status: 'planned', start_date: '2025-09-01' }
+ });
+ const { career_profile_id } = await scen.json();
+
+ // Deterministic autosuggest + degree + tuition
+ await page.route(/\/api\/schools\/suggest\?*/i, async route => {
+ await route.fulfill({ status: 200, contentType: 'application/json',
+ body: JSON.stringify([{ name: 'Alabama A & M University', unitId: 100654 }]) });
+ });
+ await page.route(/\/api\/programs\/suggest\?*/i, async route => {
+ await route.fulfill({ status: 200, contentType: 'application/json',
+ body: JSON.stringify([{ program: 'Agriculture, General.' }]) });
+ });
+ await page.route(/\/api\/programs\/types\?*/i, async route => {
+ await route.fulfill({ status: 200, contentType: 'application/json',
+ body: JSON.stringify({ types: ["Bachelor's Degree"] }) });
+ });
+ await page.route(/\/api\/tuition\/estimate\?*/i, async route => {
+ await route.fulfill({ status: 200, contentType: 'application/json',
+ body: JSON.stringify({ estimate: 17220 }) });
+ });
+
+ // Accept POST save
+ await page.route('**/api/premium/college-profile', async route => {
+ if (route.request().method() === 'POST') {
+ await route.fulfill({ status: 200, body: JSON.stringify({ ok: true }) });
+ } 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 });
+
+ // List → + New College Plan (expect link-to-career prompt)
+ await page.goto('/profile/college', { waitUntil: 'domcontentloaded' });
+ await page.getByRole('button', { name: '+ New College Plan' }).click();
+ // The prompt is a labeled box; target the