-
Notifications
You must be signed in to change notification settings - Fork 377
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
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: cc98744 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
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 |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/elements
@clerk/clerk-expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/clerk-react
@clerk/react-router
@clerk/remix
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/themes
@clerk/types
@clerk/upgrade
@clerk/vue
commit: |
📝 WalkthroughWalkthroughReworks 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
Assessment against linked issues: Out-of-scope changes
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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/historyThe 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/PropsWithChildrenThe 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.
📒 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
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
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
Useconst assertions
for literal types:as const
Usesatisfies
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 ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
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 solidCapturing 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 |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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 }); |
There was a problem hiding this comment.
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) => { |
There was a problem hiding this comment.
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)
f5d9986
to
fa81977
Compare
There was a problem hiding this 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 falseWaiting 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 consistencyThis 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 namespaceUsing 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 delayMake 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 APIClarify 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 publiccommon
APIPublic 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.
⛔ 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
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
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
Useconst assertions
for literal types:as const
Usesatisfies
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 ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
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 correctlyGood 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 submissionsThe 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. Sincecontinue()
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.
There was a problem hiding this 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 churnThe 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 bufferBudgets 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.
📒 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 appropriatelyGood 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 outputIt 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 thedist
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." fiIf 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.
There was a problem hiding this 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.tsConfigure 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 configWe 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 flakesFile: packages/clerk-js/bundlewatch.config.json
Lines: 3-3The 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:
Build the package so that
packages/clerk-js/dist/clerk.js
exists.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)) PYUpdate
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.
📒 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
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
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
Useconst assertions
for literal types:as const
Usesatisfies
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 ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
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
: Ensurepackages/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 intovendors*.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 nodist
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 of8.86KB
) to reduce noise over time.
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:
@clerk/testing
common utilities, in a non-breaking way, to support this new element as well as the old one.prepare
andattempt
requests over adding timeouts and delays.Known (intentional) differences from our existing approach:
Fixes USER-2571
Checklist
pnpm test
runs as expected.pnpm build
runs as expected.Type of change
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Refactor
Chores
Tests