Skip to content

feat(clerk-js): Update OTP to use a single transparent field #6551

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

tmilewski
Copy link
Member

@tmilewski tmilewski commented Aug 14, 2025

Description

Our OTP woes largely come down to one thing: multiple individual inputs.

The interim fixes included adding a bi-directional sync between these fields and a single hidden input and while this helped a lot with password managers, it didn't solve the underlying issue.

This updates OTP to use a single transparent field via input-otp.

Other required updates:

  • Update @clerk/testing common utilities, in a non-breaking way, to support this new element as well as the old one.
  • Await prepare and attempt requests over adding timeouts and delays.
  • More-specific test timings to speed up (largely local) integration testing.
    • We had timeouts applied very generically forcing a general waiting time of 90s across the board (usually to support application spin-ups), when we know a particular assertion or test failed much-much earlier.

Known (intentional) differences from our existing approach:

  • No longer can you select/edit individual fields as they don't exist anymore.

Fixes USER-2571

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Added a customizable OTP input segment component and integrated OTP runtime support.
  • Improvements

    • Better OTP accessibility and password-manager / SMS autofill support.
    • Focus styling now also responds to a data-focus-within attribute for inputs, groups and phone wrappers.
  • Bug Fixes

    • More reliable focus ring and related styles.
  • Refactor

    • Reworked OTP entry to a single-input, slot-driven flow with simplified reset behavior.
  • Chores

    • Added runtime dependency, changesets and updated bundle-size thresholds.
  • Tests

    • Added elementFromPoint mocks; updated tests, test helpers and Playwright timeouts.

@tmilewski tmilewski self-assigned this Aug 14, 2025
Copy link

changeset-bot bot commented Aug 14, 2025

🦋 Changeset detected

Latest commit: cc98744

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@clerk/clerk-js Minor
@clerk/testing Minor
@clerk/chrome-extension Patch
@clerk/clerk-expo Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

vercel bot commented Aug 14, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
clerk-js-sandbox Ready Ready Preview Comment Aug 19, 2025 4:28pm

Copy link

pkg-pr-new bot commented Aug 14, 2025

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@6551

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@6551

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@6551

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@6551

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@6551

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@6551

@clerk/elements

npm i https://pkg.pr.new/@clerk/elements@6551

@clerk/clerk-expo

npm i https://pkg.pr.new/@clerk/clerk-expo@6551

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@6551

@clerk/express

npm i https://pkg.pr.new/@clerk/express@6551

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@6551

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@6551

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@6551

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@6551

@clerk/clerk-react

npm i https://pkg.pr.new/@clerk/clerk-react@6551

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@6551

@clerk/remix

npm i https://pkg.pr.new/@clerk/remix@6551

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@6551

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@6551

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@6551

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@6551

@clerk/types

npm i https://pkg.pr.new/@clerk/types@6551

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@6551

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@6551

commit: cc98744

Copy link
Contributor

coderabbitai bot commented Aug 14, 2025

📝 Walkthrough

Walkthrough

Reworks OTP input to use the input-otp library: adds DivInput and OTPInputSegment, replaces per-digit inputs and hidden password-manager input with a single transparent input driving visual slot segments, updates OTPCodeControl and useCodeControl to a slot-based flow, and adds input-otp as a dependency. Adds document.elementFromPoint mocks to Jest and Vitest setups. Updates tests (unit and integration) to use label/test-id queries for OTP, adjusts focus-related selectors, adds bundlewatch entries, and creates changesets.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Fix Chrome iOS autofill so all 6 OTP digits populate (USER-2571) PR migrates to a single transparent input driven by input-otp which may enable proper autofill, but there are no platform-specific tests, telemetry, or explicit iOS/Chrome autofill handling to confirm resolution.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
bundlewatch threshold additions and updates (packages/clerk-js/bundlewatch.config.json) Bundle-size monitoring config changes unrelated to OTP autofill bug.
Playwright/global test timeout and setup changes (integration/playwright.config.ts, integration/tests/** global.setup.ts, global.teardown.ts, many test files) Test harness timeout tuning and per-test timeouts do not affect OTP autofill behavior.
Various non-OTP focus-selector tweaks (packages/clerk-js/src/ui/baseTheme.ts; src/ui/elements/InputGroup.tsx; src/ui/elements/PhoneInput/index.tsx) Styling/selector broadening is unrelated to autofill population mechanics.

Possibly related issues

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot]

This comment was marked as outdated.

@tmilewski tmilewski changed the title feat: Update OTP to use a single transparent field feat(clerk-js): Update OTP to use a single transparent field Aug 14, 2025
coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

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: 0

🧹 Nitpick comments (2)
packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx (2)

37-54: Be explicit with property descriptors when stubbing window.location/history

The restore sets configurable: true, which is fine. To avoid surprises if tests override these properties, ensure those overrides also set configurable: true. In this file, the ticket flow tests (Line 532 and Line 570) use Object.defineProperty without configurable. Consider updating them for symmetry and robustness.

Example for those overrides:

Object.defineProperty(window, 'location', {
  value: { href: 'http://localhost/sign-in?__clerk_ticket=test_ticket' },
  writable: true,
  configurable: true,
});
Object.defineProperty(window, 'history', {
  value: { replaceState: jest.fn() },
  writable: true,
  configurable: true,
});

Alternatively, you can avoid property descriptor friction by stubbing via spies where possible (e.g., jest.spyOn for methods) and only using defineProperty when necessary for non-function properties.


209-230: React type usage: import React types or switch to ReactNode/PropsWithChildren

The wrapper annotates children as React.ReactNode but this file does not import the React type namespace. Depending on your tsconfig, this can cause “Cannot find name 'React'” during type-checking. Two safe options:

  • Minimal change: add a type-only import at the top of the file:
import type React from 'react';
  • Or use named types:
import type { ReactNode } from 'react';

and update the signature to:

-const wrapperBefore = ({ children }: { children: React.ReactNode }) => (
+const wrapperBefore = ({ children }: { children: ReactNode }) => (

Either keeps runtime unchanged and satisfies TS.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 14d8edc and f5d9986.

📒 Files selected for processing (2)
  • integration/tests/next-build.test.ts (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • integration/tests/next-build.test.ts
🧰 Additional context used
📓 Path-based instructions (14)
packages/clerk-js/src/ui/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/clerk-js-ui.mdc)

packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
packages/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{jsx,tsx}: Use error boundaries in React components
Minimize re-renders in React components

**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components: UserProfile, NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
packages/**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Unit tests should use Jest or Vitest as the test runner.

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
packages/{clerk-js,elements,themes}/**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Visual regression testing should be performed for UI components.

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/react.mdc)

**/*.tsx: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*.test.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/react.mdc)

**/*.test.{jsx,tsx}: Use React Testing Library
Test component behavior, not implementation
Use proper test queries
Implement proper test isolation
Use proper test coverage
Test component interactions
Use proper test data
Implement proper test setup
Use proper test cleanup
Implement proper test assertions
Use proper test structure

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/typescript.mdc)

**/__tests__/**/*.{ts,tsx}: Create type-safe test builders/factories
Use branded types for test isolation
Implement proper mock types that match interfaces

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx (1)

17-35: Resolved getComputedStyle leak; scoped mock and restoration look solid

Capturing originals and restoring them per test prevents cross-test pollution and addresses the prior review concern. The mock shape also matches expected CSSStyleDeclaration usage.

@@ -103,7 +103,10 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('component
await u.page.goToRelative(component.path, { waitUntil: 'commit' });
await expect(u.page.getByText(component.fallback)).toBeVisible();

await signOut({ app, page, context });
// eslint-disable-next-line playwright/no-conditional-in-test
Copy link
Member Author

Choose a reason for hiding this comment

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

No need to run if it's never signed in, per a similar if statement.

maxFailures: process.env.CI ? 5 : undefined,
workers: process.env.CI ? '50%' : '70%',
use: {
actionTimeout: 10_000,
navigationTimeout: 30_000,
Copy link
Member Author

@tmilewski tmilewski Aug 18, 2025

Choose a reason for hiding this comment

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

Relatively safe generic timeouts. More-specific ones are assigned (at the existing 90s) in various beforeAll calls and tests containing similar logic.

await page.waitForTimeout(2000);
// TODO: In original test the input has autoSubmit and this step is not needed. Not used right now because it didn't work.
await page.getByRole('textbox', { name: 'Enter email verification code' }).click();
await page.keyboard.type('424242', { delay: 100 });
Copy link
Member Author

Choose a reason for hiding this comment

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

These particular actions don't apply to our generic OTP component and, as such, are handled differently... and aren't common thus shouldn't go in @clerk/testing.

// @see https://playwright.dev/docs/api/class-keyboard#keyboard-type
fillTestOtpCode: async (name: string) => {
return page.getByRole('textbox', { name: name }).fill('424242');
enterOtpCode: async (code: string, opts?: EnterOtpCodeOptions) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

Flow:

  • Await for prepare request to complete (default: true)
  • Check for the old input
  • If exists:
    • Type in each character (per each input)
  • If missing:
    • Get the new input
    • Fill in the new code (per a single input)
  • Await for attempt request to complete (default: true)

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: 0

♻️ Duplicate comments (6)
packages/testing/src/playwright/unstable/page-objects/common.ts (3)

25-31: Defaulting awaitPrepare to true can hang tests; default it to false

Waiting for prepare_* after arriving on the OTP step often misses the request and can block indefinitely. Make prepare optional by default.

Apply this diff:

       const {
         name = 'Enter verification code',
         awaitAttempt = true,
-        awaitPrepare = true,
+        awaitPrepare = false,
         awaitRequests = true,
+        typeDelayMs = 50,
       } = opts ?? {};

32-39: Soft-wait prepare_ with a short timeout and include second-factor variants*

Avoid indefinite waits by adding a timeout and catch. Include prepare_second_factor for 2FA flows.

-      if (awaitRequests && awaitPrepare) {
-        const prepareVerificationPromise = page.waitForResponse(
-          response =>
-            response.request().method() === 'POST' &&
-            (response.url().includes('prepare_verification') || response.url().includes('prepare_first_factor')),
-        );
-        await prepareVerificationPromise;
-      }
+      if (awaitRequests && awaitPrepare) {
+        await page
+          .waitForResponse(
+            response =>
+              response.request().method() === 'POST' &&
+              (response.url().includes('prepare_verification') ||
+                response.url().includes('prepare_first_factor') ||
+                response.url().includes('prepare_second_factor')),
+            { timeout: 2000 },
+          )
+          .catch(() => {});
+      }

41-58: Fix race with attempt_ by starting the wait before typing/fill; ensure visibility; remove console.warn*

  • Start the attempt_* wait before the action so you don’t miss an immediate auto-submit.
  • Wait for the OTP input to be visible.
  • Use exact: false on the label for resiliency.
  • Remove console.warn to keep test logs clean.
  • Make the per-key delay configurable via options.
-      // Handle the case for both OTP input versions
-      const originalInput = page.getByRole('textbox', { name: 'Enter verification code. Digit 1' });
-      if (await originalInput.isVisible()) {
-        console.warn('Using the original OTP input');
-        await originalInput.click();
-        await page.keyboard.type(code, { delay: 100 });
-      } else {
-        await page.getByLabel(name).fill(code);
-      }
-
-      if (awaitRequests && awaitAttempt) {
-        const attemptVerificationPromise = page.waitForResponse(
-          response =>
-            response.request().method() === 'POST' &&
-            (response.url().includes('attempt_verification') || response.url().includes('attempt_first_factor')),
-        );
-        await attemptVerificationPromise;
-      }
+      // Handle the case for both OTP input versions
+      const originalInput = page.getByRole('textbox', { name: 'Enter verification code. Digit 1' });
+      const useOriginal = await originalInput.isVisible();
+
+      const attemptPromise =
+        awaitRequests && awaitAttempt
+          ? page.waitForResponse(
+              response =>
+                response.request().method() === 'POST' &&
+                (response.url().includes('attempt_verification') ||
+                  response.url().includes('attempt_first_factor') ||
+                  response.url().includes('attempt_second_factor')),
+            )
+          : null;
+
+      if (useOriginal) {
+        await originalInput.waitFor({ state: 'visible' });
+        await originalInput.click();
+        if (attemptPromise) {
+          await Promise.all([attemptPromise, page.keyboard.type(code, { delay: typeDelayMs })]);
+        } else {
+          await page.keyboard.type(code, { delay: typeDelayMs });
+        }
+      } else {
+        const otpInput = page.getByLabel(name, { exact: false });
+        await otpInput.waitFor({ state: 'visible' });
+        if (attemptPromise) {
+          await Promise.all([attemptPromise, otpInput.fill(code)]);
+        } else {
+          await otpInput.fill(code);
+        }
+      }
packages/clerk-js/src/ui/elements/CodeControl.tsx (3)

110-113: Reset should also clear feedback to fully recover the control.

reset currently only clears values. After an error, feedback persists, which can keep the control disabled (and impacts resend button's state). Clear feedback in reset to fully restore the control.

  const reset = React.useCallback(() => {
    setValues(Array.from({ length }, () => ''));
+   clearFeedback?.();
-  }, [length]);
+  }, [length, clearFeedback]);

167-175: Disabled state never re-enables after feedback clears.

You set disabled to true when feedback becomes truthy, but never set it back to false when feedback clears, so the input may remain disabled after reset or error recovery.

Apply this diff to derive disabled directly from feedback (removing the transient state and effect):

export const OTPCodeControl = () => {
-  const [disabled, setDisabled] = React.useState(false);
  const { otpControl, isLoading, isDisabled } = useOTPInputContext();
  const { feedback, values, setValues, feedbackType, length } = otpControl.otpInputProps;
-
-  React.useEffect(() => {
-    if (feedback) {
-      setDisabled(true);
-    }
-  }, [feedback]);
+  const disabled = Boolean(feedback);

And update line 206 to use the derived disabled value:

-                isDisabled={isDisabled || isLoading || disabled || feedbackType === 'success'}
+                isDisabled={isDisabled || isLoading || disabled || feedbackType === 'success'}

182-189: Add autoComplete='one-time-code' and localize the aria-label for accessibility and iOS autofill.

Critical for Chrome iOS autofill: the input must declare autoComplete='one-time-code' to reliably trigger OTP suggestions. Also, 'Enter verification code' is hard-coded; per guidelines, localize all user-facing strings (including aria-labels).

+import { useLocalizations } from '../hooks';
+import { localizationKeys } from '../customizables';

 export const OTPCodeControl = () => {
+  const { t } = useLocalizations();
   const [disabled, setDisabled] = React.useState(false);
   const { otpControl, isLoading, isDisabled } = useOTPInputContext();
       <OTPInput
-        aria-label='Enter verification code'
+        aria-label={t(localizationKeys('formFieldInput__otpCodeInput'))}
+        autoComplete='one-time-code'
         aria-required
         maxLength={length}
         inputMode='numeric'
         pattern={REGEXP_ONLY_DIGITS}
         textAlign='center'
         value={values.join('')}
🧹 Nitpick comments (7)
packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx (2)

22-35: Optional: prefer jest.spyOn + jest.restoreAllMocks or centralize this in jest.setup for consistency

This custom defineProperty setup works, but using jest.spyOn(window, 'getComputedStyle') in beforeEach and jest.restoreAllMocks() in afterEach is simpler and consistent with Jest patterns. Alternatively, move this mock to packages/clerk-js/jest.setup.ts to DRY across tests.

Example (no functional change intended):

beforeEach(() => {
  jest.spyOn(window, 'getComputedStyle').mockReturnValue({
    animationName: '',
    pointerEvents: 'auto',
    getPropertyValue: jest.fn().mockReturnValue(''),
  } as any);
});

afterEach(() => {
  jest.restoreAllMocks();
  // keep your location/history restoration if overridden within tests
});

Also applies to: 37-44


209-230: Import ReactNode (or use PropsWithChildren) to avoid relying on a global React namespace

Using React.ReactNode in a type position can fail if the React namespace isn’t globally available via TS config. Import the type explicitly to avoid TS complaints.

Apply this diff within the selected range:

-      const wrapperBefore = ({ children }: { children: React.ReactNode }) => (
+      const wrapperBefore = ({ children }: { children: ReactNode }) => (

Add this import at the top of the file to support the change:

import type { ReactNode } from 'react';

If you prefer, you can also use:

import type { PropsWithChildren } from 'react';
const wrapperBefore = ({ children }: PropsWithChildren) => ( ... )
packages/testing/src/playwright/unstable/page-objects/common.ts (3)

3-8: Export options type and add configurable typing delay

Make the options type public for consumers of @clerk/testing and add an optional typeDelayMs to control per-key typing when using the legacy multi-input flow.

-type EnterOtpCodeOptions = {
-  name?: string;
-  awaitRequests?: boolean;
-  awaitPrepare?: boolean;
-  awaitAttempt?: boolean;
-};
+export type EnterOtpCodeOptions = {
+  readonly name?: string;
+  readonly awaitRequests?: boolean;
+  readonly awaitPrepare?: boolean;
+  readonly awaitAttempt?: boolean;
+  /** Per-key delay (ms) when typing into the legacy per-digit inputs. Defaults to 50ms. */
+  readonly typeDelayMs?: number;
+};

60-66: Add proper JSDoc for deprecation and cross-reference recommended API

Clarify deprecation with JSDoc and keep tests discoverable in IDEs.

-    // @deprecated Use .enterTestOtpCode({ name: '...' }) instead
+    /**
+     * @deprecated Use `enterTestOtpCode({ name: '...' })` instead.
+     * This will be removed in a future major version.
+     */
     fillTestOtpCode: async (name: string, opts?: Omit<EnterOtpCodeOptions, 'name'>) => {
       return self.enterOtpCode('424242', { name, ...opts });
     },

10-11: Add explicit return type and JSDoc for the public common API

Public APIs in packages should have explicit return types and JSDoc. Define and export an interface for the page-object methods returned by common for better DX and stability.

Example (apply outside this hunk):

export interface CommonPageObject {
  continue: () => Promise<void>;
  setEmailAddress: (val: string) => Promise<void>;
  setPassword: (val: string) => Promise<void>;
  setPasswordConfirmation: (val: string) => Promise<void>;
  enterOtpCode: (code: string, opts?: EnterOtpCodeOptions) => Promise<void>;
  enterTestOtpCode: (opts?: EnterOtpCodeOptions) => Promise<void>;
  fillTestOtpCode: (name: string, opts?: Omit<EnterOtpCodeOptions, 'name'>) => Promise<void>;
  getIdentifierInput: () => ReturnType<EnhancedPage['locator']>;
  getEmailAddressInput: () => ReturnType<EnhancedPage['locator']>;
  getPhoneNumberInput: () => ReturnType<EnhancedPage['locator']>;
  getUsernameInput: () => ReturnType<EnhancedPage['locator']>;
  getPasswordInput: () => ReturnType<EnhancedPage['locator']>;
  getLegalAccepted: () => ReturnType<EnhancedPage['locator']>;
  getFirstNameInput: () => ReturnType<EnhancedPage['locator']>;
  getLastNameInput: () => ReturnType<EnhancedPage['locator']>;
  waitForSession: () => Promise<unknown>;
}

/** Common page-object helpers used by tests */
export const common = ({ page }: { page: EnhancedPage }): CommonPageObject => { /* ... */ };
integration/tests/elements/next-sign-up.test.ts (1)

28-29: Consolidate repetitive OTP input code for better maintainability.

The pattern of clicking the OTP textbox and typing with a delay is repeated 6 times throughout this file. Consider extracting this into a helper function to reduce code duplication and make future changes easier.

Add a helper function at the top of the test file:

const enterOTP = async (page: Page, label: string, code: string = '424242') => {
  await page.getByRole('textbox', { name: label }).click();
  await page.keyboard.type(code, { delay: 100 });
};

Then replace all occurrences with:

-    await page.getByRole('textbox', { name: 'Enter email verification code' }).click();
-    await page.keyboard.type('424242', { delay: 100 });
+    await enterOTP(page, 'Enter email verification code');
packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx (1)

48-51: Consider adding a test for the Chrome iOS autofill scenario.

While the helper function is well-structured, consider adding a specific test case that simulates the Chrome iOS autofill behavior to ensure the fix addresses the original issue (USER-2571).

Add a test case to verify Chrome iOS autofill behavior:

it('handles Chrome iOS autofill correctly', async () => {
  const { wrapper } = await createFixtures();
  const onCodeEntryFinished = vi.fn();
  const Component = createOTPComponent(onCodeEntryFinished);

  const { getByLabelText, getAllByTestId } = render(<Component />, { wrapper });
  const input = getByLabelText(label);
  const inputDivs = getAllByTestId(testId);

  // Simulate Chrome iOS autofill behavior
  fireEvent.change(input, { target: { value: '424242' } });

  await waitFor(() => {
    // Verify all 6 digits are populated, not just the first one
    expect(inputDivs[0]).toHaveTextContent('4');
    expect(inputDivs[1]).toHaveTextContent('2');
    expect(inputDivs[2]).toHaveTextContent('4');
    expect(inputDivs[3]).toHaveTextContent('2');
    expect(inputDivs[4]).toHaveTextContent('4');
    expect(inputDivs[5]).toHaveTextContent('2');
    expect(onCodeEntryFinished).toHaveBeenCalledWith('424242', expect.any(Function), expect.any(Function));
  });
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f5d9986 and fa81977.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (42)
  • .changeset/chilly-cities-write.md (1 hunks)
  • .changeset/rare-books-jam.md (1 hunks)
  • integration/playwright.config.ts (1 hunks)
  • integration/tests/components.test.ts (1 hunks)
  • integration/tests/dynamic-keys.test.ts (1 hunks)
  • integration/tests/elements/next-sign-in.test.ts (4 hunks)
  • integration/tests/elements/next-sign-up.test.ts (5 hunks)
  • integration/tests/global.setup.ts (1 hunks)
  • integration/tests/global.teardown.ts (1 hunks)
  • integration/tests/handshake.test.ts (3 hunks)
  • integration/tests/legal-consent.test.ts (1 hunks)
  • integration/tests/localhost/localhost-different-port-different-instance.test.ts (1 hunks)
  • integration/tests/localhost/localhost-different-port-same-instance.test.ts (1 hunks)
  • integration/tests/localhost/localhost-switch-instance.test.ts (1 hunks)
  • integration/tests/machine-auth/api-keys.test.ts (2 hunks)
  • integration/tests/machine-auth/m2m.test.ts (1 hunks)
  • integration/tests/next-account-portal/clerk-v4-ap-core-1.test.ts (1 hunks)
  • integration/tests/next-account-portal/clerk-v4-ap-core-2.test.ts (1 hunks)
  • integration/tests/next-account-portal/clerk-v5-ap-core-1.test.ts (1 hunks)
  • integration/tests/next-account-portal/clerk-v5-ap-core-2.test.ts (1 hunks)
  • integration/tests/next-account-portal/common.ts (1 hunks)
  • integration/tests/next-build.test.ts (2 hunks)
  • integration/tests/session-tasks-eject-flow.test.ts (1 hunks)
  • integration/tests/sign-in-or-up-component.test.ts (1 hunks)
  • integration/tests/sign-in-or-up-restricted-mode.test.ts (1 hunks)
  • packages/clerk-js/bundlewatch.config.json (1 hunks)
  • packages/clerk-js/jest.setup.ts (1 hunks)
  • packages/clerk-js/package.json (1 hunks)
  • packages/clerk-js/src/ui/baseTheme.ts (1 hunks)
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorOneCodeForm.spec.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInFactorTwo.test.tsx (1 hunks)
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx (2 hunks)
  • packages/clerk-js/src/ui/components/UserVerification/__tests__/UVFactorTwo.test.tsx (1 hunks)
  • packages/clerk-js/src/ui/customizables/index.ts (1 hunks)
  • packages/clerk-js/src/ui/elements/CodeControl.tsx (4 hunks)
  • packages/clerk-js/src/ui/elements/Form.tsx (2 hunks)
  • packages/clerk-js/src/ui/elements/InputGroup.tsx (1 hunks)
  • packages/clerk-js/src/ui/elements/PhoneInput/index.tsx (1 hunks)
  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx (4 hunks)
  • packages/clerk-js/src/ui/primitives/Input.tsx (1 hunks)
  • packages/clerk-js/vitest.setup.mts (1 hunks)
  • packages/testing/src/playwright/unstable/page-objects/common.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/rare-books-jam.md
🚧 Files skipped from review as they are similar to previous changes (36)
  • integration/tests/localhost/localhost-switch-instance.test.ts
  • integration/tests/global.setup.ts
  • integration/tests/next-build.test.ts
  • integration/tests/localhost/localhost-different-port-different-instance.test.ts
  • integration/tests/localhost/localhost-different-port-same-instance.test.ts
  • integration/tests/machine-auth/m2m.test.ts
  • integration/tests/global.teardown.ts
  • packages/clerk-js/vitest.setup.mts
  • integration/tests/session-tasks-eject-flow.test.ts
  • packages/clerk-js/src/ui/elements/PhoneInput/index.tsx
  • integration/tests/sign-in-or-up-component.test.ts
  • integration/tests/next-account-portal/clerk-v5-ap-core-2.test.ts
  • packages/clerk-js/src/ui/elements/InputGroup.tsx
  • packages/clerk-js/jest.setup.ts
  • integration/tests/sign-in-or-up-restricted-mode.test.ts
  • integration/tests/machine-auth/api-keys.test.ts
  • packages/clerk-js/src/ui/components/SignIn/tests/SignInFactorTwo.test.tsx
  • packages/clerk-js/src/ui/customizables/index.ts
  • integration/playwright.config.ts
  • integration/tests/elements/next-sign-in.test.ts
  • integration/tests/next-account-portal/clerk-v4-ap-core-1.test.ts
  • integration/tests/dynamic-keys.test.ts
  • integration/tests/next-account-portal/clerk-v5-ap-core-1.test.ts
  • packages/clerk-js/package.json
  • integration/tests/next-account-portal/common.ts
  • packages/clerk-js/bundlewatch.config.json
  • packages/clerk-js/src/ui/baseTheme.ts
  • packages/clerk-js/src/ui/components/UserVerification/tests/UVFactorTwo.test.tsx
  • .changeset/chilly-cities-write.md
  • packages/clerk-js/src/ui/primitives/Input.tsx
  • integration/tests/next-account-portal/clerk-v4-ap-core-2.test.ts
  • packages/clerk-js/src/ui/elements/Form.tsx
  • packages/clerk-js/src/ui/components/SignIn/tests/SignInFactorOneCodeForm.spec.tsx
  • integration/tests/legal-consent.test.ts
  • integration/tests/handshake.test.ts
  • integration/tests/components.test.ts
🧰 Additional context used
📓 Path-based instructions (17)
packages/clerk-js/src/ui/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/clerk-js-ui.mdc)

packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/testing/src/playwright/unstable/page-objects/common.ts
  • integration/tests/elements/next-sign-up.test.ts
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/testing/src/playwright/unstable/page-objects/common.ts
  • integration/tests/elements/next-sign-up.test.ts
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
packages/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/testing/src/playwright/unstable/page-objects/common.ts
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/testing/src/playwright/unstable/page-objects/common.ts
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/testing/src/playwright/unstable/page-objects/common.ts
  • integration/tests/elements/next-sign-up.test.ts
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{jsx,tsx}: Use error boundaries in React components
Minimize re-renders in React components

**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components: UserProfile, NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
packages/**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Unit tests should use Jest or Vitest as the test runner.

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
packages/{clerk-js,elements,themes}/**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Visual regression testing should be performed for UI components.

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/testing/src/playwright/unstable/page-objects/common.ts
  • integration/tests/elements/next-sign-up.test.ts
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/react.mdc)

**/*.tsx: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/typescript.mdc)

**/__tests__/**/*.{ts,tsx}: Create type-safe test builders/factories
Use branded types for test isolation
Implement proper mock types that match interfaces

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx
  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/testing/src/playwright/unstable/page-objects/common.ts
  • integration/tests/elements/next-sign-up.test.ts
  • packages/clerk-js/src/ui/elements/CodeControl.tsx
**/*.test.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/react.mdc)

**/*.test.{jsx,tsx}: Use React Testing Library
Test component behavior, not implementation
Use proper test queries
Implement proper test isolation
Use proper test coverage
Test component interactions
Use proper test data
Implement proper test setup
Use proper test cleanup
Implement proper test assertions
Use proper test structure

Files:

  • packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
integration/**

📄 CodeRabbit Inference Engine (.cursor/rules/global.mdc)

Framework integration templates and E2E tests should be placed under the integration/ directory

Files:

  • integration/tests/elements/next-sign-up.test.ts
integration/**/*

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

End-to-end tests and integration templates must be located in the 'integration/' directory.

Files:

  • integration/tests/elements/next-sign-up.test.ts
integration/**/*.{test,spec}.{js,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Integration tests should use Playwright.

Files:

  • integration/tests/elements/next-sign-up.test.ts
🧬 Code Graph Analysis (2)
packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx (1)
packages/clerk-js/src/ui/elements/CodeControl.tsx (2)
  • OTPCodeControl (166-217)
  • OTPRoot (143-145)
packages/clerk-js/src/ui/elements/CodeControl.tsx (2)
packages/clerk-js/src/ui/customizables/index.ts (4)
  • Box (15-15)
  • descriptors (10-10)
  • Flex (16-16)
  • OTPInputSegment (35-37)
packages/clerk-js/src/ui/customizables/elementDescriptors.ts (1)
  • descriptors (565-565)
🔇 Additional comments (3)
packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx (1)

17-55: Fixed: no more leaking getComputedStyle and restored globals correctly

Good capture/restore of window.getComputedStyle, location, and history. Using configurable: true and restoring in afterEach addresses the prior cross-test pollution concern.

integration/tests/elements/next-sign-up.test.ts (1)

105-111: Ensure proper waiting between phone and email OTP submissions

The flow at integration/tests/elements/next-sign-up.test.ts (lines 105–111) enters a phone OTP, calls u.po.signUp.continue(), and immediately enters an email OTP. Since continue() is implemented in the external page-object, please confirm it waits for the new verification step (navigation or element render) before proceeding. If it doesn’t, add an explicit wait or assertion, for example:

• await u.page.waitForSelector('role=textbox[name="Enter email verification code"]');
• or assert the presence of the email-code input before typing.

packages/clerk-js/src/ui/elements/__tests__/CodeControl.spec.tsx (1)

74-80: Verify autoComplete='one-time-code' is present for Chrome iOS autofill support.

The test correctly checks for the autoComplete attribute, which is crucial for the iOS autofill fix mentioned in the PR objectives.

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: 0

🧹 Nitpick comments (2)
packages/clerk-js/bundlewatch.config.json (2)

3-3: Round thresholds and add small headroom to reduce churn

The exact sizes (e.g., 628.5KB, 43.78KB) will cause frequent threshold bumps with minor dependency changes. Consider rounding up and adding a small buffer so CI isn’t noisy for inconsequential diffs.

Apply rounded thresholds with modest headroom:

-    { "path": "./dist/clerk.js", "maxSize": "628.5KB" },
+    { "path": "./dist/clerk.js", "maxSize": "630KB" },
...
-    { "path": "./dist/vendors*.js", "maxSize": "43.78KB" },
+    { "path": "./dist/vendors*.js", "maxSize": "46KB" },

Also applies to: 9-9


22-33: Avoid brittle sub-2KB budgets; add minimal buffer

Budgets like 1.0–1.5KB are likely to flake with minor codegen/minifier variance (e.g., license header changes). Recommend bumping tiny caps slightly to avoid frequent rebaselining without materially increasing risk.

Example adjustments:

-    { "path": "./dist/onetap*.js", "maxSize": "1KB" },
-    { "path": "./dist/waitlist*.js", "maxSize": "1.5KB" },
+    { "path": "./dist/onetap*.js", "maxSize": "2KB" },
+    { "path": "./dist/waitlist*.js", "maxSize": "2KB" },

-    { "path": "./dist/up-billing-page*.js", "maxSize": "3.0KB" },
-    { "path": "./dist/op-billing-page*.js", "maxSize": "3.0KB" },
+    { "path": "./dist/up-billing-page*.js", "maxSize": "3.5KB" },
+    { "path": "./dist/op-billing-page*.js", "maxSize": "3.5KB" },

-    { "path": "./dist/up-plans-page*.js", "maxSize": "1.0KB" },
-    { "path": "./dist/op-plans-page*.js", "maxSize": "1.0KB" },
-    { "path": "./dist/statement-page*.js", "maxSize": "1.0KB" },
+    { "path": "./dist/up-plans-page*.js", "maxSize": "1.5KB" },
+    { "path": "./dist/op-plans-page*.js", "maxSize": "1.5KB" },
+    { "path": "./dist/statement-page*.js", "maxSize": "1.5KB" },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fa81977 and ec4f8ba.

📒 Files selected for processing (1)
  • packages/clerk-js/bundlewatch.config.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/clerk-js/bundlewatch.config.json
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • packages/clerk-js/bundlewatch.config.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Integration Tests (quickstart, chrome)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 14)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (generic, chrome)
🔇 Additional comments (2)
packages/clerk-js/bundlewatch.config.json (2)

1-35: LGTM: Bundlewatch coverage expanded appropriately

Good call adding per-surface entries to better localize bundle growth. This aligns with the OTP refactor and helps keep size budgets enforceable across pages/components.


1-35: Ensure bundlewatch globs match your build output

It looks like there’s no packages/clerk-js/dist directory in the repo yet, so the verification script can’t find any files. To avoid silent coverage gaps:

  • Run your build step for clerk-js so that the dist folder and JS artifacts are generated.

  • Update and run the following script after building to confirm each glob in bundlewatch.config.json resolves at least one file:

    #!/usr/bin/env bash
    set -euo pipefail
    
    CONFIG="packages/clerk-js/bundlewatch.config.json"
    BASE="packages/clerk-js/dist"    # must point to your actual build output
    
    missing=0
    while IFS= read -r pattern; do
      rel="${pattern#./}"
      matches=$(fd --glob "$rel" "$BASE")
      if [[ -z "$matches" ]]; then
        echo "ERROR: No matches for pattern: $pattern"
        missing=1
      else
        echo "OK: $pattern -> $(wc -l <<<"$matches") match(es)"
      fi
    done < <(jq -r '.files[].path' "$CONFIG")
    
    if (( missing )); then
      echo "✗ Some patterns did not match. Update your globs or build output paths."
      exit 1
    else
      echo "✓ All bundlewatch patterns resolved successfully."
    fi
  • If your build artifacts land in a different directory, adjust BASE to that path.

  • Consider integrating this check into your CI pipeline right after the build.

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: 0

🧹 Nitpick comments (5)
integration/tests/middleware-placement.test.ts (3)

47-54: Scope the 90s timeout via test.describe.configure instead of inside beforeAll.

For consistency and predictability, configure the suite timeout at the describe level. This avoids ambiguity about whether the current beforeAll is covered and mirrors the global config shift away from a blanket 90s per-test timeout.

Within the selected range:

-    test.setTimeout(90_000);

Update the describe configuration (outside the selected range):

test.describe.configure({ mode: 'parallel', timeout: 90_000 });

78-85: Prefer describe-scoped timeout to cover beforeAll/afterAll and tests.

Centralize the timeout with test.describe.configure({ timeout: 90_000 }) for this suite. It makes intent explicit and avoids relying on hook-local calls.

Within the selected range:

-    test.setTimeout(90_000);

Then adjust the describe’s configure (outside the selected range):

test.describe.configure({ mode: 'parallel', timeout: 90_000 });

22-29: Move timeout configuration to the suite level in integration/tests/middleware-placement.test.ts

Configure the timeout on each test.describe so it covers its beforeAll hook, then remove the in-hook test.setTimeout calls.

Apply these changes in each of the three describe blocks:

• In the “missing middleware” suite

-  test.describe.configure({ mode: 'parallel' });
+  test.describe.configure({ mode: 'parallel', timeout: 90_000 });

   test.beforeAll(async () => {
-    test.setTimeout(90_000);
     app = await commonSetup.commit();
     …
   });

• In the “invalid middleware at root” suite

-  test.describe.configure({ mode: 'parallel' });
+  test.describe.configure({ mode: 'parallel', timeout: 90_000 });

   test.beforeAll(async () => {
-    test.setTimeout(90_000);
     app = await commonSetup.addFile('middleware.ts', …).commit();
     …
   });

• In the “invalid middleware inside app” suite

-  test.describe.configure({ mode: 'parallel' });
+  test.describe.configure({ mode: 'parallel', timeout: 90_000 });

   test.beforeAll(async () => {
-    test.setTimeout(90_000);
     app = await commonSetup.addFile('src/app/middleware.ts', …).commit();
     …
   });

Optional: this pattern (in-hook test.setTimeout) appears in many other integration tests—consider aligning them to use suite-level timeouts as well.

packages/clerk-js/bundlewatch.config.json (2)

1-35: Add explicit compression setting to bundlewatch config

We verified that packages/clerk-js/bundlewatch.config.json is the only Bundlewatch config in the repo, and it doesn’t declare a compression mode. To keep your size measurements stable and transparent, explicitly add a "compression" field at the top of this file:

• packages/clerk-js/bundlewatch.config.json

 {
+  "compression": "gzip",
   "files": [
     { "path": "./dist/clerk.js", "maxSize": "628.65KB" },
     …
   ]
 }

(Use "brotli" instead if you prefer.)


3-3: Tight clerk.js budget; consider rounding up to reduce CI flakes

File: packages/clerk-js/bundlewatch.config.json
Lines: 3-3

The maxSize is currently set to 628.65KB:

{ "path": "./dist/clerk.js", "maxSize": "628.65KB" },

That level of precision can lead to flaky CI failures on trivial, non-functional changes (e.g., minor tree-shaking differences). I recommend rounding up the threshold—either to a neat value like 630KB or by adding ~2% headroom over the actual gzipped size.

Because dist/clerk.js isn’t checked in, the verification script warns that it can’t find the file. Please:

  1. Build the package so that packages/clerk-js/dist/clerk.js exists.

  2. Run this script to compute your current gzipped size and a +2% recommended threshold:

    #!/usr/bin/env bash
    set -euo pipefail
    
    python - <<'PY'
    import json, glob, gzip, math
    
    cfg = json.load(open("packages/clerk-js/bundlewatch.config.json"))
    base = "packages/clerk-js"
    
    def gz_size(p):
        return len(gzip.compress(open(p,"rb").read(), compresslevel=6))
    def fmt(b):
        return f"{math.ceil(b/1024*10)/10:.1f}KB"
    
    for e in cfg["files"]:
        if e["path"].endswith("clerk.js"):
            p = f"{base}/{e['path'][2:]}"
            b = gz_size(p)
            rec = math.ceil(b * 1.02)
            print("Current gzipped:", fmt(b), "→ recommended threshold:", fmt(rec))
    PY
  3. Update maxSize in the config to that rounded-up value.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ec4f8ba and cc98744.

📒 Files selected for processing (2)
  • integration/tests/middleware-placement.test.ts (3 hunks)
  • packages/clerk-js/bundlewatch.config.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • integration/tests/middleware-placement.test.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • integration/tests/middleware-placement.test.ts
  • packages/clerk-js/bundlewatch.config.json
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • integration/tests/middleware-placement.test.ts
integration/**

📄 CodeRabbit Inference Engine (.cursor/rules/global.mdc)

Framework integration templates and E2E tests should be placed under the integration/ directory

Files:

  • integration/tests/middleware-placement.test.ts
integration/**/*

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

End-to-end tests and integration templates must be located in the 'integration/' directory.

Files:

  • integration/tests/middleware-placement.test.ts
integration/**/*.{test,spec}.{js,ts}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Integration tests should use Playwright.

Files:

  • integration/tests/middleware-placement.test.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • integration/tests/middleware-placement.test.ts
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • integration/tests/middleware-placement.test.ts
  • packages/clerk-js/bundlewatch.config.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (26)
  • GitHub Check: Integration Tests (nextjs, chrome, 14)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (machine, chrome)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (tanstack-react-router, chrome)
  • GitHub Check: Integration Tests (sessions, chrome)
  • GitHub Check: Integration Tests (elements, chrome)
  • GitHub Check: Integration Tests (nuxt, chrome)
  • GitHub Check: Integration Tests (expo-web, chrome)
  • GitHub Check: Integration Tests (react-router, chrome)
  • GitHub Check: Integration Tests (astro, chrome)
  • GitHub Check: Integration Tests (tanstack-react-start, chrome)
  • GitHub Check: Integration Tests (vue, chrome)
  • GitHub Check: Integration Tests (express, chrome)
  • GitHub Check: Integration Tests (quickstart, chrome)
  • GitHub Check: Integration Tests (localhost, chrome)
  • GitHub Check: Integration Tests (generic, chrome)
  • GitHub Check: Integration Tests (ap-flows, chrome)
  • GitHub Check: Unit Tests (22, **)
  • GitHub Check: Publish with pkg-pr-new
  • GitHub Check: Unit Tests (18, --filter=@clerk/astro --filter=@clerk/backend --filter=@clerk/express --filter=@c...
  • GitHub Check: Static analysis
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (2)
packages/clerk-js/bundlewatch.config.json (2)

9-9: Ensure packages/clerk-js/dist/vendors*.js is built and threshold is verified

  • The ./dist/vendors*.js pattern didn’t match any files—please build the package so we can measure the actual gzipped size.
  • Once built, run the provided script (or equivalent) to find the largest vendor chunk and update maxSize. Round to a clean value (e.g., 45 KB) and include ~3% headroom to reduce noise from minor dependency shifts.
  • Confirm whether the new input-otp dependency is bundled into vendors*.js or split into its own chunk; adjust your bundlewatch budgets accordingly.

13-33: Bundlewatch patterns require manual verification against built assets
The sandbox build had no dist files, so the automated check flagged every glob as unmatched yet found no “uncovered” chunks. To ensure your patterns actually match real output on case-sensitive filesystems:

  • Run a local build of packages/clerk-js (e.g., npm run build or your standard build command).
  • Rerun the helper script from the original comment to:
    • Report any patterns that match nothing (typos, casing or hyphen vs camelCase mismatches).
    • List any emitted .js chunks not covered by your globs.
    • Compare current gzipped sizes against configured maxSize and suggest +2% headroom at 0.1 KB granularity.
  • Adjust your glob patterns to reflect actual filenames (consider using all-lowercase with hyphens if your bundler emits kebab-case).
  • Round maxSize entries to one decimal place (e.g., 8.9KB instead of 8.86KB) to reduce noise over time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants