Skip to content

Port latest master to frontend-base (incl. Redux-to-React-Query migration)#1641

Open
arbrandes wants to merge 17 commits intoopenedx:frontend-basefrom
arbrandes:frontend-base-master-ports
Open

Port latest master to frontend-base (incl. Redux-to-React-Query migration)#1641
arbrandes wants to merge 17 commits intoopenedx:frontend-basefrom
arbrandes:frontend-base-master-ports

Conversation

@arbrandes
Copy link
Contributor

@arbrandes arbrandes commented Mar 6, 2026

Description

As per the title, the intent here is to port latest master into the frontend-base branch. However, during the porting process several issues or opportunities for enhancement were identified: this PR contains corresponding changes, each in a separate commit for possible use in future backports.

Summary

  • Ports latest master to the frontend-base branch, adapting all imports, config access, and component patterns to @openedx/frontend-base
  • Includes the full Redux-to-React-Query + React Context migration (0d709d15), eliminating all Redux, Redux-Saga, and Reselect dependencies
  • Fixes 4 pre-existing bugs from the master migration (stale closure in validateInput, navigate() during render, wrong args to setEmailSuggestionContext, double RedirectLogistration)
  • Applies React Query best practices: staleTime on TPA query, payload in query key, enabled flag for conditional fetches, mount-only setThirdPartyAuthContextBegin
  • Fixes LoginContext setter types and removes dead PASSWORD_RESET_ERROR code

Tally

Pre-migration ports (7 commits): frontend-base alpha bump, injectIntl-to-useIntl refactor, forbidden username handling, username suggestions alignment, Login page Slot, missing space fix, name field length check.

Redux-to-React-Query migration (1 large commit): Replaces Redux + Redux-Saga with React Query + React Context across all modules (login, registration, forgot-password, reset-password, progressive-profiling, common-components). 123 files changed, ~8.5K insertions, ~5.8K deletions.

Bug fixes (5 commits): All bugs were pre-existing in the master migration commit and were found during review.

React Query improvements (3 commits): Performance and correctness improvements to the TPA query hook and LoginContext types.

LLM usage notice

Built with assistance from Claude models (mostly Opus 4.6, at one point with the "[1m]" beta context window).

Work Log

Pre-migration ports

Seven commits were cherry-picked and adapted from master:

  1. frontend-base alpha bump@openedx/frontend-base from alpha.13 to alpha.14
  2. injectIntl to useIntl — Ported 4 master commits (db3d007c, 43a584eb, 5bd6926f, 76e400f0), removing the injectIntl HOC from 20 files. Skipped files in src/recommendations/ (doesn't exist on this branch).
  3. Forbidden username handling — Added FORBIDDEN_USERNAME constant, switch case in RegistrationFailure, and i18n message (with proper description field per frontend-base convention).
  4. Username suggestions alignment — Added ARIA roles, tabIndex, and absolute-positioned styling for suggestions dropdown.
  5. Login page Slot — Refactored LoginPage and Logistration from connect() to hooks. Used Slot from @openedx/frontend-base (not PluginSlot), with slot ID org.openedx.frontend.slot.authn.loginComponent.v1.
  6. Missing space fix — Removed trailing space from message, added className="mx-1" to span.
  7. Name field length check — Added MAX_FULL_NAME_LENGTH = 255 validation.

Redux-to-React-Query migration

Ported master commit 0d709d15 (and fix 93bd0f24) in 5 phases:

Phase 1 — New data layer (additive): Created API files (api.ts), hook files (apiHook.ts), query keys, context providers, and types for all 6 modules. Added @tanstack/react-query as a peerDependency. 31 new tests passing.

Phase 2 — Common components & login: Migrated PasswordField (uses optional RegisterContext pattern since it's shared across flows), LoginPage, and Logistration to React Query/Context. Deleted Redux files for login and common-components. Rewrote LoginPage.test.jsx and Logistration.test.jsx. Resolved circular dependency in site.config.test.tsx by using string 'test' instead of EnvironmentTypes. 409 tests passing.

Phase 3 — Registration: Migrated RegistrationPage and all field components (CountryField, EmailField, NameField, UsernameField). Deleted all registration Redux files. Rewrote 8 test files (110 tests). 399 tests passing.

Phase 4 — Password flows, progressive profiling & final Redux removal: Migrated ForgotPasswordPage, ResetPasswordPage, and ProgressiveProfiling. Deleted all remaining Redux infrastructure (configureStore.js, root reducer/sagas, reduxUtils.js). Removed ReduxProvider from Main.tsx. Cleaned redux, redux-saga, reselect, redux-logger, redux-mock-store, @redux-devtools/extension from dependencies. 410 tests passing.

Phase 5 — Fix + cleanup: Applied 93bd0f24 fix (registration error priority over inline validations). Deleted orphaned progressive-profiling/data/service.js. 411 tests passing.

Bug fixes (post-migration review)

A full review compared the port against the master commit, the frontend-base migration guide, and React Query best practices. All bugs found were pre-existing in master commit 0d709d15:

  1. setEmailSuggestionContext wrong args — Called with {suggestion, type} object instead of two string args. One-line fix.
  2. navigate() during renderResetPasswordPage called navigate() in the render phase. Wrapped in useEffect.
  3. Stale closure in validateInput — Direct state mutation of formErrors object. Replaced with functional setFormErrors updater.
  4. Double RedirectLogistration — Both redirect components could render simultaneously in ProgressiveProfiling. Made them mutually exclusive.

React Query improvements

  1. staleTime on TPA query — TPA context is effectively static per session; added staleTime to prevent unnecessary background refetches.
  2. Payload in TPA query key — Query key now includes the payload to prevent serving stale cache when tpa_hint or other params change.
  3. enabled flag on TPA query — Prevents unnecessary fetches when the query result won't be used.
  4. Mount-only setThirdPartyAuthContextBegin — Separated from data sync effect to prevent flash of PENDING state on re-renders.
  5. LoginContext setter types — Fixed types from (fields: T) => void to React.Dispatch<React.SetStateAction<T>>. Removed dead PASSWORD_RESET_ERROR code path.

arbrandes and others added 17 commits March 6, 2026 09:06
Port of master commits db3d007, 43a584e, 5bd6926, and 76e400f.

Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port of master commit 58ec90a.

Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port of master commit ffb8a2d.

Co-Authored-By: Kyrylo Hudym-Levkovych <kyr.hudym@kyrs-MacBook-Pro.local>
Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This change adds a Slot for the login page allowing it to be customised.

Since this touched the Login Page, LoginPage and Logistration have also
been refactored to move away from redux connect.

Adapted for frontend-base: uses Slot from @openedx/frontend-base instead
of PluginSlot from @openedx/frontend-plugin-framework, slot files live
under src/slots/, and the slot ID follows the frontend-base naming
convention (org.openedx.frontend.slot.authn.loginComponent.v1).

Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Artur Filippovskii <118079918+filippovskii09@users.noreply.github.com>
Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Redux + Redux-Saga with React Query (useMutation/useQuery) for
server state and React Context for UI/form state across all modules:
login, registration, forgot-password, reset-password, progressive-
profiling, and common-components.

Port of master commits 0d709d1 and 93bd0f2, adapted for
@openedx/frontend-base:
- getSiteConfig() instead of getConfig()
- useAppConfig() for per-app configuration
- @tanstack/react-query as peerDependency (shell provides QueryClient)
- CurrentAppProvider instead of AppProvider

Also fixes EnvironmentTypes circular dependency in site.config.test.tsx
by using string literal instead of enum import.

Co-Authored-By: Jesus Balderrama <jesus.balderrama.wgu@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gestionClick

The function expects two string arguments (suggestion, type) but was
being called with a single object, corrupting the email suggestion state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Calling navigate() during the render phase triggers state updates in
the router, causing React warnings and potential infinite render loops
in strict mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…losure

validateInput was directly mutating the formErrors state object and
then spreading it. When handleSubmit called validateInput twice in
succession, the second call operated on stale closure values because
React batches state updates. Also fixed handleOnFocus for the same
issue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When shouldRedirect was true and welcomePageContext.nextUrl was truthy,
both RedirectLogistration components rendered simultaneously, causing a
double redirect attempt. The first block was a subset of the second, so
removing it is sufficient.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…_ERROR code

LoginContext setFormFields/setErrors are useState setters that accept
updater functions, so their types should be Dispatch<SetStateAction<...>>
rather than plain function signatures.

PASSWORD_RESET_ERROR was checked in ResetPasswordPage but no code path
ever set status to that value, making the conditions dead code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The TPA context is effectively static per session. Adding a 5-minute
staleTime prevents unnecessary background refetches when navigating
between login/register tabs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The query key only included pageId, so if the payload (tpa_hint, query
params, etc.) changed while pageId stayed the same, React Query would
serve stale cached data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
It was being called on every data sync effect run, causing a flash of
PENDING state even when TPA data was already available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In ProgressiveProfiling, the TPA query fired even when
registrationEmbedded was false and the result was ignored. Added an
enabled option to useThirdPartyAuthHook and set it to
registrationEmbedded in ProgressiveProfiling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@arbrandes arbrandes changed the title feat: port master commits to frontend-base (incl. Redux-to-React-Query migration) Port latest master to frontend-base (incl. Redux-to-React-Query migration) Mar 6, 2026
@arbrandes arbrandes linked an issue Mar 6, 2026 that may be closed by this pull request
@codecov
Copy link

codecov bot commented Mar 6, 2026

Codecov Report

❌ Patch coverage is 86.27451% with 77 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (frontend-base@2337a71). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...ofiling/components/ProgressiveProfilingContext.tsx 0.00% 22 Missing ⚠️
...on-components/components/ThirdPartyAuthContext.tsx 61.53% 10 Missing ⚠️
src/forgot-password/ForgotPasswordPage.jsx 82.14% 5 Missing ⚠️
src/login/data/apiHook.ts 80.76% 5 Missing ⚠️
src/register/components/RegisterContext.tsx 93.58% 5 Missing ⚠️
src/common-components/PasswordField.jsx 71.42% 4 Missing ⚠️
src/register/RegistrationPage.jsx 89.47% 4 Missing ⚠️
src/reset-password/data/apiHook.ts 89.47% 4 Missing ⚠️
src/common-components/data/api.ts 25.00% 3 Missing ⚠️
src/register/components/RegistrationFailure.jsx 0.00% 3 Missing ⚠️
... and 8 more
Additional details and impacted files
@@               Coverage Diff                @@
##             frontend-base    #1641   +/-   ##
================================================
  Coverage                 ?   92.46%           
================================================
  Files                    ?       92           
  Lines                    ?     2018           
  Branches                 ?      573           
================================================
  Hits                     ?     1866           
  Misses                   ?      149           
  Partials                 ?        3           

☔ View full report in Codecov by Sentry.
📢 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.

setFormErrors('');
sendForgotPassword(email, {
onSuccess: (data, emailUsed) => {
setStatus('complete');
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this hardcoded status was already here, but maybe is worth it to import the value from constants.
also in the onError

@brian-smith-tcril
Copy link
Contributor

Review assisted by @claude (I used claude to talk through changes, record my thoughts, keep track of where I was throughout the review process, and assist in writing this summary)

Reviewed all 17 commits commit-by-commit, comparing each against the master commit(s) it ports. The full review log is in the details block below.

Overall

The migration is solid. The Redux→React Query/Context rewrite (commit 8) is the bulk of the change and is largely correct. Commits 9–17 are focused fixes and improvements with no master equivalents — most of them address real bugs or performance issues that also exist on master and are worth porting back.

A few things in the PR need follow-up attention, noted below.


Follow-ups for this PR

Definitely fix

  • should redirect to dashboard if features flags are configured but no optional fields are configured (RegistrationPage.test.jsx) — master is async with waitFor asserting on a cookie; commit 8 is sync and still uses expect(window.location.href).toBe(dashboardUrl). Looks like an incomplete port.
  • should redirect to progressive profiling page if optional fields are configured (RegistrationPage.test.jsx) — same issue; master is async with onSuccess wiring and waitFor on the cookie; commit 8 is sync. Looks like an incomplete port.
  • Heading assertions not updated to use tab/button roles in Logistration.test.jsx (should render login/register headings when SHOW_REGISTRATION_LINKS is disabled and should render only login page when public account creation is disabled) — master updated these; commit 8 still uses old screen.getByRole('heading', { level: 3 }) assertions.
  • eslint-disable-line react-hooks/exhaustive-deps added to registrationResult useEffect in RegistrationPage.jsx — not on master. Fine for landing, but the deps array needs to be verified and the suppress removed or justified.
  • fireEvent vs userEvent — new/updated tests across NameField.test.jsx, ForgotPasswordPage.test.jsx, LoginPage.test.jsx, ProgressiveProfiling.test.jsx, EmailField.test.jsx, UsernameField.test.jsx, and ResetPasswordPage.test.jsx use fireEvent instead of userEvent. Not a blocker for this PR, but should be cleaned up.

Worth investigating / is this intentional?

  • should call internal state setters on successful registration (RegistrationPage.test.jsx) — async on master, sync in commit 8. Could be intentional.
  • Hardcoded cookie name in should check user retention cookie (RegistrationPage.test.jsx) — master uses getConfig().USER_RETENTION_COOKIE_NAME; commit 8 hardcodes 'authn-returning-user=true'. Hardcoding test data may be fine, but worth aligning.
  • Duplicate test name resolved differently (RegistrationPage.test.jsx) — master has two tests both named should redirect to url returned in registration result after successful account creation; commit 8 renames the second to should wire up onSuccess callback for registration mutation and makes it non-async. Is this intentional?
  • SESSION_COOKIE_DOMAIN: '' added to mergeAppConfig in RegistrationPage.test.jsx — not on master. Is this required for tests to pass on frontend-base?
  • ENABLE_DYNAMIC_REGISTRATION_FIELDS: true added to mergeAppConfig in RegistrationPage.test.jsx — not on master. Is this needed, and should it be added to master too?
  • 3 missing tests in Logistration.test.jsx: should call authService getCsrfTokenService on component mount, should send correct page events for login and register when handling institution login, should handle institution login with string parameters correctly. Intentional?
  • Test fixture data not fully ported in Logistration.test.jsxsecondaryProviders and default TPA mock still use minimal/old data. Is this intentional?
  • Missing tests in LoginPage.test.jsx: should dismiss reset password banner on form submission, should handle successful login, should handle SSO login success, should handle successful authentication via SSO — all present on master, absent in commit 8. Likely oversights but could be intentional.
  • sendPageEvent not imported/asserted in ProgressiveProfiling.test.jsx — master asserts on it in should call analytics functions on component mount; commit 8 doesn't. How is sendPageEvent handled on frontend-base?
  • honor_code removed from formPayload in ConfigurableRegistrationForm.test.jsx — not removed on master. Do we not have honor_code on frontend-base?
  • BrowserRouterMemoryRouter in test wrappers across several files — both work, but we should decide on a recommended pattern for frontend-base tests.
  • Late getLocale import with eslint-disable-next-line import/first in ThirdPartyAuth.test.jsx — feels wrong; worth investigating whether it was done to address a specific issue with the standard import order.
  • @testing-library/jest-dom not in frontend-base dependencies — new test files use toBeTruthy() instead of toBeInTheDocument(). If the expressiveness of jest-dom matchers isn't needed, toBeTruthy() is fine, but we should establish a best practice for frontend-base tests.
  • PasswordField context couplinguseRegisterContextOptional() + noopFn fallbacks work, but if we're going to make this a pattern, it should be intentional with good DX. Worth discussing.

Nits

  • useThirdPartyAuthHook mocked in ConfigurableRegistrationForm.test.jsx and RegistrationFailure.test.jsx but never referenced in any test.
  • Over-specified mockContextValue in progressive-profiling/data/apiHook.test.ts.
  • Repeated inline useThirdPartyAuthContext mock in Logistration.test.jsx — master uses a shared constant.

Items to port back to master

These bugs/improvements were caught or introduced during the frontend-base port and don't yet exist on master:

  • TPA query fires unconditionally in ProgressiveProfiling.jsx — fires even when registrationEmbedded is false. Fixed in commit 17 (4e7f95be).
  • setThirdPartyAuthContextBegin called on every data sync in LoginPage.jsx — causes a flash of PENDING state when TPA data is already cached. Fixed in commit 16 (0a787708).
  • payload not included in TPA query key — payload changes with same pageId would serve stale data. Fixed in commit 15 (cecbd7c5).
  • staleTime missing from TPA context query — unnecessary background refetches between login/register tabs. Added in commit 14 (ad8afe7b).
  • PASSWORD_RESET_ERROR dead code in ResetPasswordPage.jsx — no code path sets status to this value. Removed in commit 13 (537c47b7).
  • Duplicate RedirectLogistration in ProgressiveProfiling.jsx — redundant block gated on shouldRedirect && welcomePageContext.nextUrl has existed since PR feat: param based redirection #993; broader shouldRedirect block covers it. Fixed in commit 12 (5f13df70).
  • Duplicate should show single sign on provider button test in ThirdPartyAuth.test.jsx. Fixed in commit 8 (2bfa6447).
  • mockResolvedValueOnce({ success: true }) in progressive-profiling/data/apiHook.test.tspatchAccount returns void; should be undefined.
  • ResetPasswordPayload.params type — commit 8 (2bfa6447) widens to Record<string, string | boolean>; master still has Record<string, string>. Test payloads use boolean is_account_recovery in both, but api.ts converts it to a string. Worth tracing call sites to confirm the correct type.

Pre-existing gaps (not introduced by this PR)

  • Several tests absent from frontend-base since the initial refactor: migrate to frontend-base commit (8f8531a2): one test in Logistration.test.jsx and 11 tests in ProgressiveProfiling.test.jsx (see full log for details).
  • LoginComponentSlot (src/slots/LoginComponentSlot/index.jsx) is JSX/PropTypes rather than TypeScript.
  • LoginComponentSlot README has no frontend-base example code yet.
  • MAX_FULL_NAME_LENGTH is hardcoded client-side; backend validation exists but frontend doesn't read the limit from it.
  • ThirdPartyAuthAlert.test.jsx uses snapshot testing.
  • injectIntl still exported from frontend-base despite this PR reducing usage.

Full review log

PR #1641 Review Log

PR: #1641
Title: Port latest master to frontend-base (incl. Redux-to-React-Query migration)
Base branch: frontend-base
Head branch: frontend-base-master-ports

Goal

Review each commit in the PR by comparing it against the original master commit(s) it ports,
verifying that the adaptation to @openedx/frontend-base is correct and no unintended changes
were introduced.

Approach

@brian-smith-tcril reviews each commit by comparing master commits to PR commit diffs. Claude assists by looking up file lists, master commit details, and logging findings. Full diff analysis via Claude subagent is avoided where possible to keep token usage reasonable.


Commit Index

Group A — Pre-migration ports from master (commits 1–7)

# PR Commit SHA Headline Master Commit(s) Status
1 d5b1df7c chore: bump frontend-base to 1.0.0-alpha.14 (frontend-base-only, no master equiv)
2 b0ee418a refactor: replace injectIntl with useIntl db3d007c, 43a584eb, 5bd6926f, 76e400f0
3 046abb77 chore: handle forbidden username exceptions on registration 58ec90ac
4 80ce7e8c fix: username suggestions alignment ffb8a2d4
5 8d32c37e feat: add Slot for login page 0418a04f
6 00423433 fix: add missing space symbol to the 'additional.help.text' field 4cb79223
7 99a193a6 fix: adding value length check for full name field 642853e0

Group B — Redux→React Query migration (commit 8)

# PR Commit SHA Headline Master Commit(s) Status
8 2bfa6447 feat: migrate from Redux to React Query and React Context 0d709d15, 93bd0f24

Group C — Bug fixes found during port review (commits 9–12)

These have no master equivalents.

# PR Commit SHA Headline Status
9 55b6eb3e fix: pass correct arguments to setEmailSuggestionContext
10 119780a5 fix: move navigate() calls from render to useEffect in ResetPasswordPage
11 06135925 fix: use functional state updater in validateInput to prevent stale closure
12 5f13df70 fix: remove duplicate RedirectLogistration in ProgressiveProfiling

Group D — React Query improvements (commits 13–17)

No master equivalents; performance and correctness improvements to the TPA query hook and LoginContext.

# PR Commit SHA Headline Status
13 537c47b7 fix: correct LoginContext setter types and remove dead PASSWORD_RESET_ERROR code
14 ad8afe7b perf: add staleTime to third-party auth context query
15 cecbd7c5 fix: include payload in TPA query key to prevent stale cache
16 0a787708 fix: call setThirdPartyAuthContextBegin only on mount in LoginPage
17 4e7f95be perf: add enabled flag to TPA query hook to skip unnecessary fetches

Status legend: 🔲 Not started · 🔍 In progress · ✅ Approved · ⚠️ Needs discussion · ❌ Issues found


Reviews

Commit 1 — d5b1df7c: chore: bump frontend-base to 1.0.0-alpha.14 ✅

Type: frontend-base-only change (no master equivalent)
Master refs: n/a

Reviewed by: @brian-smith-tcril (GitHub web UI)
Notes: Straightforward package.json version bump plus package-lock.json update. Makes sense.


Commit 2 — b0ee418a: refactor: replace injectIntl with useIntl ✅

Type: Port of master commits
Master refs: db3d007c, 43a584eb, 5bd6926f, 76e400f0

Things to check:

  • All non-skipped files from the 4 master commits are present in the PR commit, adapted for frontend-base
  • The 3 src/recommendations/ files are the only intentional skips

Master commit file breakdown:

db3d007c — refactor: Replace of injectIntl with useIntl
@brian-smith-tcril: compared master commit to PR commit diff
Note: master commit also removes import React from 'react' in these files, but frontend-base already didn't have those imports — expected difference.

  • src/register/RegistrationFields/CountryField/CountryField.test.jsx
  • src/register/RegistrationFields/EmailField/EmailField.test.jsx
  • src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx
  • src/register/RegistrationFields/NameField/NameField.test.jsx
  • src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx
  • src/register/RegistrationFields/UsernameField/UsernameField.test.jsx

43a584eb — refactor: Replace of injectIntl with useIntl (#1538)
@brian-smith-tcril: compared master commit to PR commit diff
Note: same import React difference — expected.

  • src/common-components/tests/FormField.test.jsx
  • src/forgot-password/tests/ForgotPasswordPage.test.jsx
  • src/login/LoginPage.jsx
    • import { useEffect, useMemo, useState } from 'react' already existed on frontend-base; master commit adds it — expected difference
  • src/login/tests/AccountActivationMessage.test.jsx
  • src/login/tests/LoginPage.test.jsx

5bd6926f — refactor: Replace of injectIntl with useIntl (#1539)
@brian-smith-tcril: compared master commit to PR commit diff
Note: same import React difference — expected.

  • src/login/tests/ChangePasswordPrompt.test.jsx
  • src/login/tests/LoginFailure.test.jsx
  • src/logistration/Logistration.test.jsx
    • Master has a test 'should do nothing when user clicks on the same tab (login/register) again' that is absent from the frontend-base version — confirmed this was already absent from the initial refactor: migrate to frontend-base commit (8f8531a2), so it's a pre-existing gap, not introduced by this PR
  • src/progressive-profiling/tests/ProgressiveProfiling.test.jsx
    • Master has 11 additional tests absent from frontend-base — confirmed already absent from initial refactor: migrate to frontend-base commit (8f8531a2), pre-existing gap, not introduced by this PR:
      • should set empty host property value for non-embedded experience
      • should submit user profile details on form submission
      • should redirect to recommendations page if recommendations are enabled
      • should not redirect to recommendations page if user is on its way to enroll in a course
      • should set host property value to host where iframe is embedded for on ramp experience
      • should have onMouseDown handlers on submit and skip buttons to prevent default behavior
      • should update form values through onChange handlers
      • should call sendTrackEvent when form interactions occur
      • should call analytics functions on component mount
      • should call setThirdPartyAuthContextSuccess in embedded mode
      • should not call third party auth functions when not in embedded mode
  • src/recommendations/RecommendationsPageLayouts/SmallLayout.test.jsx ⏭️ (skip — no src/recommendations/ on frontend-base)
  • src/reset-password/tests/ResetPasswordPage.test.jsx

76e400f0 — refactor: Replace of injectIntl with useIntl (#1540)
@brian-smith-tcril: compared master commit to PR commit diff
Note: same import React difference — expected.

  • src/recommendations/tests/RecommendationsList.test.jsx ⏭️ (skip — no src/recommendations/ on frontend-base)
  • src/recommendations/tests/RecommendationsPage.test.jsx ⏭️ (skip — no src/recommendations/ on frontend-base)
  • src/register/RegistrationPage.test.jsx
  • src/register/components/tests/ConfigurableRegistrationForm.test.jsx
  • src/register/components/tests/RegistrationFailure.test.jsx
  • src/register/components/tests/ThirdPartyAuth.test.jsx

Commit 3 — 046abb77: chore: handle forbidden username exceptions on registration ✅

Type: Port of master commit
Master refs: 58ec90ac

Master commit file breakdown:

58ec90ac — chore: Handle forbidden username exceptions on registration (#1545)
@brian-smith-tcril: compared master commit to PR commit diff

  • src/register/components/RegistrationFailure.jsx
  • src/register/data/constants.js
  • src/register/messages.jsx

@brian-smith-tcril: identical to master commit


Commit 4 — 80ce7e8c: fix: username suggestions alignment ✅

Type: Port of master commit
Master refs: ffb8a2d4

Master commit file breakdown:

ffb8a2d4 — fix: username suggestions alignment (#1584)
@brian-smith-tcril: quick skim of master commit and PR commit diff looked mostly identical; Claude compared diffs and confirmed

  • src/register/RegistrationFields/UsernameField/UsernameField.jsx
  • src/register/data/sagas.js ✅ (expected frontend-base import consolidation; e.response?.status optional chaining vs e.response && e.response.status)
  • src/sass/_registration.scss

Commit 5 — 8d32c37e: feat: add Slot for login page ✅

Type: Port of master commit
Master refs: 0418a04f

Master commit file breakdown:

0418a04f — feat: Add plugin slot for login page
@brian-smith-tcril: compared master commit to PR commit diff

  • package.json ⏭️ (skip — master adds frontend-plugin-framework, not used with frontend-base)
  • package-lock.json ⏭️ (same)
  • src/login/LoginPage.jsx ✅ (@brian-smith-tcril + Claude: net changes identical; diffs look different only because some imports were already in their final positions on frontend-base and didn't need moving)
  • src/login/tests/LoginPage.test.jsx
  • src/logistration/Logistration.jsx ✅ (@brian-smith-tcril: only difference is import path ../plugin-slots/LoginComponentSlot../slots/LoginComponentSlot; Claude confirmed by comparing diffs)
  • src/logistration/Logistration.test.jsx
  • src/plugin-slots/LoginComponentSlot/README.md(master uses plugin-slots/; frontend-base uses slots/)
  • src/plugin-slots/LoginComponentSlot/component_with_prefix.png(copied from master — should be replaced with frontend-base screenshots once example code exists)
  • src/plugin-slots/LoginComponentSlot/default_component.png(same)
  • src/plugin-slots/LoginComponentSlot/index.jsx ✅ (pluginProps → plain props makes sense for frontend-base Slot API; follow-up note added for TypeScript conversion)
  • src/recommendations/data/tests/hooks.test.jsx ⏭️ (skip — no src/recommendations/ on frontend-base)

Commit 6 — 00423433: fix: add missing space symbol to the 'additional.help.text' field ✅

Type: Port of master commit
Master refs: 4cb79223

Master commit file breakdown:

4cb79223 — fix: add missing space symbol to the 'additional.help.text' field (#1521)
@brian-smith-tcril: compared master commit to PR commit diff

  • src/forgot-password/ForgotPasswordPage.jsx
  • src/forgot-password/messages.js

@brian-smith-tcril: identical to master commit


Commit 7 — 99a193a6: fix: adding value length check for full name field ✅

Type: Port of master commit
Master refs: 642853e0

Master commit file breakdown:

642853e0 — fix: adding value length check for full name field (#1561)
@brian-smith-tcril: compared master commit to PR commit diff

  • src/common-components/tests/ThirdPartyAuthAlert.test.jsx ✅ (identical; @brian-smith-tcril: not a fan of snapshot tests here — see follow-ups)
  • src/common-components/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap ✅ (identical)
  • src/register/RegistrationFields/NameField/NameField.test.jsx ✅ (identical; @brian-smith-tcril: uses fireEvent instead of userEvent — see follow-ups)
  • src/register/RegistrationFields/NameField/validator.js ✅ (identical)
  • src/register/messages.jsx ✅ (identical)

Commit 8 — 2bfa6447: feat: migrate from Redux to React Query and React Context

Type: Port of master commits (large)
Master refs: 0d709d15, 93bd0f24

Master commit file breakdown (0d709d15):
Comparing master commit to PR commit diff. Given the size, @brian-smith-tcril and Claude are reviewing together — Claude comparing diffs, @brian-smith-tcril spot-checking and providing judgment calls.

Other:

  • package.json
    • Both master and frontend-base remove: @redux-devtools/extension, redux-logger, redux-mock-store, redux-saga, redux-thunk, reselect
    • react-redux and redux were already absent from frontend-base dependencies before this commit, so no removal needed (master removes them from regular deps)
    • @tanstack/react-query added as regular dependency on master; added as peerDependency on frontend-base (shell provides QueryClient) — expected
    • react-redux and redux removed from peerDependencies on frontend-base ✅ (no peerDep changes on master)
    • devDependency differences: master adds @edx/typescript-config and @testing-library/jest-dom; frontend-base adds @types/jest — expected given different tooling
  • package-lock.json(assume updated appropriately to match package.json)
  • src/MainApp.jsx / src/Main.tsx
    • Master: removes configureStore from AppProvider, wraps app in QueryClientProvider
    • Frontend-base equivalent (Main.tsx): removes ReduxProvider + configureStore; no QueryClientProvider needed since CurrentAppProvider (via the shell) handles it
  • src/constants.ts ⏭️ (master adds export const appId = 'org.openedx.frontend.app.authn'; already present on frontend-base with identical content; absent from commit 8. Author noted it was missing on master but didn't explain why it's needed — not clear to @brian-smith-tcril either.)
  • tsconfig.json ⏭️ (master adds TypeScript config; frontend-base already doing TypeScript the frontend-base way — different extends, include, and exclude are all expected; absent from commit 8)

Deleted — Redux/Saga infrastructure (confirmed deleted in commit 8):

  • src/common-components/data/actions.js 🗑️ ✅
  • src/common-components/data/reducers.js 🗑️ ✅
  • src/common-components/data/sagas.js 🗑️ ✅
  • src/common-components/data/selectors.js 🗑️ ✅
  • src/common-components/data/tests/reducer.test.js 🗑️ ✅
  • src/common-components/data/tests/sagas.test.js 🗑️ ✅
  • src/data/configureStore.js 🗑️ ✅
  • src/data/reducers.js 🗑️ ✅
  • src/data/sagas.js 🗑️ ✅
  • src/data/tests/reduxUtils.test.js 🗑️ ✅
  • src/data/utils/reduxUtils.js 🗑️ ✅
  • src/forgot-password/data/actions.js 🗑️ ✅
  • src/forgot-password/data/reducers.js 🗑️ ✅
  • src/forgot-password/data/sagas.js 🗑️ ✅
  • src/forgot-password/data/selectors.js 🗑️ ✅
  • src/forgot-password/data/tests/reducers.test.js 🗑️ ✅
  • src/forgot-password/data/tests/sagas.test.js 🗑️ ✅
  • src/login/data/actions.js 🗑️ ✅
  • src/login/data/reducers.js 🗑️ ✅
  • src/login/data/sagas.js 🗑️ ✅
  • src/login/data/tests/reducers.test.js 🗑️ ✅
  • src/login/data/tests/sagas.test.js 🗑️ ✅
  • src/progressive-profiling/data/actions.js 🗑️ ✅
  • src/progressive-profiling/data/reducers.js 🗑️ ✅
  • src/progressive-profiling/data/sagas.js 🗑️ ✅
  • src/progressive-profiling/data/selectors.js 🗑️ ✅
  • src/register/data/actions.js 🗑️ ✅
  • src/register/data/reducers.js 🗑️ ✅
  • src/register/data/sagas.js 🗑️ ✅
  • src/register/data/selectors.js 🗑️ ✅
  • src/register/data/tests/reducers.test.js 🗑️ ✅
  • src/register/data/tests/sagas.test.js 🗑️ ✅
  • src/reset-password/data/actions.js 🗑️ ✅
  • src/reset-password/data/reducers.js 🗑️ ✅
  • src/reset-password/data/sagas.js 🗑️ ✅
  • src/reset-password/data/selectors.js 🗑️ ✅
  • src/reset-password/data/tests/sagas.test.js 🗑️ ✅

Added — React Query infrastructure:

  • src/common-components/components/ThirdPartyAuthContext.tsx ✅ (identical to master except TS interface property separators: master uses ;, commit 8 uses ,)
  • src/common-components/components/ThirdPartyAuthContext.test.tsx ✅ (@testing-library/jest-dom not imported, toBeInTheDocument()toBeTruthy() — see follow-ups)
  • src/common-components/data/apiHook.ts ✅ (identical to master)
  • src/common-components/data/queryKeys.ts ✅ (identical to master)
  • src/forgot-password/data/apiHook.ts ✅ (expected import consolidation; TS interface ;,; ApiError extends Error instead of bare interface — enables optional chaining on error.response?.status and removes error as Error cast)
  • src/forgot-password/data/apiHook.test.ts ✅ (expected import consolidation to @openedx/frontend-base + matching jest.mock update)
  • src/forgot-password/data/api.test.ts ✅ (expected import consolidation; LMS_BASE_URLlmsBaseUrl; mockConfig gets as ReturnType<typeof getSiteConfig> type assertion; minor trailing comma and comment differences)
  • src/login/components/LoginContext.tsx ✅ (identical to master except TS interface property separators: ;,)
  • src/login/components/LoginContext.test.tsx ✅ (@testing-library/jest-dom not imported, toBeInTheDocument()toBeTruthy() — see follow-ups)
  • src/login/data/apiHook.ts ✅ (expected import consolidation; TS interface ;,)
  • src/login/data/apiHook.test.ts ✅ (expected import consolidation + combined jest.mock)
  • src/login/data/api.test.ts ✅ (expected import consolidation; LMS_BASE_URLlmsBaseUrl; mockConfig type assertion; getUrlByRouteRole added to mock)
  • src/progressive-profiling/components/ProgressiveProfilingContext.tsx ✅ (identical to master except TS interface property separators: ;,)
  • src/progressive-profiling/data/apiHook.ts ✅ (TS interface property separators: ;,)
  • src/progressive-profiling/data/apiHook.test.ts ✅ (mockContextValue has extra fields beyond what's exercised; mockResolvedValueOnce(undefined) vs master's mockResolvedValueOnce({ success: true })undefined is more accurate since patchAccount returns void)
  • src/progressive-profiling/data/api.test.ts ✅ (import consolidation, getConfig/LMS_BASE_URLgetSiteConfig/lmsBaseUrl, mockConfig typed as ReturnType<typeof getSiteConfig>, trailing commas)
  • src/register/components/RegisterContext.tsx ✅ (TS ;,; CLEAR_REGISTRATION_BACKEND_ERROR destructuring → spread+delete; Array<T>T[] for setRegistrationError type; backendValidations check order reversed — registrationError before validations (see master follow-up); adds useRegisterContextOptional not on master — used by PasswordField which is shared across flows where RegisterProvider may not be present)
  • src/register/components/RegisterContext.test.tsx ✅ (jest-dom absent, see follow-up → toBeTruthy(); adds test explicitly covering registrationError priority over validations in backendValidations)
  • src/register/data/apiHook.ts ✅ (import consolidation; interface { [key: string]: unknown }type X = Record<string, unknown>; TS ;,; ApiError extends Error pattern simplifies error.response checks in both onError handlers)
  • src/register/data/apiHook.test.ts ✅ (import consolidation; combined jest.mock block)
  • src/register/data/api.test.ts ✅ (import consolidation, combined jest.mock block, getConfig/LMS_BASE_URLgetSiteConfig/lmsBaseUrl, trailing commas)
  • src/register/types.ts ✅ (TS ;,; Array<T>T[])
  • src/reset-password/data/apiHook.ts ✅ (import consolidation; TS ;,; ApiError extends Error pattern; ResetPasswordPayload.params widened to Record<string, string | boolean> — not on master tip, see follow-up)
  • src/reset-password/data/apiHook.test.ts ✅ (import consolidation, combined jest.mock, trailing commas; both master and commit 8 use boolean is_account_recovery in test payloads)
  • src/reset-password/data/api.test.ts ✅ (import consolidation, combined jest.mock, getConfig/LMS_BASE_URLgetSiteConfig/lmsBaseUrl, mockConfig typed as ReturnType<typeof getSiteConfig>, trailing commas)

Renamed on master (service.jsapi.ts), renamed in commit 8:

  • src/common-components/data/api.ts ✅ (function refactor and .catch removal identical to master; ??|| to align with master)
  • src/forgot-password/data/api.ts ✅ (identical to master except master removes an eslint disable that was already absent on frontend-base)
  • src/progressive-profiling/data/api.ts ✅ (identical to master except master removes an eslint disable that was already absent on frontend-base)
  • src/reset-password/data/api.ts ✅ (identical to master except: master removes two eslint disables already absent on frontend-base; true'true' for URLSearchParams.append() — type-correct for TypeScript, behaviorally identical)

Renamed on master (service.jsapi.ts), delete+add in commit 8 (too different for git to detect as rename):

  • src/login/data/api.ts ✅ (function refactor + .catch removal matches master; getUrlByRouteRole for default redirect was already in service.js; ??|| and redirectUrl var inlined into camelCaseObject to align with master)
  • src/register/data/api.ts ✅ (registerRequestregisterNewUserApi, url extraction, and named export format match master; ??|| to align with master; .catch intentionally kept in both functions)

Modified — pure import React removal on master, already absent on frontend-base; confirmed none of these files appear in the PR commit (skip):

  • src/base-container/components/default-layout/DefaultLayout.test.jsx ⏭️
  • src/base-container/components/default-layout/LargeLayout.jsx ⏭️
  • src/base-container/components/default-layout/MediumLayout.jsx ⏭️
  • src/base-container/components/default-layout/SmallLayout.jsx ⏭️
  • src/base-container/components/welcome-page-layout/LargeLayout.jsx ⏭️
  • src/base-container/components/welcome-page-layout/MediumLayout.jsx ⏭️
  • src/base-container/components/welcome-page-layout/SmallLayout.jsx ⏭️
  • src/base-container/index.jsx ⏭️
  • src/base-container/tests/BaseContainer.test.jsx ⏭️
  • src/common-components/EmbeddedRegistrationRoute.jsx ⏭️
  • src/common-components/EnterpriseSSO.jsx ⏭️
  • src/common-components/InstitutionLogistration.jsx ⏭️
  • src/common-components/NotFoundPage.jsx ⏭️
  • src/common-components/SocialAuthProviders.jsx ⏭️
  • src/common-components/ThirdPartyAuth.jsx ⏭️
  • src/common-components/ThirdPartyAuthAlert.jsx ⏭️
  • src/common-components/Zendesk.jsx ⏭️
  • src/common-components/tests/SocialAuthProviders.test.jsx ⏭️
  • src/common-components/tests/ThirdPartyAuthAlert.test.jsx ⏭️
  • src/common-components/tests/UnAuthOnlyRoute.test.jsx ⏭️
  • src/field-renderer/FieldRenderer.jsx ⏭️
  • src/field-renderer/tests/FieldRenderer.test.jsx ⏭️
  • src/login/AccountActivationMessage.jsx ⏭️
  • src/register/RegistrationFields/TermsOfServiceField/TermsOfService.jsx ⏭️
  • src/plugin-slots/LoginComponentSlot/index.jsx ⏭️

Modified — import React removal plus minor other changes on master, absent from commit 8 (skip):

  • src/forgot-password/ForgotPasswordAlert.jsx ⏭️ (removes import React from 'react' + indentation fix on master; absent from commit 8)
  • src/reset-password/ResetPasswordFailure.jsx ⏭️ (removes import React from 'react' + indentation fix on master; absent from commit 8)

Modified — pure import React, { X }import { X } on master, already absent on frontend-base; confirmed none of these files appear in the PR commit (skip):

  • src/common-components/FormGroup.jsx ⏭️
  • src/index.jsx ⏭️
  • src/login/LoginFailure.jsx ⏭️
  • src/register/RegistrationFields/HonorCodeField/HonorCode.jsx ⏭️
  • src/register/components/ConfigurableRegistrationForm.jsx ⏭️

Modified — import React, { X }import { X } plus minor other changes on master (eslint comment / indentation), absent from commit 8 (skip):

  • src/login/ChangePasswordPrompt.jsx ⏭️ (import React, { useEffect, useState }import { useEffect, useState }; also adds @typescript-eslint/no-unused-vars to eslint comment)
  • src/register/components/RegistrationFailure.jsx ⏭️ (import React, { useEffect }import { useEffect }; also fixes indentation on a break statement)

Modified — minor other changes only on master, absent from commit 8 (skip):

  • src/common-components/RedirectLogistration.jsx ⏭️ (blank line removal only on master; absent from commit 8)
  • src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx ⏭️ (switches eslint disable from no-unused-vars to @typescript-eslint/no-unused-vars on master; absent from commit 8)

Modified — Redux export removals from index/barrel files, identical to master:

  • src/common-components/index.jsx ✅ (removes reducer, saga, storeName exports)
  • src/data/utils/index.js ✅ (removes AsyncActionType export)
  • src/forgot-password/index.js ✅ (removes reducer, FORGOT_PASSWORD, saga, forgotPasswordResultSelector exports)
  • src/progressive-profiling/index.js ✅ (removes reducer, saga exports)
  • src/register/index.js ✅ (removes reducer, saga, storeName exports)
  • src/reset-password/index.js ✅ (removes reducer, RESET_PASSWORD, saga, storeName exports)

Modified — Redux export removals from index/barrel files, differs from master:

  • src/login/index.js ✅ (master removes reducer, saga; commit 8 also removes export const storeName = 'login' + blank line — expected, storeName not needed on frontend-base)

Modified — components and tests:

  • src/common-components/PasswordField.jsx ✅ (import consolidation; useRegisterContextuseRegisterContextOptional with noopFn fallbacks; see follow-up re: context coupling)
  • src/common-components/tests/FormField.test.jsx ✅ (Redux/store setup removed; RegisterProvider not needed in wrapper since PasswordField uses optional context; mock factory approach simplifies beforeEach to just mockClear(); import consolidation)
  • src/forgot-password/ForgotPasswordPage.jsx ✅ (Redux→React Query migration identical; import consolidation; useAppConfig() correctly extracted to component level on frontend-base)
  • src/forgot-password/tests/ForgotPasswordPage.test.jsx ✅ (import consolidation; mergeConfigmergeAppConfig(appId, ...); CurrentAppProvider added to wrapper; mockedNavigator assertion uses stringContaining; test rewrites largely identical to master; uses fireEvent — see follow-ups)
  • src/login/LoginPage.jsx ✅ (identical migration to master; minor import ordering adjustment for AccountActivationMessage)
  • src/login/tests/LoginPage.test.jsx ✅ (import consolidation; CurrentAppProvider/mergeAppConfig wrapper setup; some tests renamed; several tests removed — see follow-ups; some fireEvent usage — see follow-ups)
  • src/logistration/Logistration.jsx ✅ (identical migration to master)
  • src/logistration/Logistration.test.jsx ✅ (import consolidation; Redux/store setup removed; CurrentAppProvider in wrapper; mergeAppConfig per-test; heading assertions not updated to match master's tab/button roles — see follow-up; test fixture data gaps — see follow-up; 3 tests missing — see follow-up)
  • src/progressive-profiling/ProgressiveProfiling.jsx ✅ (identical migration to master; mockLoggingServiceloggingService bug fix; appConfig.X style for useAppConfig() fields)
  • src/progressive-profiling/tests/ProgressiveProfiling.test.jsx ✅ (import consolidation; Redux/store setup removed; CurrentAppProvider in wrapper; extra mergeConfig calls on master not needed in commit 8; sendPageEvent missing — see follow-up; fireEvent usage — see follow-up; recommendations tests absent — expected; all other tests present in both)
  • src/register/RegistrationFields/CountryField/CountryField.jsx ✅ (identical migration to master; minor cosmetic formatting on validateCountryField and handleOnChange calls)
  • src/register/RegistrationFields/CountryField/CountryField.test.jsx ✅ (identical migration to master; CurrentAppProvider in wrapper; BrowserRouterMemoryRouter — see follow-up; mergeAppConfig replaces pre-existing mergeConfig)
  • src/register/RegistrationFields/EmailField/EmailField.jsx ✅ (identical migration to master)
  • src/register/RegistrationFields/EmailField/EmailField.test.jsx ✅ (identical migration to master; CurrentAppProvider in wrapper; BrowserRouterMemoryRouter — see follow-up; fireEvent in new tests — see follow-up)
  • src/register/RegistrationFields/NameField/NameField.jsx ✅ (identical migration to master)
  • src/register/RegistrationFields/NameField/NameField.test.jsx ✅ (identical migration to master; CurrentAppProvider in wrapper; BrowserRouterMemoryRouter — see follow-up; fireEvent in new/updated tests — see follow-up)
  • src/register/RegistrationFields/UsernameField/UsernameField.jsx ✅ (identical migration to master)
  • src/register/RegistrationFields/UsernameField/UsernameField.test.jsx ✅ (identical migration to master; CurrentAppProvider in wrapper; BrowserRouterMemoryRouter — see follow-up; test names updated to drop "in redux"; fireEvent in new tests — see follow-up)
  • src/register/RegistrationPage.jsx ✅ (identical migration to master; eslint-disable-line react-hooks/exhaustive-deps added — see follow-up)
  • src/register/RegistrationPage.test.jsx ✅ (migration mostly matches master; several follow-ups logged — see below)
  • src/register/components/tests/ConfigurableRegistrationForm.test.jsx ✅ (migration mostly matches master; several follow-ups logged — see below)
  • src/register/components/tests/RegistrationFailure.test.jsx ✅ (migration matches master; MemoryRouter and useThirdPartyAuthHook mock noted in follow-ups)
  • src/register/components/tests/ThirdPartyAuth.test.jsx ✅ (migration mostly matches master; follow-ups logged — see below)
  • src/register/data/utils.js — not modified in commit 8; master commit has indentation fixes and ||= refactoring (no functional change). Worth porting for consistency.
  • src/reset-password/ResetPasswordPage.jsx ✅ (identical migration to master; getConfig().SITE_NAMEgetSiteConfig().siteName expected)
  • src/reset-password/tests/ResetPasswordPage.test.jsx ✅ (migration matches master; toBeTruthy() vs toBeInTheDocument() and fireEvent usage noted in follow-ups)

Skipped — src/recommendations/:

  • src/recommendations/ProductCard/BaseCard/index.jsx ⏭️
  • src/recommendations/ProductCard/Footer/index.jsx ⏭️
  • src/recommendations/ProductCard/index.jsx ⏭️
  • src/recommendations/RecommendationsList.jsx ⏭️
  • src/recommendations/RecommendationsPage.jsx ⏭️
  • src/recommendations/RecommendationsPageLayouts/LargeLayout.jsx ⏭️
  • src/recommendations/RecommendationsPageLayouts/SmallLayout.test.jsx ⏭️
  • src/recommendations/tests/RecommendationsList.test.jsx ⏭️
  • src/recommendations/tests/RecommendationsPage.test.jsx ⏭️

Master commit file breakdown (93bd0f24):
fix: prioritize registration errors over inline validations in backendValidations

  • src/register/components/RegisterContext.tsx ✅ (priority order fix already present in commit 8)
  • src/register/components/RegisterContext.test.tsx ✅ (new should prioritize registrationError over validations for backendValidations test already present in commit 8)

Commit 9 — 55b6eb3e: fix: pass correct arguments to setEmailSuggestionContext ✅

Type: Bug fix introduced during port (no master equivalent)

  • src/register/RegistrationFields/EmailField/EmailField.jsx ✅ (setEmailSuggestionContext({ suggestion: '', type: '' })setEmailSuggestionContext('', '') — function expects two string args, not a single object)
  • src/register/RegistrationFields/EmailField/EmailField.test.jsx ✅ (adds assertion verifying correct call signature)

Commit 10 — 119780a5: fix: move navigate() calls from render to useEffect in ResetPasswordPage ✅

Type: Bug fix introduced during port (no master equivalent)

  • src/reset-password/ResetPasswordPage.jsx ✅ (moves navigate() calls from render phase into useEffect — avoids React warnings and potential infinite render loops in strict mode)

Commit 11 — 06135925: fix: use functional state updater in validateInput to prevent stale closure ✅

Type: Bug fix introduced during port (no master equivalent)

  • src/reset-password/ResetPasswordPage.jsx ✅ (replaces direct formErrors mutation + spread with functional updater (prev) => ({ ...prev, [name]: fieldError }) in validateInput and handleOnFocus; simplifies return value to !fieldError)

Commit 12 — 5f13df70: fix: remove duplicate RedirectLogistration in ProgressiveProfiling ✅

Type: Pre-existing bug caught during port (no master equivalent)

  • src/progressive-profiling/ProgressiveProfiling.jsx ✅ (removes redundant RedirectLogistration gated on shouldRedirect && welcomePageContext.nextUrl; the broader shouldRedirect block below covers the same condition — it's not clear from the existing code or PR feat: param based redirection #993 context why educationLevel/userId would intentionally be omitted for the nextUrl case)

Commit 13 — 537c47b7: fix: correct LoginContext setter types and remove dead PASSWORD_RESET_ERROR code ✅

Type: Fix (no master equivalent)

  • src/login/components/LoginContext.tsx ✅ (setFormFields/setErrors types corrected from plain function signatures to Dispatch<SetStateAction<...>>)
  • src/reset-password/ResetPasswordPage.jsx ✅ (PASSWORD_RESET_ERROR removed — no code path sets status to that value; dead code in useEffect condition and navigate call)
  • src/reset-password/tests/ResetPasswordPage.test.jsx ✅ (comment update only)

Commit 14 — ad8afe7b: perf: add staleTime to third-party auth context query ✅

Type: Perf improvement (no master equivalent)

  • src/common-components/data/apiHook.ts ✅ (adds staleTime: 5 * 60 * 1000 — TPA context is effectively static per session, prevents unnecessary background refetches)

Commit 15 — cecbd7c5: fix: include payload in TPA query key to prevent stale cache ✅

Type: Bug fix (no master equivalent)

  • src/common-components/data/apiHook.ts ✅ (passes payload to ThirdPartyAuthQueryKeys.byPage)
  • src/common-components/data/queryKeys.ts ✅ (byPage updated to accept optional payload and include it in the key)

Commit 16 — 0a787708: fix: call setThirdPartyAuthContextBegin only on mount in LoginPage ✅

Type: Bug fix (no master equivalent)

  • src/login/LoginPage.jsx ✅ (moves setThirdPartyAuthContextBegin() from data sync useEffect to mount-only useEffect alongside sendPageEvent; removes it from the sync effect's deps array)

Commit 17 — 4e7f95be: perf: add enabled flag to TPA query hook to skip unnecessary fetches ✅

Type: Perf improvement (no master equivalent)

  • src/common-components/data/apiHook.ts ✅ (adds optional enabled param, defaulting to true)
  • src/progressive-profiling/ProgressiveProfiling.jsx ✅ (passes { enabled: registrationEmbedded } to skip TPA fetch when not in embedded mode)

Follow-ups

  • injectIntl still exported from frontend-base: This PR reduces the number of places that need injectIntl by migrating to useIntl, but frontend-base still exports it. Worth deprecating/removing that export in a follow-up.

  • LoginComponentSlot should be TypeScript: src/slots/LoginComponentSlot/index.jsx is JSX with PropTypes; should be converted to TypeScript to match frontend-base conventions.

  • LoginComponentSlot README and screenshots: The master version includes example code and screenshots; the frontend-base version has screenshots copied from master but no example code. Once example code is added, the screenshots should be retaken from frontend-base.

  • MAX_FULL_NAME_LENGTH hardcoded on frontend: @brian-smith-tcril commented on the master PR that the max length should come from the backend rather than being hardcoded client-side. The original feedback (avoid magic numbers) was addressed by extracting it into a named constant, but it's still a hardcoded value. The backend validation exists (edx-platform PR #34577) but the frontend doesn't read the limit from it.

  • fireEvent vs userEvent in tests: New/updated tests in NameField.test.jsx, ForgotPasswordPage.test.jsx, LoginPage.test.jsx, ProgressiveProfiling.test.jsx, EmailField.test.jsx, UsernameField.test.jsx, and ResetPasswordPage.test.jsx use fireEvent instead of the preferred userEvent. CountryField.test.jsx also has pre-existing fireEvent usage. Worth updating in a follow-up.

  • ThirdPartyAuthAlert snapshot test: ThirdPartyAuthAlert.test.jsx uses a snapshot test which is not ideal. Worth replacing with more explicit assertions in a follow-up.

  • @testing-library/jest-dom not in frontend-base dependencies: Master adds @testing-library/jest-dom as a devDependency; it's absent from frontend-base entirely. As a result, new test files use toBeTruthy() instead of toBeInTheDocument(), giving weaker test failure messages. Affects ThirdPartyAuthContext.test.tsx, LoginContext.test.tsx, RegisterContext.test.tsx, and ResetPasswordPage.test.jsx. Worth adding jest-dom to frontend-base so tests can use the more expressive matchers.

  • (nit) Over-specified mock context in progressive-profiling/data/apiHook.test.ts: mockContextValue includes all fields of the context interface (isLoading, showError, success, setLoading, clearState) even though only setShowError, setSuccess, and setSubmitState are exercised in assertions. I find that mocks that only include what's needed make the intent clearer.

  • PasswordField context coupling: PasswordField is a shared component used across login, registration, and reset-password flows, but it reaches directly into RegisterContext to get setValidationsSuccess, setValidationsFailure, validationApiRateLimited, and clearRegistrationBackendError. On master this throws outside a RegisterProvider; the frontend-base version works around it with useRegisterContextOptional() and noopFn fallbacks. The noopFn boilerplate is a sign that this isn't a great pattern — using an optional context in a shared component shouldn't require manual setup like this.

  • should dismiss reset password banner on form submission removed in login/tests/LoginPage.test.jsx: This test is present on master but removed in commit 8. Worth verifying the removal is intentional.

  • should handle successful login missing from login/tests/LoginPage.test.jsx in commit 8: Master has a test covering successful login with redirect (previously named should redirect to url returned by login endpoint after successful authentication); it appears to be absent from commit 8. Worth verifying whether this was intentionally dropped or accidentally omitted.

  • should handle SSO login success missing from login/tests/LoginPage.test.jsx in commit 8: This test was added on master but appears to be absent from commit 8. Worth verifying whether it was intentionally dropped or accidentally omitted.

  • should handle successful authentication via SSO missing from login/tests/LoginPage.test.jsx in commit 8: This test is present on master but appears to be absent from commit 8. Worth verifying whether it was intentionally dropped or accidentally omitted.

  • Missing tests in frontend-base: Several tests present on master are absent from the frontend-base branch, dropped during the initial refactor: migrate to frontend-base migration. These are pre-existing gaps unrelated to this PR but worth addressing:

    • src/logistration/Logistration.test.jsx: should do nothing when user clicks on the same tab (login/register) again
    • src/progressive-profiling/tests/ProgressiveProfiling.test.jsx: 11 tests (see Commit 2 notes for full list)
  • Heading assertions not updated in Logistration.test.jsx: Master updated sign-in/register assertions to use tab/button roles in should render login/register headings when SHOW_REGISTRATION_LINKS is disabled and should render only login page when public account creation is disabled; commit 8 still uses old screen.getByRole('heading', { level: 3 }) assertions in both tests. Worth updating.

  • 3 tests missing from Logistration.test.jsx in commit 8: The following tests are present on master but absent from commit 8: should call authService getCsrfTokenService on component mount, should send correct page events for login and register when handling institution login, should handle institution login with string parameters correctly. Worth adding.

  • BrowserRouterMemoryRouter in test wrappers: Commit 8 switches from BrowserRouter to MemoryRouter in CountryField.test.jsx, EmailField.test.jsx, NameField.test.jsx, UsernameField.test.jsx, RegistrationPage.test.jsx, ConfigurableRegistrationForm.test.jsx, and RegistrationFailure.test.jsx. Worth investigating whether this is intentional.

  • SESSION_COOKIE_DOMAIN: '' added to mergeAppConfig in RegistrationPage.test.jsx: Not present on master. Worth investigating whether this is required for tests to pass on frontend-base and why.

  • ENABLE_DYNAMIC_REGISTRATION_FIELDS: true added to mergeAppConfig in RegistrationPage.test.jsx: Present in commit 8's describe('Test Registration Page') block but not in master's mergeConfig. Worth investigating whether this is needed and if it should be added to master as well.

  • Hardcoded cookie name in should check user retention cookie in RegistrationPage.test.jsx: Master uses getConfig().USER_RETENTION_COOKIE_NAME dynamically; commit 8 hardcodes 'authn-returning-user=true'. Worth updating to use getSiteConfig() dynamically to match master's intent.

  • should redirect to dashboard if features flags are configured but no optional fields are configured incompletely ported in commit 8 (RegistrationPage.test.jsx): Master is async and updates assertions to check the cookie and that a div exists (waitFor(() => expect(document.cookie)...) + expect(container.querySelector('div')).toBeTruthy()); commit 8 is sync and still uses the old expect(window.location.href).toBe(dashboardUrl) assertion. Worth updating.

  • should redirect to progressive profiling page if optional fields are configured incompletely ported in commit 8 (RegistrationPage.test.jsx): Similar issues — master is async with onSuccess callback wiring and waitFor on the cookie with a dynamic getConfig().USER_RETENTION_COOKIE_NAME reference; commit 8 is sync and hardcodes 'authn-returning-user=true'. Worth updating.

  • should call internal state setters on successful registration is async on master but sync in commit 8 (RegistrationPage.test.jsx): Master is async with waitFor asserting on the cookie; commit 8 is sync and asserts directly on setRegistrationResult being called. Worth investigating whether the async/cookie assertion approach should be ported.

  • Duplicate test name resolved differently in RegistrationPage.test.jsx: Master has two tests both named should redirect to url returned in registration result after successful account creation (one sync, one async using onSuccess callback wiring). Commit 8 renames the second to should wire up onSuccess callback for registration mutation and makes it non-async. Worth investigating whether this divergence is intentional or addressed in a later commit.

  • honor_code removed from formPayload in should show error if email and confirm email fields do not match on submit click in ConfigurableRegistrationForm.test.jsx on frontend-base but not on master: Worth investigating whether this removal is intentional and whether master should be updated to match.

  • Late getLocale import with eslint-disable-next-line import/first in ThirdPartyAuth.test.jsx: getLocale is imported after jest.mock('@openedx/frontend-base', ...) rather than being included in the main import block. Worth investigating why this is split out and whether it can be consolidated.

  • (nit) useThirdPartyAuthHook mocked in ConfigurableRegistrationForm.test.jsx and RegistrationFailure.test.jsx but never referenced in any test: Commit 8 adds a top-level mock for ../../../common-components/data/apiHook solely to prevent the real hook from running. Master doesn't need it. Worth confirming this is intentional and considering whether it can be removed or consolidated.

  • eslint-disable-line react-hooks/exhaustive-deps added to registrationResult useEffect in RegistrationPage.jsx: Not present on master. Worth investigating whether the deps array is actually correct or whether this is suppressing a legitimate warning.

  • sendPageEvent not imported in ProgressiveProfiling.test.jsx: Master adds sendPageEvent to the analytics imports and asserts on it in should call analytics functions on component mount; commit 8 doesn't import it and the assertion is absent. Worth adding.

  • (nit) Repeated inline useThirdPartyAuthContext mock in Logistration.test.jsx: Several tests that need secondary providers repeat the full useThirdPartyAuthContext.mockReturnValue(...) inline. Master uses a shared secondaryProviders constant for this. A shared mock object or helper could reduce the duplication.

  • Test fixture data not fully ported in Logistration.test.jsx: Master upgrades test fixtures with more complete data — secondaryProviders gets iconClass, iconImage, and real-looking URLs with saml-test_university as the id, and the default ThirdPartyAuthContext mock includes a full providers array (Facebook) and populated secondaryProviders. Commit 8 still uses minimal fixtures with empty providers/secondaryProviders arrays in the default mock and old minimal data (id: 'saml-test', /dummy-auth URLs) inlined per-test. Worth updating.

Master only

  • TPA query fires unconditionally in ProgressiveProfiling.jsx: useThirdPartyAuthHook is called even when registrationEmbedded is false and the result is ignored. Fixed in commit 17 (4e7f95be) on frontend-base by adding an enabled option to the hook and passing registrationEmbedded as the flag; worth porting to master.

  • setThirdPartyAuthContextBegin called on every data sync in login/LoginPage.jsx: It's in the data sync useEffect (which runs on every TPA data update), causing a flash of PENDING state when TPA data is already cached. Fixed in commit 16 (0a787708) on frontend-base by moving it to the mount-only useEffect; worth porting to master.

  • payload not included in TPA query key in common-components/data/queryKeys.ts: byPage only keys on pageId, so payload changes (e.g. tpa_hint, query params) while pageId stays the same would serve stale cached data. Fixed in commit 15 (cecbd7c5) on frontend-base; worth porting to master.

  • staleTime missing from TPA context query in common-components/data/apiHook.ts: The TPA context is effectively static per session; adding staleTime: 5 * 60 * 1000 prevents unnecessary background refetches when navigating between login/register tabs. Added in commit 14 (ad8afe7b) on frontend-base; worth porting to master.

  • PASSWORD_RESET_ERROR dead code in ResetPasswordPage.jsx: No code path sets status to PASSWORD_RESET_ERROR, making the checks in the useEffect condition and navigate call dead code. Removed in commit 13 (537c47b7) on frontend-base; worth removing on master too.

  • Duplicate RedirectLogistration in ProgressiveProfiling.jsx: A redundant RedirectLogistration block gated on shouldRedirect && welcomePageContext.nextUrl has existed since PR feat: param based redirection #993. The broader shouldRedirect block below it covers the same condition. It's not clear from existing code or PR context why educationLevel/userId would intentionally be omitted for the nextUrl case. Fixed in commit 12 (5f13df70) on frontend-base; worth porting the removal to master.

  • Duplicate should show single sign on provider button test in ThirdPartyAuth.test.jsx: Master has two tests with the same name; commit 8 (2bfa6447) consolidates to one. Worth removing the duplicate on master.

  • mockResolvedValueOnce({ success: true }) in progressive-profiling/data/apiHook.test.ts: Master resolves patchAccount with a { success: true } object, but patchAccount returns void. The frontend-base version correctly uses undefined. Worth fixing on master.

  • backendValidations priority order in register/components/RegisterContext.tsx: Master checks state.validations before state.registrationError. Fixed on master in 93bd0f24 (which is included in commit 8 (2bfa6447) scope). Both master and commit 8 now give registrationError priority. ✅

  • ResetPasswordPayload.params type in reset-password/data/apiHook.ts: Commit 8 (2bfa6447) widens params from Record<string, string> to Record<string, string | boolean>, but master tip still has the narrower type. Test payloads in both master and commit 8 use boolean values for is_account_recovery, while reset-password/data/api.ts converts it to a string via url.searchParams.append('is_account_recovery', 'true'). Worth tracing actual call sites to determine the correct type — the test data alone isn't sufficient to confirm which is right.

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.

[frontend-base] Port the react-query conversion from master

9 participants