Skip to content

test: make the LoadedTabPage streak test actually assert - #2108

Merged
brian-smith-tcril merged 1 commit into
masterfrom
bsmith/loaded-tab-page-streak-test-assert
Sep 23, 2026
Merged

brian-smith-tcril merged 1 commit into
masterfrom
bsmith/loaded-tab-page-streak-test-assert

Conversation

@brian-smith-tcril

@brian-smith-tcril brian-smith-tcril commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Make the shows streak celebration modal case in tab-page/LoadedTabPage.test.jsx actually assert. It could not fail: the StreakModal mock rendered a bare div whatever its props, and LoadedTabPage renders StreakModal on every render, open or closed; and its fixture set celebrations on the courseware metadata factory, while the component has read celebrations from courseHomeMeta since AA-1018 (2022), which the test store fills from a separate factory with celebrations: null. Removing celebrations from the fixture left the test green. Test-only; LoadedTabPage.tsx is untouched. Stacked on #2106 as the stack top; below B2 (#2085), which rewrites this suite's data source and should land on a case that checks something. Part of #1946 as test hygiene on code the migration touches. Closes #2107.

What changed

  • The fixture goes on the courseHomeMetadata factory, through the initializeTestStore option the suite's staff case already uses, so the streak reaches the model the component reads, camel-cased by the real normalizer.
  • The modal is no longer mocked. Closed, StreakCelebrationModal renders nothing; open, it renders a dialog titled with the streak length. The case asserts a dialog reading "3 day streak" is on the page. Rendering it costs what its own suite pays: the analytics module mocked and an act wrapper around the render.
  • renders correctly asserts the contrast: no dialog on the default fixture. A modal that is always open cannot satisfy the streak case's assertion.

Testing

npm run types and npm run lint clean; LoadedTabPage.test.jsx 3/3 with no console output. Negative checks on this layer: removing celebrations from the fixture fails the streak case (no element with role dialog); forcing the modal open in the component fails the contrast (a dialog titled "null day streak"). The full suite was not run for a one-file test change.

Provenance

The mock has been unconditional since the case was written in #354 (2021-02-22). The fixture stopped reaching the component in AA-1018 (3fe5bb1, 2022-01-14), which moved LoadedTabPage's read to courseHomeMeta and gave the test store its own courseHomeMetadata factory. Found while planning B2 (#2085), which rewrites how this suite gets its data; split out as its own layer, like #2078 / #2079 and #2100 / #2101, so B2's diff to the suite is only the data-source swap.

Decisions

Full decision log

Decisions — make the LoadedTabPage streak test actually assert (#2107)

Test-only layer between #2103 (PR #2106) and B2 (#2085). Entries 1 and 2 were
settled while reviewing the #2085 plan; the rest landed with the code.

  1. A layer of its own, below B2. B2 rewrites how LoadedTabPage.test.jsx
    gets its data (the query instead of the store), so it would have had to
    touch this case anyway, and the first draft of the B2 plan fixed it there.
    Rejected in review for the Make the courseware redirect-rule tests actually assert: un-awaited waitFor and unwired mocks from #1501 #2078 / Make the useIsCourseLoaded tests actually assert: replaced mocks and unsettled checks from #2071 #2100 shape: a small issue and a tiny
    test: layer first, so B2's diff to the suite is only the data-source
    swap and the repair of a 2021 test is reviewed as what it is. It sits on
    Stop the progress tab data refetching from components under the tab #2103 because that is the stack top; nothing in it depends on Stop the progress tab data refetching from components under the tab #2103.

  2. Two defects, either of which alone keeps the case green.

    • The mock rendered unconditionally. StreakCelebrationModal was
      mocked as a component returning <div data-testid="StreakModal" />
      whatever its props, and LoadedTabPage always renders StreakModal
      (LoadedTabPage.tsx:71-77), open or closed. "The mock is in the
      document" is true on every render. So since the case was written
      (2525805, [AA-304] Create three day streak celebration #354, 2021-02-22).
    • The fixture never reached the component. The case built the
      courseware metadata factory with celebrations, but LoadedTabPage has
      read celebrations from courseHomeMeta since 3fe5bb1 (AA-1018,
      2022-01-14), and initializeTestStore fills that model from a separate
      courseHomeMetadata factory built with celebrations: null
      (buildSimpleCourseAndSequenceMetadata, sequenceMetadata.factory.js).
      The case was not updated then. b3d3366 (Updated frontend-build to v12 #962, 2023) only reformatted
      the mock for a lint rule.

    Removing celebrations from the fixture left the case green, on this
    layer's base with no other change.

  3. The fixture goes on the courseHomeMetadata factory, through the
    initializeTestStore option.
    initializeTestStore({ courseHomeMetadata })
    is the shape the suite's staff case already uses
    (original_user_is_staff: true), and the store fills courseHomeMeta
    from the mocked course_metadata endpoint through the real normalizer, so
    streak_length_to_celebrate arrives camel-cased exactly as production
    data does. The course id comes from getTestStoreIds(testStore) rather
    than a hand-built courseMetadata, since the case no longer builds one.

  4. The modal is not mocked; the case asserts on the dialog. A first
    version kept a mock and made it a jest.fn component
    (UnitSuspense.test.jsx:20-21's shape), asserting on the props
    LoadedTabPage passed it — isStreakCelebrationOpen: true,
    streakLengthToCelebrate: 3. Rejected in review: that tests that
    LoadedTabPage hands a stub an open flag, not that anything is shown,
    which is what the case is named for. The real StreakCelebrationModal
    returns null when closed (StreakCelebrationModal.jsx:117-119) and
    renders a Paragon ModalDialog titled
    ${streakLengthToCelebrate} day streak when open (:143-161), so the
    case now asserts getByRole('dialog') has text 3 day streak: both the
    open flag and the length flow through the real component to what a
    learner sees. Rendering it costs what its own suite pays
    (StreakCelebrationModal.test.jsx:18-19,56): jest.mock('@edx/frontend-platform/analytics'),
    because opening fires recordStreakCelebration → sendTrackEvent, and an
    act wrapper around the render for the modal's effects. Its own
    useModel('courseHomeMeta') read (org, username) is served by the
    same store the fixture fills; its discount effect is skipped because
    streakDiscountCouponEnabled is null on the default fixture.

  5. renders correctly asserts the contrast. On the default fixture
    (celebrations: null) there is no dialog on the page
    (queryByRole('dialog') is null). Without it the streak case's assertion
    could be satisfied by a modal that is always open — the mirror of
    defect 1. It also pins the !! coercion on the prop
    (LoadedTabPage.tsx:75): forcing the prop to true in the component
    renders a dialog titled "null day streak" and fails this case.

  6. Test-only, one file. LoadedTabPage.tsx is untouched; the diff is
    src/tab-page/LoadedTabPage.test.jsx alone (10 insertions, 8 deletions).
    Suite 3/3 with no console output; npm run lint and npm run types
    clean. Negative checks on this layer: removing celebrations from the
    fixture fails shows streak celebration modal (no element with role
    dialog); replacing !!isStreakCelebrationOpen with a bare
    isStreakCelebrationOpen on the StreakModal prop fails renders
    correctly
    . The full suite was not run for a one-file test change.

🤖 Generated with Claude Code

@brian-smith-tcril
brian-smith-tcril added this pull request to stack #2080 September 23, 2026 13:56
@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.95%. Comparing base (8ffa5fe) to head (d908d76).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2108   +/-   ##
=======================================
  Coverage   93.95%   93.95%           
=======================================
  Files         367      367           
  Lines        5939     5939           
  Branches     1435     1435           
=======================================
  Hits         5580     5580           
  Misses        345      345           
  Partials       14       14           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@brian-smith-tcril
brian-smith-tcril marked this pull request as ready for review September 23, 2026 14:04
@brian-smith-tcril
brian-smith-tcril force-pushed the bsmith/loaded-tab-page-streak-test-assert branch from 01a9403 to bece479 Compare September 23, 2026 14:05
Base automatically changed from bsmith/progress-tab-readers-no-refetch to master September 23, 2026 14:11
The *shows streak celebration modal* case could not fail. Its `StreakModal`
mock rendered a bare div whatever its props, and `LoadedTabPage` always
renders `StreakModal`, open or closed, so "the mock is in the document" was
true on every render. Its fixture also set `celebrations` on the courseware
metadata factory, while `LoadedTabPage` has read `celebrations` from
`courseHomeMeta` since AA-1018; the test store builds that from a separate
factory with `celebrations: null`, so the streak never reached the component.
Removing `celebrations` from the fixture left the test green.

The fixture now goes on the `courseHomeMetadata` factory, through the
`initializeTestStore` option the suite's staff case already uses, and the
modal is no longer mocked: closed it renders nothing, open it renders a
dialog titled with the streak length, so the case asserts a dialog reading
"3 day streak" is on the page and *renders correctly* asserts there is no
dialog on the default fixture. Removing `celebrations` from the fixture now
fails the streak case, and forcing the modal open in the component fails the
contrast. The component is unchanged.

Part of #1946 (Stage 1). Closes #2107.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@brian-smith-tcril
brian-smith-tcril force-pushed the bsmith/loaded-tab-page-streak-test-assert branch from bece479 to d908d76 Compare September 23, 2026 14:11

@arbrandes arbrandes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

@brian-smith-tcril
brian-smith-tcril merged commit 68c7608 into master Sep 23, 2026
7 checks passed
@brian-smith-tcril
brian-smith-tcril deleted the bsmith/loaded-tab-page-streak-test-assert branch September 23, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make the LoadedTabPage streak test actually assert: an unconditional mock from #354 and a fixture inert since AA-1018

2 participants