Skip to main content

How to Test EdTech Course Enrollment and Progress

Short answer

EdTech flows fail on prerequisite bypass, waitlist promotion races, progress not gating certificates, and media completion lies—not on whether enroll button works. Seed course DAGs and seat counts, probe enrollment_state and watchedSeconds, pair video completion with media player patterns, and expand waitlist scenarios when TrueCoverage shows full courses in prod.

Part of Testing Guides by industry.

Who this is for

Teams shipping LMS platforms, course marketplaces, cohort programs, or corporate training with prerequisites, seat caps, waitlists, progress tracking, quizzes, and certificates.

Why testing EdTech enrollment matters

Enrollment bugs block revenue and accreditation:

  • Prerequisite bypass — user enrolls in Course B without completing A; accreditation audit fails.
  • Waitlist errors — seat opens, wrong user promoted; fairness complaints and refund requests.
  • Progress lies — UI 100% but probe incomplete; certificate issued incorrectly.
  • Payment gating — free preview grants full content; revenue leakage.
  • SCORM/xAPI mismatch — completion reported to external LRS wrong; enterprise SLA breach.

Probe enrollment_state, progress, and certificate_eligible—UI checkmarks are insufficient.

Complexity map

ScenarioEdge caseWhy tests breakApproach
PrerequisiteMissing course AB enroll succeedsProbe block_reason
Seat capFull courseOver-enrollProbe seats_remaining=0
Waitlist promoteCancel frees seatWrong orderProbe queue position
Cohort start dateEarly accessContent leakclock + probe
Payment pendingContent visibleBypass paywallProbe entitlement
Quiz gateFail quizCertificate issuedProbe quiz_passed
Video completionSeek to endCertificateProbe watchedSeconds
Re-enrollExpired certDuplicate rowProbe single active
Instructor previewStudent view conflatedFree accessProbe role
Bundle enrollPartial failureOrphan paymentProbe bundle state
Drop courseProgress retainedPolicyProbe enrollment_state
CE creditsWrong hoursComplianceProbe credit_minutes

Prerequisite chain seed

await request.post('/api/test/seed-courses', {
data: {
runId,
courses: [
{ id: 'course-a', title: 'Intro', requires: [] },
{ id: 'course-b', title: 'Advanced', requires: ['course-a'] },
],
},
});
await request.post('/api/test/seed-user-progress', {
data: { runId, userId: `u-${runId}`, completed: [] }, // not completed A
});

Negative prerequisite

await page.goto('/courses/course-b/enroll');
await page.getByRole('button', { name: 'Enroll' }).click();
const enroll = await request.get(`/api/test/enrollment?runId=${runId}&course=course-b`).then(r => r.json());
expect(enroll.state).toBe('blocked');
expect(enroll.blockReason).toMatch(/prerequisite/i);

Waitlist promotion

await request.post('/api/test/seed-course-capacity', {
data: { runId, courseId: 'course-a', capacity: 1, enrolled: 1, waitlist: [`u-${runId}`] },
});
await request.post('/api/test/free-seat', { data: { runId, courseId: 'course-a' } }); // cancel other
await expect.poll(async () =>
(await request.get(`/api/test/enrollment?runId=${runId}&course=course-a`)).json()
).toMatchObject({ state: 'enrolled' });

Progress and media

Pair with video/audio players:

await page.goto('/lessons/lesson-1');
// play + seek pattern ...
await expect.poll(async () =>
(await request.get(`/api/test/progress?runId=${runId}&lesson=lesson-1`)).json()
).toMatchObject({ completed: true });
await page.goto('/courses/course-a/certificate');
const cert = await request.get(`/api/test/certificate?runId=${runId}&course=course-a`);
expect(cert.status()).toBe(200);

Requirement slices to cover

  • enrollment_state — enrolled, waitlisted, blocked, completed
  • course_id — top courses by prod enrollment

Run /testchimp evolve when waitlist promotion slice undertested.

CI checklist

  1. Course DAG in seed data per runId
  2. Prerequisite negative for every required edge
  3. Waitlist promotion via admin/free-seat route
  4. Progress probe before certificate assert
  5. Payment entitlement probe if monetized
  6. Video completion uses stub clip not hour-long media

Anti-patterns

Anti-patternWhy it failsBetter approach
UI progress bar onlyLieswatchedSeconds probe
Skip waitlistRace bugsPromotion poll
Enroll B without A testAccreditationblockReason probe
Full video watch in CISlowSeek near end
Shared course capacityParallel flakePer-run capacity
Certificate without quizCompliancequiz_passed probe

Example scenario

Situation: Learner completes all lessons but fails final quiz; attempts certificate download.

Expected outcome: Certificate blocked; enrollment_state completed lessons but certificate_eligible false.

Why UI-only automation breaks: Certificate button visible; probe issues cert—accreditation violation.

  1. Arrange: Seed course with quiz_required; user completes lessons, fails quiz.
  2. Act: Click download certificate.
  3. Assert: Probe certificate 403; UI error; enrollment probe certificate_eligible=false.

TestChimp workflow: Track enrollment_state by course_id; evolve waitlist and prerequisite paths per prod enrollment share.

Same Arrange/Act/Assert pattern as expired-coupon checkout.

Connect scenarios to your QA workflow

Capture business rules in markdown test plans and enforce them with seed routes and probe Assert. Link SmartTests with // @Scenario: for requirement traceability. Use /testchimp test on PRs; /testchimp explore on SmartTest paths for non-functional gaps (ExploreChimp).

External references

Frequently asked questions

How do I test waitlist promotion?

Seed full course with user on waitlist, free seat via admin/cancel route, poll enrollment probe until state=enrolled—assert queue order if product guarantees FIFO.

How do prerequisites work in E2E?

Seed course DAG; attempt enroll without completing required course; probe blockReason. Happy path: complete A via progress probe then enroll B.

How is video progress verified?

Use stub media and seek-to-end pattern; poll progress probe watchedSeconds and completed=true—do not trust UI percent alone.

How do certificates gate on quizzes?

Complete lessons, fail quiz probe, attempt certificate—expect 403. Pass quiz probe then certificate 200.

How do paid courses differ?

Probe entitlement before content URLs; attempt enroll without payment—expect blocked; after test payment seed, content probe 200.

Which courses need TrueCoverage priority?

Compare enrollment_state by course_id prod vs test—evolve waitlist and prerequisite specs for top enrollment courses.

Can ExploreChimp help complex cohort flows?

Yes for multi-week cohort UX; convert to SmartTests with course seeds and // @Scenario: for accreditation checklists.

Apply these patterns in your repo

Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.

Start free on TestChimp · Book a demo