Skip to content

Move to loginRedirect from loginPopup, and update e2e tests#293

Merged
devksingh4 merged 7 commits intomainfrom
update-login-method
Feb 11, 2026
Merged

Move to loginRedirect from loginPopup, and update e2e tests#293
devksingh4 merged 7 commits intomainfrom
update-login-method

Conversation

@devksingh4
Copy link
Member

@devksingh4 devksingh4 commented Feb 11, 2026

Summary by CodeRabbit

  • New Features

    • Authentication redirect page with loading state and post-login return support.
    • Store pages preserve product selection and quantity via URL parameters.
  • Refactor

    • Switched authentication flow from popup to redirect.
  • Tests

    • Added/expanded E2E suites for Home, Identity Sync, Membership, Resources, Navigation (parameterized).
    • Added test IDs to key UI elements for more reliable testing.
  • Chores

    • Improved CI: parallelized E2E shards and consolidated HTML test reports.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

Warning

Rate limit exceeded

@devksingh4 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 7 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Refactors authentication flows from MSAL popup-based to redirect-based, adding a returnPath parameter to token acquisition. Threads returnPath through AuthActionButton and various buttons (membership, netid sync, store) and adds URL-based initialization for store item variant/quantity. Adds an auth-redirect runtime page that handles MSAL redirect results. Introduces or updates multiple Playwright e2e tests (home, identity sync, membership, resources, navigation) and test IDs in components/layouts. Adds calendar test IDs, membership perks test ID, and updates GitHub Actions to shard e2e runs and merge HTML reports.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: migrating from loginPopup to loginRedirect authentication flow and adding/updating end-to-end tests to validate the changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch update-login-method

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Feb 11, 2026

Deploying with Cloudflare Pages

Name Result
Last commit: 099cc31
Preview URL: https://4cc609f7.acmuiuc.pages.dev
Branch Preview URL: https://update-login-method.acmuiuc.pages.dev

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/AuthActionButton.tsx (1)

62-73: ⚠️ Potential issue | 🟡 Minor

Unhandled error from getUserAccessToken when it throws on redirect.

As discussed in src/authConfig.ts, both loginRedirect and acquireTokenRedirect paths throw Error('Redirecting to login'). handleClick has no catch block, so this propagates as an unhandled rejection. The finally block still runs, calling setIsWorking(false) on a potentially unmounting component.

Consider catching and ignoring the redirect sentinel, and suppressing the state-after-unmount case:

💡 Suggested fix
  const handleClick = async () => {
    if (!pca) {
      return;
    }
    setIsWorking(true);
    try {
      const accessToken = await getUserAccessToken(pca, returnPath);
      await onAction(accessToken, showError);
+   } catch (e) {
+     if (e instanceof Error && e.message === 'Redirecting to login') return;
+     throw e;
    } finally {
      setIsWorking(false);
    }
  };
src/components/store/StoreItem.tsx (1)

126-135: ⚠️ Potential issue | 🟠 Major

Background getUserAccessToken calls may unexpectedly redirect the user.

checkMembershipStatus (Line 128) and the preload effect (Line 204) call getUserAccessToken(pca) without a returnPath. Under the old popup flow, an InteractionRequiredAuthError would show a popup; now it calls acquireTokenRedirect, which navigates the user away from the page during a background operation they didn't initiate.

While the !user guard prevents calls when no account exists, token expiry or consent requirements can still trigger InteractionRequiredAuthError for a known account.

Consider wrapping these background calls to only attempt acquireTokenSilent and silently fail on interaction-required errors, rather than redirecting:

💡 Sketch: silent-only token helper
// In authConfig.ts or a local helper
export const getUserAccessTokenSilentOnly = async (
  pca: IPublicClientApplication
): Promise<string | null> => {
  const account = pca.getActiveAccount() || pca.getAllAccounts()[0];
  if (!account) return null;
  try {
    const response = await pca.acquireTokenSilent({
      ...loginRequest,
      account,
    });
    return response.accessToken;
  } catch {
    return null;
  }
};

Then use it in checkMembershipStatus and the preload effect instead of getUserAccessToken.

Also applies to: 202-208

🤖 Fix all issues with AI agents
In `@e2e/home.spec.ts`:
- Around line 8-13: The test "Hero renders with heading" uses Playwright's
assertion without awaiting it: change the assertion on the locator variable
heading so the promise is awaited (i.e., add await before the
expect(...).toBeVisible() call) so the auto-retrying assertion actually runs;
update the same pattern if present in the related test resources.spec.ts.

In `@e2e/resources.spec.ts`:
- Around line 17-22: The async Playwright assertion call in the looped tests is
missing an await, so the promise returned by header.toContainText(heading) is
discarded and the test can pass incorrectly; update the test block inside the
for loop (the test callback that defines header via page.getByRole) to await the
assertion (await header.toContainText(heading)) so the auto-retrying assertion
runs and the test fails properly when the heading is absent.

In `@src/authConfig.ts`:
- Around line 40-42: The thrown Error('Redirecting to login') from the
pca.loginRedirect branches (the throw after await pca.loginRedirect(request) in
authConfig functions) causes an unhandled promise rejection; instead of
throwing, stop propagation by returning immediately after calling
pca.loginRedirect(request) (and do the same for the second identical throw), so
the caller (AuthActionButton.handleClick) won't receive an exception; locate the
throws referencing "Redirecting to login" and replace them with an early return
to avoid the noisy unhandled rejection.

In `@src/components/MembershipCheckButton.tsx`:
- Line 26: The returnPath currently uses window.location.href which produces
absolute URLs and can corrupt query strings; update the MembershipCheckButton
component to pass a relative path instead (consistent with other components) by
setting returnPath to a relative URL (e.g., "/membership?authButtonClick") or,
if you need to preserve the current path, build a safe relative path using
window.location.pathname + (window.location.search ? window.location.search +
"&authButtonClick" : "?authButtonClick") so the returnPath prop (used by the
OIDC state/redirect) is always a proper relative path and never produces a
malformed query string.

In `@src/pages/auth-redirect.astro`:
- Around line 10-20: Wrap the asynchronous redirect handling in a try/catch
around initMsalClient() and pca.handleRedirectPromise() to handle rejections
(use initMsalClient, handleRedirectPromise, pca.setActiveAccount), log or
surface the error and redirect to a safe fallback (e.g., '/login' or '/'); and
before calling window.location.replace(result.state || '/'), validate
result.state to prevent open redirects by ensuring it is either a relative path
(starts with '/') or same-origin (parse and compare origin), otherwise ignore it
and use the safe fallback. Ensure the catch block also falls back to the safe
redirect so users are not left on a spinner.
🧹 Nitpick comments (5)
src/components/calendar/CalendarControls.tsx (1)

38-39: Prefer a narrower cast over as any.

as any disables all type checking. If the previous as string worked, it's a safer cast. If the issue is that localizer.add expects a more specific type, consider casting to that specific type instead.

-    // eslint-disable-next-line `@typescript-eslint/no-explicit-any`
-    const candidate = localizer.add(displayDate, offset, unit as any);
+    const candidate = localizer.add(displayDate, offset, unit as string);
e2e/identitySync.spec.ts (1)

16-26: Consider a less brittle URL assertion for the login redirect.

The waitForURL hardcodes the Azure AD tenant ID and client ID. If the auth configuration ever changes, this test silently becomes stale. A more resilient check would assert only the login domain:

Suggested change
-    await page.waitForURL(
-      'https://login.microsoftonline.com/44467e6f-462c-4ea2-823f-7800de5434e3/oauth2/v2.0/authorize?client_id=d64e9c50-d144-4b4a-a315-ad2ed456c37**'
-    );
+    await page.waitForURL('https://login.microsoftonline.com/**');
e2e/resources.spec.ts (1)

5-7: Nit: Missing leading / in goto path.

Other test files consistently use absolute paths (e.g., /admin/sync, /). Consider page.goto('/resources') for consistency.

Proposed fix
-    await page.goto('resources/');
+    await page.goto('/resources');
src/components/AuthActionButton.tsx (1)

50-60: Missing handleClick in useEffect dependency array — and potential stale closure.

The effect references handleClick but only declares [pca] as a dependency. This will trigger an react-hooks/exhaustive-deps lint warning. More importantly, if returnPath or onAction props change before pca initializes, the effect captures a stale handleClick.

In practice this is unlikely to cause issues (the effect runs once after MSAL init and the ref guard prevents re-runs), but it's worth being explicit.

♻️ Option: wrap handleClick in useCallback and include in deps
- const handleClick = async () => {
+ const handleClick = useCallback(async () => {
    if (!pca) {
      return;
    }
    setIsWorking(true);
    try {
      const accessToken = await getUserAccessToken(pca, returnPath);
      await onAction(accessToken, showError);
    } finally {
      setIsWorking(false);
    }
- };
+ }, [pca, returnPath, onAction, showError]);

  useEffect(() => {
    if (!pca || autoTriggered.current) return;
    const params = new URLSearchParams(window.location.search);
    if (!params.has('authButtonClick')) return;
    autoTriggered.current = true;
    params.delete('authButtonClick');
    const qs = params.toString();
    const newUrl = window.location.pathname + (qs ? `?${qs}` : '');
    window.history.replaceState({}, '', newUrl);
    handleClick();
- }, [pca]);
+ }, [pca, handleClick]);
e2e/membership.spec.ts (1)

24-27: Extract the duplicated login URL pattern into a shared constant to improve maintainability.

The Microsoft login URL glob pattern is duplicated across test files—appearing twice in membership.spec.ts (lines 25, 47) and once in identitySync.spec.ts (line 24). If the tenant ID or client ID changes, multiple test files require updates.

Create e2e/helpers.ts:

export const LOGIN_URL_PATTERN =
  'https://login.microsoftonline.com/44467e6f-462c-4ea2-823f-7800de5434e3/oauth2/v2.0/authorize?client_id=d64e9c50-d144-4b4a-a315-ad2ed456c37**';

Then import and use it in each spec file:

-    await page.waitForURL(
-      'https://login.microsoftonline.com/44467e6f-462c-4ea2-823f-7800de5434e3/oauth2/v2.0/authorize?client_id=d64e9c50-d144-4b4a-a315-ad2ed456c37**'
-    );
+    await page.waitForURL(LOGIN_URL_PATTERN);

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @.github/workflows/deploy.yml:
- Around line 166-169: The workflow is using mismatched action versions and
Node.js target: update the uses entries for actions/checkout and
actions/setup-node to the same major version used elsewhere (change
actions/checkout@v5 and actions/setup-node@v5 to `@v4`) and set node-version to
the consistent value (change node-version: lts/* to node-version: 24.x) so all
jobs use the same action majors and Node.js runtime (look for the occurrences of
actions/checkout, actions/setup-node, and node-version in the deploy job).
- Around line 131-137: The CI is configured to upload blob reports from
blob-report/, but the Playwright config uses reporter: process.env.CI ? 'html' :
'list'; change the reporter to produce blob reports in CI by replacing that
expression with reporter: process.env.CI ? ['blob'] : 'list' (or equivalent
array form) in playwright.config.ts so Playwright writes to blob-report/,
ensuring the artifact upload step (which names blob-report-${{ matrix.shardIndex
}}) finds the expected files.
- Around line 161-189: The merge-reports job lacks a minimal permissions block
and can run when e2e was skipped; add a permissions block (e.g., minimal read
perms needed for artifacts/actions) to the merge-reports job and tighten its if
condition so it only runs when e2e actually ran successfully or for PRs; update
the job named merge-reports to include a permissions entry (grant only the least
privileges required for downloading/uploading artifacts) and change the
job-level if from "${{ !cancelled() }}" to a compound guard such as "${{
!cancelled() && needs.e2e.result == 'success' }}" or scope it to pull_request
events (e.g., check github.event_name == 'pull_request') so the "Download blob
reports" step won't fail when there are no artifacts.

@devksingh4 devksingh4 merged commit 1f59649 into main Feb 11, 2026
11 checks passed
@devksingh4 devksingh4 deleted the update-login-method branch February 11, 2026 07:16
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.

1 participant