Skip to content

Rewrite the instance-poking presentational tests with React Testing Library#315

Open
jonathangreen wants to merge 1 commit into
mainfrom
chore/rewrite-instance-poking-test-rtl
Open

Rewrite the instance-poking presentational tests with React Testing Library#315
jonathangreen wants to merge 1 commit into
mainfrom
chore/rewrite-instance-poking-test-rtl

Conversation

@jonathangreen

@jonathangreen jonathangreen commented Jul 9, 2026

Copy link
Copy Markdown
Member

Description

Rewrites 22 enzyme suites that reached into class internals with instance()/setState()/state(): the announcement/color/table widgets, the complaint and troubleshooting views, the library/book/list edit forms, the diagnostics and reset-adobe panels, and ContextProvider.

Each internal poke becomes an observable assertion:

  • instance() reads of computed values (Announcement.format, ClassificationsTable.readableType, ContextProvider methods) are driven through real inputs and asserted on the rendered DOM.
  • setState()/instance-method calls become the user action that causes them — a checkbox click, a Save click, a tab click — with the assertion on the DOM that reflects the new state.
  • Ref-based getValue() APIs (Autocomplete, ColorPicker) are exercised through a real ref, since production calls them that way.
  • ContextProvider is rewritten around a capturing consumer child that reads the legacy context the component provides (pathFor/prepareCollectionUrl/prepareBookUrl), rather than the component instance. The legacy global.jsdom.reconfigure origin trick is dropped — those helpers strip only the origin, so the real jsdom origin yields the same encoded output.

Connected panels (Complaints, PatronAuthServices, SitewideAnnouncements, Classifications, BookDetailsEditor, CustomListsForBook, ResetAdobeId, SelfTests's category) render their connected default export with fetch stubbed and awaited, so mapStateToProps/mapDispatchToProps stay covered. The coverage gate caught two gaps: a Complaints type-format fallback (added a non-matching-type case) and SelfTests's own connect wiring, which only the SelfTestsTabContainer full-mount covered — so SelfTestsTabContainer is deferred (original retained) until SelfTests is migrated to render its connected export.

userEvent usage follows the const user = userEvent.setup() convention across tests/jest/.

Motivation and Context

Part of the mocha/chai/enzyme → jest/RTL migration. This tranche clears the suites that poked class internals — the ones whose translation is least mechanical, since each instance()/setState() has to be re-expressed as a user action and a DOM assertion.

How Has This Been Tested?

npm test — full suite green. Coverage rises to 94.68% (+38 lines over baseline), no previously-covered line lost.

Checklist:

  • I have updated the documentation accordingly.
  • All new and existing tests passed.

@claude

claude Bot commented Jul 9, 2026

Copy link
Copy Markdown

Claude finished @jonathangreen's task in 3m 58s —— View job


Claude Code Review

  • Gather PR context (diff, changed files)
  • Review the migrated RTL tests for correctness & coverage fidelity
  • Review test isolation, mocking, and connect-wiring coverage
  • Post review feedback

Summary

This is a careful, high-quality tranche of the enzyme→RTL migration. The hard part — re-expressing each instance()/setState()/state() poke as a user action plus a DOM assertion — is done consistently and with useful explanatory comments. The choices around covering connect() wiring (rendering the connected default export with fetch stubbed and awaited, while injecting connect-derived props through the unconnected class where a real mount would reject an uncaught fetch) are sound and keep mapStateToProps/mapDispatchToProps covered. fetch spies are restored per suite, fresh Responses are returned where multiple fetches can occur, the userEvent.setup() convention is followed throughout, and no focused/skipped tests or stray console calls slipped in. I found only one minor test-design point; nothing blocking.

Details

Minor: tests/jest/components/ClassificationsTable.test.tsx:9-14

The test re-implements the component's own readableType regex and asserts the rendered cell equals readableType(c.type), i.e. it verifies the DOM against a copy of the very transformation under test. A total regression (e.g. the component stops stripping the prefix) is still caught, but a subtle bug in the regex that a maintainer replicates identically in this copy would pass unnoticed. Since the fixture is fixed and known, asserting the literal expected strings is both simpler and strictly stronger:

// classificationsData row: type "http://librarysimplified.org/terms/genres/Simplified/"
expect(cells[0].textContent).toBe("Simplified");

const readableType = (type: string) =>
type.replace(
/http:\/\/librarysimplified\.org\/terms\/genres\/([^/]+)\//,
"$1"
);

· chore/rewrite-instance-poking-test-rtl

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR rewrites legacy Enzyme component tests as React Testing Library tests. The main changes are:

  • Moves instance-poking suites from src/components/__tests__ into tests/jest/components.
  • Exercises form submits, clicks, refs, and connected Redux panels through rendered behavior.
  • Adds a shared FormData shim for form submit tests.
  • Adds direct coverage for the shared shim’s browser-like form serialization.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
tests/jest/testUtils/formDataShim.ts Adds a shared FormData shim that models browser form serialization for the migrated submit tests.
tests/jest/testUtils/formDataShim.test.ts Adds focused tests for disabled controls, unchecked controls, button inputs, multi-selects, and shim read/write methods.
tests/jest/components/BookEditForm.test.tsx Migrates the book edit form tests to React Testing Library and uses the shared form submit shim.
tests/jest/components/SitewideAnnouncements.test.tsx Migrates the connected announcements panel tests and checks the submitted announcements payload through the shared shim.
tests/jest/components/ComplaintForm.test.tsx Migrates complaint form behavior tests to user-facing interactions with the shared form submit shim installed.
tests/jest/components/LibraryEditForm.test.tsx Migrates library edit form tests and uses the shared form submit shim for payload assertions.
tests/jest/components/LibraryRegistrationForm.test.tsx Migrates library registration form tests and installs the shared form submit shim consistently with sibling suites.

Reviews (6): Last reviewed commit: "Rewrite the instance-poking presentation..." | Re-trigger Greptile

Comment thread tests/jest/components/BookEditForm.test.tsx Outdated
Comment thread tests/jest/components/SitewideAnnouncements.test.tsx Outdated
@codecov-commenter

codecov-commenter commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.47%. Comparing base (7014b93) to head (ec398ca).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #315      +/-   ##
==========================================
+ Coverage   90.19%   90.47%   +0.27%     
==========================================
  Files         205      205              
  Lines        7610     7610              
  Branches     1664     1664              
==========================================
+ Hits         6864     6885      +21     
+ Misses        437      423      -14     
+ Partials      309      302       -7     
Flag Coverage Δ
jest 90.47% <ø> (+0.27%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@jonathangreen jonathangreen force-pushed the chore/rewrite-instance-poking-test-rtl branch 2 times, most recently from 314c5da to 2e7091e Compare July 9, 2026 22:24
@jonathangreen jonathangreen requested a review from a team July 10, 2026 13:23
@jonathangreen jonathangreen force-pushed the chore/rewrite-instance-poking-test-rtl branch from 2e7091e to 1bb0927 Compare July 10, 2026 14:09
@jonathangreen jonathangreen changed the base branch from chore/jest-remove-mocha to chore/formdata-shim-test-util July 10, 2026 14:09
@jonathangreen jonathangreen force-pushed the chore/formdata-shim-test-util branch from d45ae2c to 3739c04 Compare July 10, 2026 14:43
@jonathangreen jonathangreen force-pushed the chore/rewrite-instance-poking-test-rtl branch from 1bb0927 to 618357e Compare July 10, 2026 14:53
jonathangreen added a commit that referenced this pull request Jul 10, 2026
## Description

Adds one shared FormData test shim at
`tests/jest/testUtils/formDataShim.ts`, plus its spec test.

The unit jsdom environment installs undici's `FormData`, which throws on
`new FormData(formElement)`. The reusable-components `Form` builds
FormData that way on submit, so any test that asserts a submitted
payload needs a stand-in. Previously each migration branch carried its
own inline copy; this consolidates them into one.

The shim mirrors the browser's `new FormData(form)`
**successful-controls** rules:
- named, non-disabled controls only;
- checkboxes/radios contribute only when checked;
- submit/reset/button/image/file inputs excluded;
- every selected option of a `<select multiple>` included.

It also exposes the standard read/write/iterate surface
(`get`/`getAll`/`has`/`append`/`set`/`delete`/`forEach`/`keys`/`values`/`entries`/iterator).
`installFormDataShim()` swaps it in around a suite (`beforeAll`) and
restores the real `FormData` afterward (`afterAll`).

`formDataShim.test.ts` (8 cases) pins the successful-controls behavior —
disabled omitted, checkbox only when checked, radio group, submit
excluded, multi-select expansion, and the read/write API — so the
fidelity can't silently regress.

## Motivation and Context

Part of the mocha/chai/enzyme → jest/RTL migration. Extracting the shim
into its own small, self-tested base PR lets #314, #315, and #316 depend
on one implementation via `installFormDataShim()` instead of duplicating
(and independently drifting) a local shim in each — while keeping this
base stable so those stacked PRs don't churn.

## How Has This Been Tested?

`npx jest --selectProjects=unit --testPathPattern formDataShim` — 8/8
green.

## Checklist:

- [x] I have updated the documentation accordingly.
- [x] All new and existing tests passed.
Base automatically changed from chore/formdata-shim-test-util to main July 10, 2026 20:22
…ibrary

22 more enzyme suites, the ones that reached into class internals with
instance()/setState()/state(): the announcement/color/table widgets, the
complaint and troubleshooting views, the library/book/list edit forms, the
diagnostics and reset-adobe panels, and ContextProvider.

Each internal poke becomes an observable assertion. instance() reads of computed
values (Announcement.format, ClassificationsTable.readableType, ContextProvider
methods) are driven through the real inputs and asserted on the rendered DOM.
setState()/instance()-method calls become the user action that causes them — a
checkbox click, a Save click, a tab click — and the assertion moves to the DOM
that reflects the new state. Ref-based getValue() APIs (Autocomplete,
ColorPicker) are exercised through a real ref, since production calls them that
way.

ContextProvider is rewritten around a capturing consumer child that reads the
legacy context the component provides, rather than the component instance —
pathFor/prepareCollectionUrl/prepareBookUrl are tested through that context, and
the legacy `global.jsdom.reconfigure` origin trick (no equivalent in the unit
env) is dropped: those helpers strip only the origin, so the real jsdom origin
yields the same encoded output.

Connected panels (Complaints, PatronAuthServices, SitewideAnnouncements,
Classifications, BookDetailsEditor, CustomListsForBook, ResetAdobeId,
SelfTestsTabContainer's category) render their connected default export with
fetch stubbed and awaited, so mapStateToProps/mapDispatchToProps stay covered —
the wave-2 lesson, applied up front. The gate still caught two gaps: a Complaints
type-format fallback (added a non-matching-type case) and SelfTests's own connect
wiring, which only the SelfTestsTabContainer full-mount covered — so
SelfTestsTabContainer is deferred (original retained) until SelfTests is migrated
connected in the final wave.

Coverage rises to 94.68% (+38 lines over baseline), no previously-covered line
lost.

36 legacy suites remain: the HARD-heavy set, the ten that must merge into
existing tests/jest files, and the three deferred containers.
@jonathangreen jonathangreen force-pushed the chore/rewrite-instance-poking-test-rtl branch from 618357e to ec398ca Compare July 11, 2026 11:41
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.

2 participants