Skip to content

Convert the User screen test suites from Enzyme to React Testing Library - #398

Merged
cigamit merged 3 commits into
ctrliq:mainfrom
blaipr:feature/rtl-batch-user
Jun 14, 2026
Merged

cigamit merged 3 commits into
ctrliq:mainfrom
blaipr:feature/rtl-batch-user

Conversation

@blaipr

@blaipr blaipr commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

Converts all 22 test suites under screens/User/ (~1,100 lines) from Enzyme to React Testing Library, continuing the migration started in #385. Zero enzyme imports remain in the directory. Tests now drive the real UI through roles and labels instead of reaching into component props, so they survive the React 18 upgrade that I'm pursuing.

Infrastructure fixes the conversion surfaced

  • setupTests.js: @nteract/mockument replaces document.createRange with a stub missing cloneRange and most of the Range API, which crashes @testing-library/user-event's pointer handling. Restored jsdom's real Range and grafted on the two rect methods the editor components actually need from the stub.
  • testUtils/rtlContexts.js gains settleTooltips(): closing a PF Modal restores focus (on a timeout) to the button that opened it; if that button is wrapped in a PF Tooltip, the tooltip engages after its 300ms entry delay and a tooltip still pending at unmount logs "state update on unmounted component" into the next test — an order-dependent failure that bisects to nothing. Applied in the three list suites that end shortly after closing an error modal (UserTeamList, UserList, UserTokenList). Every list-screen conversion from here on will want this helper.
  • testUtils/rtlContexts.js gains assertDetail(): shared label/value assertion for <Detail> pairs; UserDetail and UserTokenDetail use it now, the other ~10 detail suites will as they convert.
  • DisassociateButton: PF4 Tooltip doesn't accept ouiaId and forwards it to a DOM div, which React warns about. Enzyme's simulate('click') never opened the tooltip so this went unnoticed for years; user-event's real hover found it. One-line removal — nothing selects on that id.
  • Deleted the orphaned enzymeHelpers.test.jsx.snap that made Jest print "1 snapshot file obsolete" on every run.

Gotchas for the next conversion batches

  1. resetMocks: true strips jest.fn(() => …) implementations from jest.mock factories between tests — use a plain function (capture props via a mock-prefixed variable if you need them).
  2. PF required-field labels render as "Label *" — anchor with getByLabelText(/^Label/), exact match fails.
  3. An aria-label overrides visible text as the accessible name (the Edit link is name: 'edit').
  4. PF Modal's self-referencing aria-labelledby inflates the dialog's accessible name — match dialogs with a regex, not the exact title.
  5. Tests that end right after closing a modal need settleTooltips() (see above).
  6. Run with TZ=UTC like the npm script does — bare npx jest fails the date assertions with a confusing 1-hour offset.

Fully independent of every other open PR.

ISSUE TYPE

  • Bug, Docs Fix or other nominal change

COMPONENT NAME

  • UI

ASCENDER VERSION

awx: 25.4.1.dev17+g3946cc5.d20260613

ADDITIONAL INFORMATION

npm --prefix awx/ui run lint     # clean
npm --prefix awx/ui run test     # 546 suites passed (1 skipped), 2878 tests passed (19 skipped)

The converted suites plus testUtils were additionally run 3× in a loop to shake out order/timing flakes (106 tests green each pass), since the tooltip bug class this PR fixes only shows up across test boundaries.

Converts all 22 test suites under screens/User/ to RTL, continuing the
migration started in ctrliq#385. Tests drive the real UI through roles and
labels instead of reaching into component props.

Infrastructure fixes surfaced by the conversion:

- setupTests.js: mockument's createRange stub lacks cloneRange and most
  of the Range API, crashing @testing-library/user-event's pointer
  handling. Restore jsdom's real Range with the two rect-method stubs
  the editor components need.
- testUtils/rtlContexts.js gains settleTooltips(): a PF Modal close
  restores focus to a Tooltip-wrapped button, and a tooltip pending at
  unmount logs a state-update warning into the NEXT test, producing
  order-dependent failures. Applied in the three list suites that end
  right after closing an error modal.
- testUtils/rtlContexts.js gains assertDetail() shared by the detail
  suites.
- DisassociateButton: drop the invalid ouiaId prop on PF Tooltip that
  leaked to the DOM (enzyme never opened the tooltip; user-event's real
  hover exposed the warning).
- Delete the orphaned enzymeHelpers.test.jsx.snap behind the perpetual
  'snapshot file obsolete' notice.

Copilot AI 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.

Pull request overview

Migrates the awx/ui/src/screens/User/ test suites from Enzyme to React Testing Library, aligning the UI test strategy with React 18+ compatibility and improving resilience by asserting on accessible roles/labels rather than component internals.

Changes:

  • Converted User screen tests from Enzyme mountWithContexts/wrapper assertions to RTL renderWithContexts + screen/userEvent interactions.
  • Enhanced RTL test infrastructure (settleTooltips(), assertDetail()) and updated Jest setup to restore a functional document.createRange for @testing-library/user-event.
  • Removed an obsolete Jest snapshot and eliminated a Tooltip prop that caused React unknown-prop warnings.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
awx/ui/testUtils/rtlContexts.js Expands RTL helpers (incl. tooltip settling and shared Detail assertions).
awx/ui/testUtils/snapshots/enzymeHelpers.test.jsx.snap Deletes obsolete Enzyme snapshot output.
awx/ui/src/setupTests.js Restores jsdom Range behavior to support RTL user-event pointer handling.
awx/ui/src/screens/User/UserTokens/UserTokens.test.js Converts token creation modal test to RTL interactions.
awx/ui/src/screens/User/UserTokenList/UserTokenListItem.test.js Converts list-row rendering assertions to RTL.
awx/ui/src/screens/User/UserTokenList/UserTokenList.test.js Converts list selection/deletion/error flows to RTL; uses settleTooltips().
awx/ui/src/screens/User/UserTokenDetail/UserTokenDetail.test.js Converts detail rendering and delete/error flows to RTL; uses assertDetail().
awx/ui/src/screens/User/UserTokenAdd/UserTokenAdd.test.js Converts add-form flows to RTL using real form interactions.
awx/ui/src/screens/User/UserToken/UserToken.test.js Converts token screen tab rendering/API call assertions to RTL.
awx/ui/src/screens/User/UserTeams/UserTeamListItem.test.js Converts team list row rendering checks to RTL.
awx/ui/src/screens/User/UserTeams/UserTeamList.test.js Converts team association/disassociation flows to RTL; uses settleTooltips().
awx/ui/src/screens/User/Users.test.js Converts breadcrumb/header assertions to RTL with a resetMocks-safe mock.
awx/ui/src/screens/User/UserRoles/UserRolesListItem.test.js Converts role list row assertions (including chip affordances) to RTL.
awx/ui/src/screens/User/UserRoles/UserRolesList.test.js Converts roles list flows (wizard, disassociate, errors, empty state) to RTL.
awx/ui/src/screens/User/UserOrganizations/UserOrganizations.test.js Replaces Enzyme shallow test with RTL render + mock child list.
awx/ui/src/screens/User/UserOrganizations/UserOrganizationListItem.test.js Converts organization list row rendering to RTL.
awx/ui/src/screens/User/UserOrganizations/UserOrganizationList.test.js Converts organization list route-based mount + API assertions to RTL.
awx/ui/src/screens/User/UserList/UserListItem.test.js Converts user list-row permission-based rendering assertions to RTL.
awx/ui/src/screens/User/UserList/UserList.test.js Converts Users list load/select/delete/error flows to RTL; uses settleTooltips().
awx/ui/src/screens/User/UserEdit/UserEdit.test.js Converts edit-form save/cancel flows to RTL interactions.
awx/ui/src/screens/User/UserDetail/UserDetail.test.js Converts detail assertions + edit/delete/error flows to RTL; uses assertDetail().
awx/ui/src/screens/User/UserAdd/UserAdd.test.js Converts add-form flows to RTL and accounts for PF required-label rendering.
awx/ui/src/screens/User/User.test.js Converts User screen routing/tab visibility/error route test coverage to RTL.
awx/ui/src/screens/User/shared/UserTokenForm.test.js Converts shared token form behavior tests to RTL (lookup + validation).
awx/ui/src/screens/User/shared/UserForm.test.js Converts shared user form behavior tests to RTL (org lookup + required fields).
awx/ui/src/components/DisassociateButton/DisassociateButton.js Removes unsupported Tooltip prop that produced React DOM warnings.

Comment thread awx/ui/testUtils/rtlContexts.js
Comment thread awx/ui/src/screens/User/UserRoles/UserRolesList.test.js
Comment thread awx/ui/src/screens/User/UserRoles/UserRolesList.test.js
blaipr added 2 commits June 13, 2026 13:42
# Conflicts:
#	awx/ui/testUtils/rtlContexts.js
…values

The submit handler mutated Formik's values object (deleting password,
assigning is_superuser/is_system_auditor). Deleting password flips the
still-mounted password field from controlled to uncontrolled after a
successful submit, which React warns about — surfaced once the merged
react-router-dom-v5-compat bridge drives a re-render of the mounted form
on navigate(). Destructure into a fresh submitValues and mutate only the
copy; Formik's own state is left intact.
@blaipr

blaipr commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main now that the react-router v6 compat migration has merged. Two things the merge required:

  • testUtils/rtlContexts.js conflict — both branches edited this file (the compat work wired a controlled CompatV6Layer into renderWithContexts; this branch added the settleTooltips/assertDetail helpers and dropped the dead Lingui v5 plural block). Resolved by keeping both, and removing the now-unnecessary eslint-disable import-x/prefer-default-export since the file exports three symbols.
  • UserForm controlled-input fix — with the compat bridge live, a successful submit's navigate() now drives a re-render of the still-mounted form, which surfaced a latent bug: the submit handler mutated Formik's values object (delete values.password), flipping the password field from controlled to uncontrolled and tripping the setupTests console-error trap. Fixed by building the submit payload from a copy and leaving Formik's state intact.

Full suite green against current main: 546 suites passed (1 skipped), 2,902 tests passed (5 skipped), lint clean.
GitHub now reports the branch mergeable.

@cigamit
cigamit merged commit 14a1b77 into ctrliq:main Jun 14, 2026
cigamit pushed a commit that referenced this pull request Jun 16, 2026
* enzyme -> RTL: convert the CredentialType screen suites

Convert the six enzyme test suites in screens/CredentialType to React
Testing Library via renderWithContexts (continuing the step-3 directory
conversions after screens/User in #398):

- CredentialTypeListItem, CredentialTypeList, CredentialTypeDetails,
  CredentialTypeAdd, CredentialTypeEdit, and the shared CredentialTypeForm.
- Container suites (Add/Edit) stub the shared form and drive its
  onSubmit/onCancel/submitError props, matching how the enzyme tests treated
  the form as a unit (it has its own suite).
- Delete flows interact for real; PF4's Modal aria-hides the tree under
  jsdom, so the confirm button is queried by label and clicked via fireEvent.
- Drop a prop-inspection-only assertion (ToolbarDeleteButton
  deleteDetailsRequests count) that has no DOM-observable behavior.

33 tests pass; screens/CredentialType no longer references enzyme.

* Address Copilot review on the CredentialType RTL suites

- CredentialTypeForm: the value-update test now exercises both Name and
  Description and asserts the inputs exist before typing.
- CredentialTypeListItem: pass rowIndex so the selection checkbox has a
  stable accessible name, and assert it by name ('Select row 0').
- CredentialTypeList: select row checkboxes by accessible name
  ('Select row 0') instead of a brittle getAllByRole('checkbox') index.
cigamit pushed a commit that referenced this pull request Jun 18, 2026
* enzyme -> RTL: convert the NotificationTemplate screen suites

Convert the four enzyme test suites in screens/NotificationTemplate to React
Testing Library via renderWithContexts (continuing step 3 after #398/#433):

- NotificationTemplateListItem, NotificationTemplateList,
  NotificationTemplateDetail, and the shared NotificationTemplateForm.
- Bulk-delete uses the PF4-modal-in-jsdom pattern (confirm by label +
  fireEvent). The test-notification toast asserts on its title text since the
  PF AlertGroup toast has no role=alert.
- The poll-and-toast flow uses jest.runAllTimersAsync to drain the
  test() -> setTimeout -> readDetail -> onAddToast chain under fake timers.
- The form's react-ace editors are stubbed to render their value (jsdom
  cannot query ace content), and OrganizationLookup's async fetch is settled
  in act; secret-revert is covered for all six notification types.

31 tests pass; screens/NotificationTemplate no longer references enzyme.

* Address Copilot review comments
cigamit pushed a commit that referenced this pull request Jun 18, 2026
* enzyme -> RTL: convert the Template screen suites

Convert all 47 enzyme test suites under screens/Template to React Testing
Library (the last remaining screens/ directory), matching the
renderWithContexts + screen/fireEvent/waitFor pattern of the prior
conversions (#398, #433-#453).

Covers: Templates / Template / WorkflowJobTemplate + TemplateSurvey, the
JobTemplate and WorkflowJobTemplate Add/Edit/Detail screens, the shared
JobTemplateForm / WorkflowJobTemplateForm / WebhookSubForm, the Survey editor
(list/item/question add-edit-form/reorder/toolbar/multiple-choice), and the
entire WorkflowJobTemplateVisualizer tree (graph/node/link/start/toolbar, the
link and node modals, and the node-type resource lists).

d3/SVG and PatternFly portals are asserted via element ids,
data-cy / data-ouia-component-id, foreignObject content and accessible names
rather than geometry or component-name lookups. Two interactions that are
infeasible in jsdom (native drag reorder; one visualizer save-error scenario
that passes in the browser) are kept as documented skips.

48 suites / 257 tests pass (2 documented skips); no production code changed.

* Address Copilot review feedback on PR #483 RTL tests

Remove the duplicated Delete All dispatch test; fix the 'incrimented' and 'survery' test-name typos.

* Remove accidentally committed node_modules symlink

awx/ui/node_modules was committed as a self-referential symlink; .gitignore only excludes the directory contents, not the symlink itself. Untrack it so checkout doesn't clobber a real node_modules.

* Cede the 3 Template entry/detail test files to PR #484

Templates.test.js, Template.test.js and WorkflowJobTemplate.test.js are the only files this PR shared with #484 (Templates route tree -> v6). #484 now owns and RTL-converts those three (mounted for v6). Revert them here so the two PRs touch disjoint files and are independently mergeable in either order; this PR still converts the rest of screens/Template to RTL.
@cigamit cigamit self-assigned this Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants