Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/true-items-burn.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Empty changeset will likely fail the Changesets check — declare a “none” bump or remove it

Use a no-release changeset so CI passes, e.g.:

-----------+---+"@clerk/clerk-js": none+---++chore(clerk-js): convert additional tests to Vitest (no release)

If the published package name differs, replace "@clerk/clerk-js" accordingly.


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"PWD: $(pwd)"echoecho"=== .changeset directory listing ==="
ls -la .changeset ||trueechoecho"=== Content of .changeset/true-items-burn.md ==="if [ -f .changeset/true-items-burn.md ];then
sed -n '1,200p' .changeset/true-items-burn.md ||trueelseecho"file not found"fiechoecho"=== Root package.json (if present) ==="if [ -f package.json ];then
sed -n '1,200p' package.json ||trueelseecho"no root package.json"fiechoecho"=== package.json files (depth 3) ==="
find . -maxdepth 3 -type f -name package.json -print -exec sed -n '1,80p' {} \;||true

Length of output: 74924


Empty changeset — add a no-release bump or remove the file (CI will fail)

.changedset/true-items-burn.md (lines 1–2) contains only frontmatter ("---" / "---"). Replace with a no-release changeset or delete the file. Example (use the correct package name if different):


"@clerk/clerk-js": none

chore(clerk-js): convert additional tests to Vitest (no release)

🤖 Prompt for AI Agents
.changeset/true-items-burn.md lines 1-2 contain only empty frontmatter which
will cause CI to fail; replace the file content with a valid no-release
changeset or delete the file. Edit the file to include a YAML frontmatter block
with the package name mapped to "none" (or the correct package name if
different) and a short summary message below, for example a minimal changeset
that sets "@your/package": none and a one-line description, or remove the file
entirely if no changeset is needed.

Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getCookieDomain } from '../../getCookieDomain';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createClientUatCookie } from '../clientUat';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getCookieDomain');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getCookieDomain');
vi.mock('../../getSecureAttribute');

describe('createClientUatCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockExpires = new Date('2024-12-31');
const mockDomain = 'test.domain';
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getCookieDomain as jest.Mock).mockReturnValue(mockDomain);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getCookieDomain as ReturnType<typeof vi.fn>).mockReturnValue(mockDomain);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -60,7 +61,7 @@ describe('createClientUatCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createClientUatCookie(mockCookieSuffix);
cookieHandler.set({
id: 'test-client',
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createSessionCookie } from '../session';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getSecureAttribute');

describe('createSessionCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockToken = 'test-token';
const mockExpires = new Date('2024-12-31');
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -52,7 +53,7 @@ describe('createSessionCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createSessionCookie(mockCookieSuffix);
cookieHandler.set(mockToken);

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Organization } from '../internal';

describe('Organization', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationDomain } from '../internal';

describe('OrganizationDomain', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationInvitation } from '../internal';

describe('OrganizationInvitation', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembership } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembershipRequest } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationSuggestion } from '../internal';

describe('OrganizationSuggestion', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { PublicUserData } from '../PublicUserData';

describe('PublicUserData', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Waitlist } from '../internal';

describe('Waitlist', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { populateCacheRemoveItem, populateCacheUpdateItem } from '../utils';

const staleInfiniteCache = [
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
import type { LoadedClerk, SignUpResource } from '@clerk/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { handleCombinedFlowTransfer, hasOptionalFields } from '../handleCombinedFlowTransfer';

// eslint-disable-next-line no-var -- Jest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: jest.Mock;
jest.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = jest.fn();
// eslint-disable-next-line no-var -- Vitest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: ReturnType<typeof vi.fn>;
vi.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = vi.fn();
return {
lazyCompleteSignUpFlow: () => {
return Promise.resolve(mockCompleteSignUpFlow);
},
};
});

const mockNavigate = jest.fn();
const mockHandleError = jest.fn();
const mockNavigate = vi.fn();
const mockHandleError = vi.fn();

describe('handleCombinedFlowTransfer', () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should call completeSignUpFlow', async () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockResolvedValue({}),
create: vi.fn().mockResolvedValue({}),
optionalFields: [],
},
},
Expand All@@ -40,7 +41,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockCompleteSignUpFlow).toHaveBeenCalled();
Expand All@@ -50,7 +51,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['phone_number'],
},
},
Expand All@@ -65,7 +66,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).not.toHaveBeenCalled();
Expand All@@ -77,7 +78,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['password'],
},
},
Expand All@@ -92,7 +93,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: true,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -104,7 +105,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: [],
},
},
Expand All@@ -119,7 +120,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -131,7 +132,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['first_name'],
},
},
Expand All@@ -146,7 +147,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { SignInResource } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import type { FormControlState } from '@/ui/utils/useFormControl';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { Attribute } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import { determineActiveFields, determineRequiredIdentifier, getInitialActiveIdentifier } from '../signUpFormHelpers';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { VerificationJSON } from '@clerk/types';
import { describe, expect, it } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

LGTM: Explicit Vitest imports are correct for this migration

Using import { describe, expect, it } from 'vitest' is consistent with Vitest and avoids relying on globals. No further changes needed here.

To ensure the broader migration is clean, run:


🏁 Script executed:

#!/bin/bashset -euo pipefail
# 1) Flag any lingering direct Jest API usages under clerk-js
rg -nP -C2 '\bjest\.(fn|spyOn|mock|useFakeTimers|advanceTimersByTime|runAllTimers|resetAllMocks|clearAllMocks)\b' packages/clerk-js ||true# 2) Identify tests that call describe/it/test/expect without importing vitest (if globals are disabled)# Adjust the glob if your tests live elsewhere.
fd -t f -a --glob 'packages/clerk-js/**/__tests__/**/*.{ts,tsx,js,jsx}' \
|whileread -r f;doif rg -nP '^\s*(describe|it|test|expect)\s*\('"$f"> /dev/null;thenif! rg -nP "from\s+['\"]vitest['\"]""$f"> /dev/null;thenecho"Potential missing vitest import: $f"fifidone

Length of output: 120643


LGTM for this file's explicit Vitest imports — migration is incomplete; critical jest -> vi changes required

The import in packages/clerk-js/src/ui/components/UserProfile/tests/utils.spec.ts is correct, but the scan found many direct Jest API usages that must be migrated to Vitest (vi) or handled via a compatibility shim.

  • Key offenders (examples): packages/clerk-js/src/ui/utils/test/runFakeTimers.ts (jest.useFakeTimers / jest.advanceTimersByTime / jest.runAllTimers), packages/clerk-js/src/ui/utils/test/mockHelpers.ts (jest.fn), packages/clerk-js/jest.setup.ts (global Jest setup using jest.fn), packages/clerk-js/src/testUtils.ts (jest.spyOn / jest.fn), packages/clerk-js/src/core/test/fixtures.ts (global.fetch = jest.fn), plus many tests using jest.useFakeTimers(), jest.mock(), jest.fn(), jest.spyOn, etc.
  • Action: replace jest.* calls with vi.* (or import { vi } from 'vitest' where needed) and convert the Jest setup to a Vitest setup (or enable a compatibility layer). Re-run the verification script until no lingering jest.* usages remain.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserProfile/__tests__/utils.spec.ts
around line 2, the file correctly imports Vitest but the repository still
contains many direct Jest API usages that must be migrated; replace all jest.*
usages with vi.* (or add import { vi } from 'vitest' where needed) across the
listed files (packages/clerk-js/src/ui/utils/test/runFakeTimers.ts,
packages/clerk-js/src/ui/utils/test/mockHelpers.ts,
packages/clerk-js/jest.setup.ts, packages/clerk-js/src/testUtils.ts,
packages/clerk-js/src/core/test/fixtures.ts and all __tests__), convert global
Jest setup into a Vitest setup file (or enable a compatibility shim), change
jest.useFakeTimers/advanceTimers/runAllTimers →
vi.useFakeTimers/advanceTimersToNextTimer/runAllTimers as appropriate, replace
jest.fn/jest.spyOn/jest.mock → vi.fn/vi.spyOn/vi.mock, and re-run the
verification script until no jest.* references remain.


import { EmailAddress, PhoneNumber } from '../../../../core/resources';
import { sortIdentificationBasedOnVerification } from '../utils';
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';
import { renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { useFormattedPhoneNumber } from '../useFormattedPhoneNumber';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createInternalTheme } from '../createInternalTheme';

describe('createInternalTheme', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore getComputedStyle after each test to avoid global pollution

window.getComputedStyle is mocked but never restored; reassigning global.window to the same originalWindow doesn’t undo the mock. This can leak into other tests. Prefer a spy + restore.

Apply:

-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@
- const originalWindow = window;- const mockGetComputedStyle = vi.fn();+ const mockGetComputedStyle = vi.fn();
@@
- beforeEach(() => {- // Mock window.getComputedStyle- mockGetComputedStyle.mockReset();- Object.defineProperty(window, 'getComputedStyle', {- value: mockGetComputedStyle,- writable: true,- });- });+ beforeEach(() => {+ mockGetComputedStyle.mockReset();+ vi.spyOn(window, 'getComputedStyle').mockImplementation(+ mockGetComputedStyle as unknown as typeof window.getComputedStyle,+ );+ });
@@
- afterEach(() => {- // Restore window- Object.defineProperty(global, 'window', {- value: originalWindow,- writable: true,- });- });+ afterEach(() => {+ vi.restoreAllMocks();+ });

Also applies to: 8-17, 19-25

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/hooks/__tests__/useDirection.spec.ts around lines 2
and also covering 8-17 and 19-25, the test suite mocks window.getComputedStyle
but never restores it causing global test pollution; replace the direct
reassignment with a spy (e.g., vi.spyOn(window, 'getComputedStyle')) and then
call mockRestore() (or .mockRestore()) in afterEach to restore the original
implementation, ensuring every test cleans up its spy/mocking in a
finally/afterEach block so other tests are unaffected.


import { useDirection } from '../useDirection';

describe('useDirection', () => {
const originalWindow = window;
const mockGetComputedStyle = jest.fn();
const mockGetComputedStyle = vi.fn();

beforeEach(() => {
// Mock window.getComputedStyle
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createCssVariables } from '../createCssVariables';
import { createVariants } from '../createVariants';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatSafeIdentifier } from '../formatSafeIdentifier';

describe('formatSafeIdentifier', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatToCompactNumber } from '../intl';

describe('formatToCompactNumber()', function () {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/true-items-burn.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Empty changeset will likely fail the Changesets check — declare a “none” bump or remove it

Use a no-release changeset so CI passes, e.g.:

-----------+---+"@clerk/clerk-js": none+---++chore(clerk-js): convert additional tests to Vitest (no release)

If the published package name differs, replace "@clerk/clerk-js" accordingly.


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"PWD: $(pwd)"echoecho"=== .changeset directory listing ==="
ls -la .changeset ||trueechoecho"=== Content of .changeset/true-items-burn.md ==="if [ -f .changeset/true-items-burn.md ];then
sed -n '1,200p' .changeset/true-items-burn.md ||trueelseecho"file not found"fiechoecho"=== Root package.json (if present) ==="if [ -f package.json ];then
sed -n '1,200p' package.json ||trueelseecho"no root package.json"fiechoecho"=== package.json files (depth 3) ==="
find . -maxdepth 3 -type f -name package.json -print -exec sed -n '1,80p' {} \;||true

Length of output: 74924


Empty changeset — add a no-release bump or remove the file (CI will fail)

.changedset/true-items-burn.md (lines 1–2) contains only frontmatter ("---" / "---"). Replace with a no-release changeset or delete the file. Example (use the correct package name if different):


"@clerk/clerk-js": none

chore(clerk-js): convert additional tests to Vitest (no release)

🤖 Prompt for AI Agents
.changeset/true-items-burn.md lines 1-2 contain only empty frontmatter which
will cause CI to fail; replace the file content with a valid no-release
changeset or delete the file. Edit the file to include a YAML frontmatter block
with the package name mapped to "none" (or the correct package name if
different) and a short summary message below, for example a minimal changeset
that sets "@your/package": none and a one-line description, or remove the file
entirely if no changeset is needed.

Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getCookieDomain } from '../../getCookieDomain';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createClientUatCookie } from '../clientUat';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getCookieDomain');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getCookieDomain');
vi.mock('../../getSecureAttribute');

describe('createClientUatCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockExpires = new Date('2024-12-31');
const mockDomain = 'test.domain';
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getCookieDomain as jest.Mock).mockReturnValue(mockDomain);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getCookieDomain as ReturnType<typeof vi.fn>).mockReturnValue(mockDomain);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -60,7 +61,7 @@ describe('createClientUatCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createClientUatCookie(mockCookieSuffix);
cookieHandler.set({
id: 'test-client',
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createSessionCookie } from '../session';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getSecureAttribute');

describe('createSessionCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockToken = 'test-token';
const mockExpires = new Date('2024-12-31');
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -52,7 +53,7 @@ describe('createSessionCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createSessionCookie(mockCookieSuffix);
cookieHandler.set(mockToken);

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Organization } from '../internal';

describe('Organization', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationDomain } from '../internal';

describe('OrganizationDomain', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationInvitation } from '../internal';

describe('OrganizationInvitation', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembership } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembershipRequest } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationSuggestion } from '../internal';

describe('OrganizationSuggestion', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { PublicUserData } from '../PublicUserData';

describe('PublicUserData', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Waitlist } from '../internal';

describe('Waitlist', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { populateCacheRemoveItem, populateCacheUpdateItem } from '../utils';

const staleInfiniteCache = [
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
import type { LoadedClerk, SignUpResource } from '@clerk/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { handleCombinedFlowTransfer, hasOptionalFields } from '../handleCombinedFlowTransfer';

// eslint-disable-next-line no-var -- Jest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: jest.Mock;
jest.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = jest.fn();
// eslint-disable-next-line no-var -- Vitest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: ReturnType<typeof vi.fn>;
vi.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = vi.fn();
return {
lazyCompleteSignUpFlow: () => {
return Promise.resolve(mockCompleteSignUpFlow);
},
};
});

const mockNavigate = jest.fn();
const mockHandleError = jest.fn();
const mockNavigate = vi.fn();
const mockHandleError = vi.fn();

describe('handleCombinedFlowTransfer', () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should call completeSignUpFlow', async () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockResolvedValue({}),
create: vi.fn().mockResolvedValue({}),
optionalFields: [],
},
},
Expand All@@ -40,7 +41,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockCompleteSignUpFlow).toHaveBeenCalled();
Expand All@@ -50,7 +51,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['phone_number'],
},
},
Expand All@@ -65,7 +66,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).not.toHaveBeenCalled();
Expand All@@ -77,7 +78,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['password'],
},
},
Expand All@@ -92,7 +93,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: true,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -104,7 +105,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: [],
},
},
Expand All@@ -119,7 +120,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -131,7 +132,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['first_name'],
},
},
Expand All@@ -146,7 +147,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { SignInResource } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import type { FormControlState } from '@/ui/utils/useFormControl';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { Attribute } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import { determineActiveFields, determineRequiredIdentifier, getInitialActiveIdentifier } from '../signUpFormHelpers';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { VerificationJSON } from '@clerk/types';
import { describe, expect, it } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

LGTM: Explicit Vitest imports are correct for this migration

Using import { describe, expect, it } from 'vitest' is consistent with Vitest and avoids relying on globals. No further changes needed here.

To ensure the broader migration is clean, run:


🏁 Script executed:

#!/bin/bashset -euo pipefail
# 1) Flag any lingering direct Jest API usages under clerk-js
rg -nP -C2 '\bjest\.(fn|spyOn|mock|useFakeTimers|advanceTimersByTime|runAllTimers|resetAllMocks|clearAllMocks)\b' packages/clerk-js ||true# 2) Identify tests that call describe/it/test/expect without importing vitest (if globals are disabled)# Adjust the glob if your tests live elsewhere.
fd -t f -a --glob 'packages/clerk-js/**/__tests__/**/*.{ts,tsx,js,jsx}' \
|whileread -r f;doif rg -nP '^\s*(describe|it|test|expect)\s*\('"$f"> /dev/null;thenif! rg -nP "from\s+['\"]vitest['\"]""$f"> /dev/null;thenecho"Potential missing vitest import: $f"fifidone

Length of output: 120643


LGTM for this file's explicit Vitest imports — migration is incomplete; critical jest -> vi changes required

The import in packages/clerk-js/src/ui/components/UserProfile/tests/utils.spec.ts is correct, but the scan found many direct Jest API usages that must be migrated to Vitest (vi) or handled via a compatibility shim.

  • Key offenders (examples): packages/clerk-js/src/ui/utils/test/runFakeTimers.ts (jest.useFakeTimers / jest.advanceTimersByTime / jest.runAllTimers), packages/clerk-js/src/ui/utils/test/mockHelpers.ts (jest.fn), packages/clerk-js/jest.setup.ts (global Jest setup using jest.fn), packages/clerk-js/src/testUtils.ts (jest.spyOn / jest.fn), packages/clerk-js/src/core/test/fixtures.ts (global.fetch = jest.fn), plus many tests using jest.useFakeTimers(), jest.mock(), jest.fn(), jest.spyOn, etc.
  • Action: replace jest.* calls with vi.* (or import { vi } from 'vitest' where needed) and convert the Jest setup to a Vitest setup (or enable a compatibility layer). Re-run the verification script until no lingering jest.* usages remain.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserProfile/__tests__/utils.spec.ts
around line 2, the file correctly imports Vitest but the repository still
contains many direct Jest API usages that must be migrated; replace all jest.*
usages with vi.* (or add import { vi } from 'vitest' where needed) across the
listed files (packages/clerk-js/src/ui/utils/test/runFakeTimers.ts,
packages/clerk-js/src/ui/utils/test/mockHelpers.ts,
packages/clerk-js/jest.setup.ts, packages/clerk-js/src/testUtils.ts,
packages/clerk-js/src/core/test/fixtures.ts and all __tests__), convert global
Jest setup into a Vitest setup file (or enable a compatibility shim), change
jest.useFakeTimers/advanceTimers/runAllTimers →
vi.useFakeTimers/advanceTimersToNextTimer/runAllTimers as appropriate, replace
jest.fn/jest.spyOn/jest.mock → vi.fn/vi.spyOn/vi.mock, and re-run the
verification script until no jest.* references remain.


import { EmailAddress, PhoneNumber } from '../../../../core/resources';
import { sortIdentificationBasedOnVerification } from '../utils';
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';
import { renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { useFormattedPhoneNumber } from '../useFormattedPhoneNumber';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createInternalTheme } from '../createInternalTheme';

describe('createInternalTheme', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore getComputedStyle after each test to avoid global pollution

window.getComputedStyle is mocked but never restored; reassigning global.window to the same originalWindow doesn’t undo the mock. This can leak into other tests. Prefer a spy + restore.

Apply:

-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@
- const originalWindow = window;- const mockGetComputedStyle = vi.fn();+ const mockGetComputedStyle = vi.fn();
@@
- beforeEach(() => {- // Mock window.getComputedStyle- mockGetComputedStyle.mockReset();- Object.defineProperty(window, 'getComputedStyle', {- value: mockGetComputedStyle,- writable: true,- });- });+ beforeEach(() => {+ mockGetComputedStyle.mockReset();+ vi.spyOn(window, 'getComputedStyle').mockImplementation(+ mockGetComputedStyle as unknown as typeof window.getComputedStyle,+ );+ });
@@
- afterEach(() => {- // Restore window- Object.defineProperty(global, 'window', {- value: originalWindow,- writable: true,- });- });+ afterEach(() => {+ vi.restoreAllMocks();+ });

Also applies to: 8-17, 19-25

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/hooks/__tests__/useDirection.spec.ts around lines 2
and also covering 8-17 and 19-25, the test suite mocks window.getComputedStyle
but never restores it causing global test pollution; replace the direct
reassignment with a spy (e.g., vi.spyOn(window, 'getComputedStyle')) and then
call mockRestore() (or .mockRestore()) in afterEach to restore the original
implementation, ensuring every test cleans up its spy/mocking in a
finally/afterEach block so other tests are unaffected.


import { useDirection } from '../useDirection';

describe('useDirection', () => {
const originalWindow = window;
const mockGetComputedStyle = jest.fn();
const mockGetComputedStyle = vi.fn();

beforeEach(() => {
// Mock window.getComputedStyle
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createCssVariables } from '../createCssVariables';
import { createVariants } from '../createVariants';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatSafeIdentifier } from '../formatSafeIdentifier';

describe('formatSafeIdentifier', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatToCompactNumber } from '../intl';

describe('formatToCompactNumber()', function () {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/true-items-burn.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Empty changeset will likely fail the Changesets check — declare a “none” bump or remove it

Use a no-release changeset so CI passes, e.g.:

-----------+---+"@clerk/clerk-js": none+---++chore(clerk-js): convert additional tests to Vitest (no release)

If the published package name differs, replace "@clerk/clerk-js" accordingly.


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"PWD: $(pwd)"echoecho"=== .changeset directory listing ==="
ls -la .changeset ||trueechoecho"=== Content of .changeset/true-items-burn.md ==="if [ -f .changeset/true-items-burn.md ];then
sed -n '1,200p' .changeset/true-items-burn.md ||trueelseecho"file not found"fiechoecho"=== Root package.json (if present) ==="if [ -f package.json ];then
sed -n '1,200p' package.json ||trueelseecho"no root package.json"fiechoecho"=== package.json files (depth 3) ==="
find . -maxdepth 3 -type f -name package.json -print -exec sed -n '1,80p' {} \;||true

Length of output: 74924


Empty changeset — add a no-release bump or remove the file (CI will fail)

.changedset/true-items-burn.md (lines 1–2) contains only frontmatter ("---" / "---"). Replace with a no-release changeset or delete the file. Example (use the correct package name if different):


"@clerk/clerk-js": none

chore(clerk-js): convert additional tests to Vitest (no release)

🤖 Prompt for AI Agents
.changeset/true-items-burn.md lines 1-2 contain only empty frontmatter which
will cause CI to fail; replace the file content with a valid no-release
changeset or delete the file. Edit the file to include a YAML frontmatter block
with the package name mapped to "none" (or the correct package name if
different) and a short summary message below, for example a minimal changeset
that sets "@your/package": none and a one-line description, or remove the file
entirely if no changeset is needed.

Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getCookieDomain } from '../../getCookieDomain';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createClientUatCookie } from '../clientUat';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getCookieDomain');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getCookieDomain');
vi.mock('../../getSecureAttribute');

describe('createClientUatCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockExpires = new Date('2024-12-31');
const mockDomain = 'test.domain';
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getCookieDomain as jest.Mock).mockReturnValue(mockDomain);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getCookieDomain as ReturnType<typeof vi.fn>).mockReturnValue(mockDomain);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -60,7 +61,7 @@ describe('createClientUatCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createClientUatCookie(mockCookieSuffix);
cookieHandler.set({
id: 'test-client',
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createSessionCookie } from '../session';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getSecureAttribute');

describe('createSessionCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockToken = 'test-token';
const mockExpires = new Date('2024-12-31');
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -52,7 +53,7 @@ describe('createSessionCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createSessionCookie(mockCookieSuffix);
cookieHandler.set(mockToken);

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Organization } from '../internal';

describe('Organization', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationDomain } from '../internal';

describe('OrganizationDomain', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationInvitation } from '../internal';

describe('OrganizationInvitation', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembership } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembershipRequest } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationSuggestion } from '../internal';

describe('OrganizationSuggestion', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { PublicUserData } from '../PublicUserData';

describe('PublicUserData', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Waitlist } from '../internal';

describe('Waitlist', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { populateCacheRemoveItem, populateCacheUpdateItem } from '../utils';

const staleInfiniteCache = [
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
import type { LoadedClerk, SignUpResource } from '@clerk/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { handleCombinedFlowTransfer, hasOptionalFields } from '../handleCombinedFlowTransfer';

// eslint-disable-next-line no-var -- Jest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: jest.Mock;
jest.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = jest.fn();
// eslint-disable-next-line no-var -- Vitest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: ReturnType<typeof vi.fn>;
vi.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = vi.fn();
return {
lazyCompleteSignUpFlow: () => {
return Promise.resolve(mockCompleteSignUpFlow);
},
};
});

const mockNavigate = jest.fn();
const mockHandleError = jest.fn();
const mockNavigate = vi.fn();
const mockHandleError = vi.fn();

describe('handleCombinedFlowTransfer', () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should call completeSignUpFlow', async () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockResolvedValue({}),
create: vi.fn().mockResolvedValue({}),
optionalFields: [],
},
},
Expand All@@ -40,7 +41,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockCompleteSignUpFlow).toHaveBeenCalled();
Expand All@@ -50,7 +51,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['phone_number'],
},
},
Expand All@@ -65,7 +66,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).not.toHaveBeenCalled();
Expand All@@ -77,7 +78,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['password'],
},
},
Expand All@@ -92,7 +93,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: true,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -104,7 +105,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: [],
},
},
Expand All@@ -119,7 +120,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -131,7 +132,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['first_name'],
},
},
Expand All@@ -146,7 +147,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { SignInResource } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import type { FormControlState } from '@/ui/utils/useFormControl';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { Attribute } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import { determineActiveFields, determineRequiredIdentifier, getInitialActiveIdentifier } from '../signUpFormHelpers';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { VerificationJSON } from '@clerk/types';
import { describe, expect, it } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

LGTM: Explicit Vitest imports are correct for this migration

Using import { describe, expect, it } from 'vitest' is consistent with Vitest and avoids relying on globals. No further changes needed here.

To ensure the broader migration is clean, run:


🏁 Script executed:

#!/bin/bashset -euo pipefail
# 1) Flag any lingering direct Jest API usages under clerk-js
rg -nP -C2 '\bjest\.(fn|spyOn|mock|useFakeTimers|advanceTimersByTime|runAllTimers|resetAllMocks|clearAllMocks)\b' packages/clerk-js ||true# 2) Identify tests that call describe/it/test/expect without importing vitest (if globals are disabled)# Adjust the glob if your tests live elsewhere.
fd -t f -a --glob 'packages/clerk-js/**/__tests__/**/*.{ts,tsx,js,jsx}' \
|whileread -r f;doif rg -nP '^\s*(describe|it|test|expect)\s*\('"$f"> /dev/null;thenif! rg -nP "from\s+['\"]vitest['\"]""$f"> /dev/null;thenecho"Potential missing vitest import: $f"fifidone

Length of output: 120643


LGTM for this file's explicit Vitest imports — migration is incomplete; critical jest -> vi changes required

The import in packages/clerk-js/src/ui/components/UserProfile/tests/utils.spec.ts is correct, but the scan found many direct Jest API usages that must be migrated to Vitest (vi) or handled via a compatibility shim.

  • Key offenders (examples): packages/clerk-js/src/ui/utils/test/runFakeTimers.ts (jest.useFakeTimers / jest.advanceTimersByTime / jest.runAllTimers), packages/clerk-js/src/ui/utils/test/mockHelpers.ts (jest.fn), packages/clerk-js/jest.setup.ts (global Jest setup using jest.fn), packages/clerk-js/src/testUtils.ts (jest.spyOn / jest.fn), packages/clerk-js/src/core/test/fixtures.ts (global.fetch = jest.fn), plus many tests using jest.useFakeTimers(), jest.mock(), jest.fn(), jest.spyOn, etc.
  • Action: replace jest.* calls with vi.* (or import { vi } from 'vitest' where needed) and convert the Jest setup to a Vitest setup (or enable a compatibility layer). Re-run the verification script until no lingering jest.* usages remain.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserProfile/__tests__/utils.spec.ts
around line 2, the file correctly imports Vitest but the repository still
contains many direct Jest API usages that must be migrated; replace all jest.*
usages with vi.* (or add import { vi } from 'vitest' where needed) across the
listed files (packages/clerk-js/src/ui/utils/test/runFakeTimers.ts,
packages/clerk-js/src/ui/utils/test/mockHelpers.ts,
packages/clerk-js/jest.setup.ts, packages/clerk-js/src/testUtils.ts,
packages/clerk-js/src/core/test/fixtures.ts and all __tests__), convert global
Jest setup into a Vitest setup file (or enable a compatibility shim), change
jest.useFakeTimers/advanceTimers/runAllTimers →
vi.useFakeTimers/advanceTimersToNextTimer/runAllTimers as appropriate, replace
jest.fn/jest.spyOn/jest.mock → vi.fn/vi.spyOn/vi.mock, and re-run the
verification script until no jest.* references remain.


import { EmailAddress, PhoneNumber } from '../../../../core/resources';
import { sortIdentificationBasedOnVerification } from '../utils';
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';
import { renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { useFormattedPhoneNumber } from '../useFormattedPhoneNumber';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createInternalTheme } from '../createInternalTheme';

describe('createInternalTheme', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore getComputedStyle after each test to avoid global pollution

window.getComputedStyle is mocked but never restored; reassigning global.window to the same originalWindow doesn’t undo the mock. This can leak into other tests. Prefer a spy + restore.

Apply:

-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@
- const originalWindow = window;- const mockGetComputedStyle = vi.fn();+ const mockGetComputedStyle = vi.fn();
@@
- beforeEach(() => {- // Mock window.getComputedStyle- mockGetComputedStyle.mockReset();- Object.defineProperty(window, 'getComputedStyle', {- value: mockGetComputedStyle,- writable: true,- });- });+ beforeEach(() => {+ mockGetComputedStyle.mockReset();+ vi.spyOn(window, 'getComputedStyle').mockImplementation(+ mockGetComputedStyle as unknown as typeof window.getComputedStyle,+ );+ });
@@
- afterEach(() => {- // Restore window- Object.defineProperty(global, 'window', {- value: originalWindow,- writable: true,- });- });+ afterEach(() => {+ vi.restoreAllMocks();+ });

Also applies to: 8-17, 19-25

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/hooks/__tests__/useDirection.spec.ts around lines 2
and also covering 8-17 and 19-25, the test suite mocks window.getComputedStyle
but never restores it causing global test pollution; replace the direct
reassignment with a spy (e.g., vi.spyOn(window, 'getComputedStyle')) and then
call mockRestore() (or .mockRestore()) in afterEach to restore the original
implementation, ensuring every test cleans up its spy/mocking in a
finally/afterEach block so other tests are unaffected.


import { useDirection } from '../useDirection';

describe('useDirection', () => {
const originalWindow = window;
const mockGetComputedStyle = jest.fn();
const mockGetComputedStyle = vi.fn();

beforeEach(() => {
// Mock window.getComputedStyle
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createCssVariables } from '../createCssVariables';
import { createVariants } from '../createVariants';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatSafeIdentifier } from '../formatSafeIdentifier';

describe('formatSafeIdentifier', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatToCompactNumber } from '../intl';

describe('formatToCompactNumber()', function () {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/true-items-burn.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Empty changeset will likely fail the Changesets check — declare a “none” bump or remove it

Use a no-release changeset so CI passes, e.g.:

-----------+---+"@clerk/clerk-js": none+---++chore(clerk-js): convert additional tests to Vitest (no release)

If the published package name differs, replace "@clerk/clerk-js" accordingly.


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"PWD: $(pwd)"echoecho"=== .changeset directory listing ==="
ls -la .changeset ||trueechoecho"=== Content of .changeset/true-items-burn.md ==="if [ -f .changeset/true-items-burn.md ];then
sed -n '1,200p' .changeset/true-items-burn.md ||trueelseecho"file not found"fiechoecho"=== Root package.json (if present) ==="if [ -f package.json ];then
sed -n '1,200p' package.json ||trueelseecho"no root package.json"fiechoecho"=== package.json files (depth 3) ==="
find . -maxdepth 3 -type f -name package.json -print -exec sed -n '1,80p' {} \;||true

Length of output: 74924


Empty changeset — add a no-release bump or remove the file (CI will fail)

.changedset/true-items-burn.md (lines 1–2) contains only frontmatter ("---" / "---"). Replace with a no-release changeset or delete the file. Example (use the correct package name if different):


"@clerk/clerk-js": none

chore(clerk-js): convert additional tests to Vitest (no release)

🤖 Prompt for AI Agents
.changeset/true-items-burn.md lines 1-2 contain only empty frontmatter which
will cause CI to fail; replace the file content with a valid no-release
changeset or delete the file. Edit the file to include a YAML frontmatter block
with the package name mapped to "none" (or the correct package name if
different) and a short summary message below, for example a minimal changeset
that sets "@your/package": none and a one-line description, or remove the file
entirely if no changeset is needed.

Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getCookieDomain } from '../../getCookieDomain';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createClientUatCookie } from '../clientUat';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getCookieDomain');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getCookieDomain');
vi.mock('../../getSecureAttribute');

describe('createClientUatCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockExpires = new Date('2024-12-31');
const mockDomain = 'test.domain';
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getCookieDomain as jest.Mock).mockReturnValue(mockDomain);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getCookieDomain as ReturnType<typeof vi.fn>).mockReturnValue(mockDomain);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -60,7 +61,7 @@ describe('createClientUatCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createClientUatCookie(mockCookieSuffix);
cookieHandler.set({
id: 'test-client',
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createSessionCookie } from '../session';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getSecureAttribute');

describe('createSessionCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockToken = 'test-token';
const mockExpires = new Date('2024-12-31');
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -52,7 +53,7 @@ describe('createSessionCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createSessionCookie(mockCookieSuffix);
cookieHandler.set(mockToken);

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Organization } from '../internal';

describe('Organization', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationDomain } from '../internal';

describe('OrganizationDomain', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationInvitation } from '../internal';

describe('OrganizationInvitation', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembership } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembershipRequest } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationSuggestion } from '../internal';

describe('OrganizationSuggestion', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { PublicUserData } from '../PublicUserData';

describe('PublicUserData', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Waitlist } from '../internal';

describe('Waitlist', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { populateCacheRemoveItem, populateCacheUpdateItem } from '../utils';

const staleInfiniteCache = [
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
import type { LoadedClerk, SignUpResource } from '@clerk/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { handleCombinedFlowTransfer, hasOptionalFields } from '../handleCombinedFlowTransfer';

// eslint-disable-next-line no-var -- Jest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: jest.Mock;
jest.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = jest.fn();
// eslint-disable-next-line no-var -- Vitest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: ReturnType<typeof vi.fn>;
vi.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = vi.fn();
return {
lazyCompleteSignUpFlow: () => {
return Promise.resolve(mockCompleteSignUpFlow);
},
};
});

const mockNavigate = jest.fn();
const mockHandleError = jest.fn();
const mockNavigate = vi.fn();
const mockHandleError = vi.fn();

describe('handleCombinedFlowTransfer', () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should call completeSignUpFlow', async () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockResolvedValue({}),
create: vi.fn().mockResolvedValue({}),
optionalFields: [],
},
},
Expand All@@ -40,7 +41,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockCompleteSignUpFlow).toHaveBeenCalled();
Expand All@@ -50,7 +51,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['phone_number'],
},
},
Expand All@@ -65,7 +66,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).not.toHaveBeenCalled();
Expand All@@ -77,7 +78,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['password'],
},
},
Expand All@@ -92,7 +93,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: true,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -104,7 +105,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: [],
},
},
Expand All@@ -119,7 +120,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -131,7 +132,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['first_name'],
},
},
Expand All@@ -146,7 +147,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { SignInResource } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import type { FormControlState } from '@/ui/utils/useFormControl';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { Attribute } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import { determineActiveFields, determineRequiredIdentifier, getInitialActiveIdentifier } from '../signUpFormHelpers';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { VerificationJSON } from '@clerk/types';
import { describe, expect, it } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

LGTM: Explicit Vitest imports are correct for this migration

Using import { describe, expect, it } from 'vitest' is consistent with Vitest and avoids relying on globals. No further changes needed here.

To ensure the broader migration is clean, run:


🏁 Script executed:

#!/bin/bashset -euo pipefail
# 1) Flag any lingering direct Jest API usages under clerk-js
rg -nP -C2 '\bjest\.(fn|spyOn|mock|useFakeTimers|advanceTimersByTime|runAllTimers|resetAllMocks|clearAllMocks)\b' packages/clerk-js ||true# 2) Identify tests that call describe/it/test/expect without importing vitest (if globals are disabled)# Adjust the glob if your tests live elsewhere.
fd -t f -a --glob 'packages/clerk-js/**/__tests__/**/*.{ts,tsx,js,jsx}' \
|whileread -r f;doif rg -nP '^\s*(describe|it|test|expect)\s*\('"$f"> /dev/null;thenif! rg -nP "from\s+['\"]vitest['\"]""$f"> /dev/null;thenecho"Potential missing vitest import: $f"fifidone

Length of output: 120643


LGTM for this file's explicit Vitest imports — migration is incomplete; critical jest -> vi changes required

The import in packages/clerk-js/src/ui/components/UserProfile/tests/utils.spec.ts is correct, but the scan found many direct Jest API usages that must be migrated to Vitest (vi) or handled via a compatibility shim.

  • Key offenders (examples): packages/clerk-js/src/ui/utils/test/runFakeTimers.ts (jest.useFakeTimers / jest.advanceTimersByTime / jest.runAllTimers), packages/clerk-js/src/ui/utils/test/mockHelpers.ts (jest.fn), packages/clerk-js/jest.setup.ts (global Jest setup using jest.fn), packages/clerk-js/src/testUtils.ts (jest.spyOn / jest.fn), packages/clerk-js/src/core/test/fixtures.ts (global.fetch = jest.fn), plus many tests using jest.useFakeTimers(), jest.mock(), jest.fn(), jest.spyOn, etc.
  • Action: replace jest.* calls with vi.* (or import { vi } from 'vitest' where needed) and convert the Jest setup to a Vitest setup (or enable a compatibility layer). Re-run the verification script until no lingering jest.* usages remain.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserProfile/__tests__/utils.spec.ts
around line 2, the file correctly imports Vitest but the repository still
contains many direct Jest API usages that must be migrated; replace all jest.*
usages with vi.* (or add import { vi } from 'vitest' where needed) across the
listed files (packages/clerk-js/src/ui/utils/test/runFakeTimers.ts,
packages/clerk-js/src/ui/utils/test/mockHelpers.ts,
packages/clerk-js/jest.setup.ts, packages/clerk-js/src/testUtils.ts,
packages/clerk-js/src/core/test/fixtures.ts and all __tests__), convert global
Jest setup into a Vitest setup file (or enable a compatibility shim), change
jest.useFakeTimers/advanceTimers/runAllTimers →
vi.useFakeTimers/advanceTimersToNextTimer/runAllTimers as appropriate, replace
jest.fn/jest.spyOn/jest.mock → vi.fn/vi.spyOn/vi.mock, and re-run the
verification script until no jest.* references remain.


import { EmailAddress, PhoneNumber } from '../../../../core/resources';
import { sortIdentificationBasedOnVerification } from '../utils';
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';
import { renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { useFormattedPhoneNumber } from '../useFormattedPhoneNumber';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createInternalTheme } from '../createInternalTheme';

describe('createInternalTheme', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore getComputedStyle after each test to avoid global pollution

window.getComputedStyle is mocked but never restored; reassigning global.window to the same originalWindow doesn’t undo the mock. This can leak into other tests. Prefer a spy + restore.

Apply:

-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@
- const originalWindow = window;- const mockGetComputedStyle = vi.fn();+ const mockGetComputedStyle = vi.fn();
@@
- beforeEach(() => {- // Mock window.getComputedStyle- mockGetComputedStyle.mockReset();- Object.defineProperty(window, 'getComputedStyle', {- value: mockGetComputedStyle,- writable: true,- });- });+ beforeEach(() => {+ mockGetComputedStyle.mockReset();+ vi.spyOn(window, 'getComputedStyle').mockImplementation(+ mockGetComputedStyle as unknown as typeof window.getComputedStyle,+ );+ });
@@
- afterEach(() => {- // Restore window- Object.defineProperty(global, 'window', {- value: originalWindow,- writable: true,- });- });+ afterEach(() => {+ vi.restoreAllMocks();+ });

Also applies to: 8-17, 19-25

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/hooks/__tests__/useDirection.spec.ts around lines 2
and also covering 8-17 and 19-25, the test suite mocks window.getComputedStyle
but never restores it causing global test pollution; replace the direct
reassignment with a spy (e.g., vi.spyOn(window, 'getComputedStyle')) and then
call mockRestore() (or .mockRestore()) in afterEach to restore the original
implementation, ensuring every test cleans up its spy/mocking in a
finally/afterEach block so other tests are unaffected.


import { useDirection } from '../useDirection';

describe('useDirection', () => {
const originalWindow = window;
const mockGetComputedStyle = jest.fn();
const mockGetComputedStyle = vi.fn();

beforeEach(() => {
// Mock window.getComputedStyle
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createCssVariables } from '../createCssVariables';
import { createVariants } from '../createVariants';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatSafeIdentifier } from '../formatSafeIdentifier';

describe('formatSafeIdentifier', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatToCompactNumber } from '../intl';

describe('formatToCompactNumber()', function () {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/true-items-burn.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Empty changeset will likely fail the Changesets check — declare a “none” bump or remove it

Use a no-release changeset so CI passes, e.g.:

-----------+---+"@clerk/clerk-js": none+---++chore(clerk-js): convert additional tests to Vitest (no release)

If the published package name differs, replace "@clerk/clerk-js" accordingly.


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"PWD: $(pwd)"echoecho"=== .changeset directory listing ==="
ls -la .changeset ||trueechoecho"=== Content of .changeset/true-items-burn.md ==="if [ -f .changeset/true-items-burn.md ];then
sed -n '1,200p' .changeset/true-items-burn.md ||trueelseecho"file not found"fiechoecho"=== Root package.json (if present) ==="if [ -f package.json ];then
sed -n '1,200p' package.json ||trueelseecho"no root package.json"fiechoecho"=== package.json files (depth 3) ==="
find . -maxdepth 3 -type f -name package.json -print -exec sed -n '1,80p' {} \;||true

Length of output: 74924


Empty changeset — add a no-release bump or remove the file (CI will fail)

.changedset/true-items-burn.md (lines 1–2) contains only frontmatter ("---" / "---"). Replace with a no-release changeset or delete the file. Example (use the correct package name if different):


"@clerk/clerk-js": none

chore(clerk-js): convert additional tests to Vitest (no release)

🤖 Prompt for AI Agents
.changeset/true-items-burn.md lines 1-2 contain only empty frontmatter which
will cause CI to fail; replace the file content with a valid no-release
changeset or delete the file. Edit the file to include a YAML frontmatter block
with the package name mapped to "none" (or the correct package name if
different) and a short summary message below, for example a minimal changeset
that sets "@your/package": none and a one-line description, or remove the file
entirely if no changeset is needed.

Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getCookieDomain } from '../../getCookieDomain';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createClientUatCookie } from '../clientUat';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getCookieDomain');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getCookieDomain');
vi.mock('../../getSecureAttribute');

describe('createClientUatCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockExpires = new Date('2024-12-31');
const mockDomain = 'test.domain';
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getCookieDomain as jest.Mock).mockReturnValue(mockDomain);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getCookieDomain as ReturnType<typeof vi.fn>).mockReturnValue(mockDomain);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -60,7 +61,7 @@ describe('createClientUatCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createClientUatCookie(mockCookieSuffix);
cookieHandler.set({
id: 'test-client',
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createSessionCookie } from '../session';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getSecureAttribute');

describe('createSessionCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockToken = 'test-token';
const mockExpires = new Date('2024-12-31');
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -52,7 +53,7 @@ describe('createSessionCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createSessionCookie(mockCookieSuffix);
cookieHandler.set(mockToken);

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Organization } from '../internal';

describe('Organization', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationDomain } from '../internal';

describe('OrganizationDomain', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationInvitation } from '../internal';

describe('OrganizationInvitation', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembership } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembershipRequest } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationSuggestion } from '../internal';

describe('OrganizationSuggestion', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { PublicUserData } from '../PublicUserData';

describe('PublicUserData', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Waitlist } from '../internal';

describe('Waitlist', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { populateCacheRemoveItem, populateCacheUpdateItem } from '../utils';

const staleInfiniteCache = [
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
import type { LoadedClerk, SignUpResource } from '@clerk/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { handleCombinedFlowTransfer, hasOptionalFields } from '../handleCombinedFlowTransfer';

// eslint-disable-next-line no-var -- Jest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: jest.Mock;
jest.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = jest.fn();
// eslint-disable-next-line no-var -- Vitest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: ReturnType<typeof vi.fn>;
vi.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = vi.fn();
return {
lazyCompleteSignUpFlow: () => {
return Promise.resolve(mockCompleteSignUpFlow);
},
};
});

const mockNavigate = jest.fn();
const mockHandleError = jest.fn();
const mockNavigate = vi.fn();
const mockHandleError = vi.fn();

describe('handleCombinedFlowTransfer', () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should call completeSignUpFlow', async () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockResolvedValue({}),
create: vi.fn().mockResolvedValue({}),
optionalFields: [],
},
},
Expand All@@ -40,7 +41,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockCompleteSignUpFlow).toHaveBeenCalled();
Expand All@@ -50,7 +51,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['phone_number'],
},
},
Expand All@@ -65,7 +66,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).not.toHaveBeenCalled();
Expand All@@ -77,7 +78,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['password'],
},
},
Expand All@@ -92,7 +93,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: true,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -104,7 +105,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: [],
},
},
Expand All@@ -119,7 +120,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -131,7 +132,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['first_name'],
},
},
Expand All@@ -146,7 +147,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { SignInResource } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import type { FormControlState } from '@/ui/utils/useFormControl';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { Attribute } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import { determineActiveFields, determineRequiredIdentifier, getInitialActiveIdentifier } from '../signUpFormHelpers';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { VerificationJSON } from '@clerk/types';
import { describe, expect, it } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

LGTM: Explicit Vitest imports are correct for this migration

Using import { describe, expect, it } from 'vitest' is consistent with Vitest and avoids relying on globals. No further changes needed here.

To ensure the broader migration is clean, run:


🏁 Script executed:

#!/bin/bashset -euo pipefail
# 1) Flag any lingering direct Jest API usages under clerk-js
rg -nP -C2 '\bjest\.(fn|spyOn|mock|useFakeTimers|advanceTimersByTime|runAllTimers|resetAllMocks|clearAllMocks)\b' packages/clerk-js ||true# 2) Identify tests that call describe/it/test/expect without importing vitest (if globals are disabled)# Adjust the glob if your tests live elsewhere.
fd -t f -a --glob 'packages/clerk-js/**/__tests__/**/*.{ts,tsx,js,jsx}' \
|whileread -r f;doif rg -nP '^\s*(describe|it|test|expect)\s*\('"$f"> /dev/null;thenif! rg -nP "from\s+['\"]vitest['\"]""$f"> /dev/null;thenecho"Potential missing vitest import: $f"fifidone

Length of output: 120643


LGTM for this file's explicit Vitest imports — migration is incomplete; critical jest -> vi changes required

The import in packages/clerk-js/src/ui/components/UserProfile/tests/utils.spec.ts is correct, but the scan found many direct Jest API usages that must be migrated to Vitest (vi) or handled via a compatibility shim.

  • Key offenders (examples): packages/clerk-js/src/ui/utils/test/runFakeTimers.ts (jest.useFakeTimers / jest.advanceTimersByTime / jest.runAllTimers), packages/clerk-js/src/ui/utils/test/mockHelpers.ts (jest.fn), packages/clerk-js/jest.setup.ts (global Jest setup using jest.fn), packages/clerk-js/src/testUtils.ts (jest.spyOn / jest.fn), packages/clerk-js/src/core/test/fixtures.ts (global.fetch = jest.fn), plus many tests using jest.useFakeTimers(), jest.mock(), jest.fn(), jest.spyOn, etc.
  • Action: replace jest.* calls with vi.* (or import { vi } from 'vitest' where needed) and convert the Jest setup to a Vitest setup (or enable a compatibility layer). Re-run the verification script until no lingering jest.* usages remain.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserProfile/__tests__/utils.spec.ts
around line 2, the file correctly imports Vitest but the repository still
contains many direct Jest API usages that must be migrated; replace all jest.*
usages with vi.* (or add import { vi } from 'vitest' where needed) across the
listed files (packages/clerk-js/src/ui/utils/test/runFakeTimers.ts,
packages/clerk-js/src/ui/utils/test/mockHelpers.ts,
packages/clerk-js/jest.setup.ts, packages/clerk-js/src/testUtils.ts,
packages/clerk-js/src/core/test/fixtures.ts and all __tests__), convert global
Jest setup into a Vitest setup file (or enable a compatibility shim), change
jest.useFakeTimers/advanceTimers/runAllTimers →
vi.useFakeTimers/advanceTimersToNextTimer/runAllTimers as appropriate, replace
jest.fn/jest.spyOn/jest.mock → vi.fn/vi.spyOn/vi.mock, and re-run the
verification script until no jest.* references remain.


import { EmailAddress, PhoneNumber } from '../../../../core/resources';
import { sortIdentificationBasedOnVerification } from '../utils';
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';
import { renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { useFormattedPhoneNumber } from '../useFormattedPhoneNumber';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createInternalTheme } from '../createInternalTheme';

describe('createInternalTheme', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore getComputedStyle after each test to avoid global pollution

window.getComputedStyle is mocked but never restored; reassigning global.window to the same originalWindow doesn’t undo the mock. This can leak into other tests. Prefer a spy + restore.

Apply:

-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@
- const originalWindow = window;- const mockGetComputedStyle = vi.fn();+ const mockGetComputedStyle = vi.fn();
@@
- beforeEach(() => {- // Mock window.getComputedStyle- mockGetComputedStyle.mockReset();- Object.defineProperty(window, 'getComputedStyle', {- value: mockGetComputedStyle,- writable: true,- });- });+ beforeEach(() => {+ mockGetComputedStyle.mockReset();+ vi.spyOn(window, 'getComputedStyle').mockImplementation(+ mockGetComputedStyle as unknown as typeof window.getComputedStyle,+ );+ });
@@
- afterEach(() => {- // Restore window- Object.defineProperty(global, 'window', {- value: originalWindow,- writable: true,- });- });+ afterEach(() => {+ vi.restoreAllMocks();+ });

Also applies to: 8-17, 19-25

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/hooks/__tests__/useDirection.spec.ts around lines 2
and also covering 8-17 and 19-25, the test suite mocks window.getComputedStyle
but never restores it causing global test pollution; replace the direct
reassignment with a spy (e.g., vi.spyOn(window, 'getComputedStyle')) and then
call mockRestore() (or .mockRestore()) in afterEach to restore the original
implementation, ensuring every test cleans up its spy/mocking in a
finally/afterEach block so other tests are unaffected.


import { useDirection } from '../useDirection';

describe('useDirection', () => {
const originalWindow = window;
const mockGetComputedStyle = jest.fn();
const mockGetComputedStyle = vi.fn();

beforeEach(() => {
// Mock window.getComputedStyle
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createCssVariables } from '../createCssVariables';
import { createVariants } from '../createVariants';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatSafeIdentifier } from '../formatSafeIdentifier';

describe('formatSafeIdentifier', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatToCompactNumber } from '../intl';

describe('formatToCompactNumber()', function () {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/true-items-burn.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Empty changeset will likely fail the Changesets check — declare a “none” bump or remove it

Use a no-release changeset so CI passes, e.g.:

-----------+---+"@clerk/clerk-js": none+---++chore(clerk-js): convert additional tests to Vitest (no release)

If the published package name differs, replace "@clerk/clerk-js" accordingly.


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"PWD: $(pwd)"echoecho"=== .changeset directory listing ==="
ls -la .changeset ||trueechoecho"=== Content of .changeset/true-items-burn.md ==="if [ -f .changeset/true-items-burn.md ];then
sed -n '1,200p' .changeset/true-items-burn.md ||trueelseecho"file not found"fiechoecho"=== Root package.json (if present) ==="if [ -f package.json ];then
sed -n '1,200p' package.json ||trueelseecho"no root package.json"fiechoecho"=== package.json files (depth 3) ==="
find . -maxdepth 3 -type f -name package.json -print -exec sed -n '1,80p' {} \;||true

Length of output: 74924


Empty changeset — add a no-release bump or remove the file (CI will fail)

.changedset/true-items-burn.md (lines 1–2) contains only frontmatter ("---" / "---"). Replace with a no-release changeset or delete the file. Example (use the correct package name if different):


"@clerk/clerk-js": none

chore(clerk-js): convert additional tests to Vitest (no release)

🤖 Prompt for AI Agents
.changeset/true-items-burn.md lines 1-2 contain only empty frontmatter which
will cause CI to fail; replace the file content with a valid no-release
changeset or delete the file. Edit the file to include a YAML frontmatter block
with the package name mapped to "none" (or the correct package name if
different) and a short summary message below, for example a minimal changeset
that sets "@your/package": none and a one-line description, or remove the file
entirely if no changeset is needed.

Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getCookieDomain } from '../../getCookieDomain';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createClientUatCookie } from '../clientUat';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getCookieDomain');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getCookieDomain');
vi.mock('../../getSecureAttribute');

describe('createClientUatCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockExpires = new Date('2024-12-31');
const mockDomain = 'test.domain';
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getCookieDomain as jest.Mock).mockReturnValue(mockDomain);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getCookieDomain as ReturnType<typeof vi.fn>).mockReturnValue(mockDomain);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -60,7 +61,7 @@ describe('createClientUatCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createClientUatCookie(mockCookieSuffix);
cookieHandler.set({
id: 'test-client',
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createSessionCookie } from '../session';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getSecureAttribute');

describe('createSessionCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockToken = 'test-token';
const mockExpires = new Date('2024-12-31');
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -52,7 +53,7 @@ describe('createSessionCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createSessionCookie(mockCookieSuffix);
cookieHandler.set(mockToken);

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Organization } from '../internal';

describe('Organization', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationDomain } from '../internal';

describe('OrganizationDomain', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationInvitation } from '../internal';

describe('OrganizationInvitation', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembership } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembershipRequest } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationSuggestion } from '../internal';

describe('OrganizationSuggestion', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { PublicUserData } from '../PublicUserData';

describe('PublicUserData', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Waitlist } from '../internal';

describe('Waitlist', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { populateCacheRemoveItem, populateCacheUpdateItem } from '../utils';

const staleInfiniteCache = [
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
import type { LoadedClerk, SignUpResource } from '@clerk/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { handleCombinedFlowTransfer, hasOptionalFields } from '../handleCombinedFlowTransfer';

// eslint-disable-next-line no-var -- Jest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: jest.Mock;
jest.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = jest.fn();
// eslint-disable-next-line no-var -- Vitest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: ReturnType<typeof vi.fn>;
vi.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = vi.fn();
return {
lazyCompleteSignUpFlow: () => {
return Promise.resolve(mockCompleteSignUpFlow);
},
};
});

const mockNavigate = jest.fn();
const mockHandleError = jest.fn();
const mockNavigate = vi.fn();
const mockHandleError = vi.fn();

describe('handleCombinedFlowTransfer', () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should call completeSignUpFlow', async () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockResolvedValue({}),
create: vi.fn().mockResolvedValue({}),
optionalFields: [],
},
},
Expand All@@ -40,7 +41,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockCompleteSignUpFlow).toHaveBeenCalled();
Expand All@@ -50,7 +51,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['phone_number'],
},
},
Expand All@@ -65,7 +66,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).not.toHaveBeenCalled();
Expand All@@ -77,7 +78,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['password'],
},
},
Expand All@@ -92,7 +93,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: true,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -104,7 +105,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: [],
},
},
Expand All@@ -119,7 +120,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -131,7 +132,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['first_name'],
},
},
Expand All@@ -146,7 +147,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { SignInResource } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import type { FormControlState } from '@/ui/utils/useFormControl';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { Attribute } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import { determineActiveFields, determineRequiredIdentifier, getInitialActiveIdentifier } from '../signUpFormHelpers';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { VerificationJSON } from '@clerk/types';
import { describe, expect, it } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

LGTM: Explicit Vitest imports are correct for this migration

Using import { describe, expect, it } from 'vitest' is consistent with Vitest and avoids relying on globals. No further changes needed here.

To ensure the broader migration is clean, run:


🏁 Script executed:

#!/bin/bashset -euo pipefail
# 1) Flag any lingering direct Jest API usages under clerk-js
rg -nP -C2 '\bjest\.(fn|spyOn|mock|useFakeTimers|advanceTimersByTime|runAllTimers|resetAllMocks|clearAllMocks)\b' packages/clerk-js ||true# 2) Identify tests that call describe/it/test/expect without importing vitest (if globals are disabled)# Adjust the glob if your tests live elsewhere.
fd -t f -a --glob 'packages/clerk-js/**/__tests__/**/*.{ts,tsx,js,jsx}' \
|whileread -r f;doif rg -nP '^\s*(describe|it|test|expect)\s*\('"$f"> /dev/null;thenif! rg -nP "from\s+['\"]vitest['\"]""$f"> /dev/null;thenecho"Potential missing vitest import: $f"fifidone

Length of output: 120643


LGTM for this file's explicit Vitest imports — migration is incomplete; critical jest -> vi changes required

The import in packages/clerk-js/src/ui/components/UserProfile/tests/utils.spec.ts is correct, but the scan found many direct Jest API usages that must be migrated to Vitest (vi) or handled via a compatibility shim.

  • Key offenders (examples): packages/clerk-js/src/ui/utils/test/runFakeTimers.ts (jest.useFakeTimers / jest.advanceTimersByTime / jest.runAllTimers), packages/clerk-js/src/ui/utils/test/mockHelpers.ts (jest.fn), packages/clerk-js/jest.setup.ts (global Jest setup using jest.fn), packages/clerk-js/src/testUtils.ts (jest.spyOn / jest.fn), packages/clerk-js/src/core/test/fixtures.ts (global.fetch = jest.fn), plus many tests using jest.useFakeTimers(), jest.mock(), jest.fn(), jest.spyOn, etc.
  • Action: replace jest.* calls with vi.* (or import { vi } from 'vitest' where needed) and convert the Jest setup to a Vitest setup (or enable a compatibility layer). Re-run the verification script until no lingering jest.* usages remain.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserProfile/__tests__/utils.spec.ts
around line 2, the file correctly imports Vitest but the repository still
contains many direct Jest API usages that must be migrated; replace all jest.*
usages with vi.* (or add import { vi } from 'vitest' where needed) across the
listed files (packages/clerk-js/src/ui/utils/test/runFakeTimers.ts,
packages/clerk-js/src/ui/utils/test/mockHelpers.ts,
packages/clerk-js/jest.setup.ts, packages/clerk-js/src/testUtils.ts,
packages/clerk-js/src/core/test/fixtures.ts and all __tests__), convert global
Jest setup into a Vitest setup file (or enable a compatibility shim), change
jest.useFakeTimers/advanceTimers/runAllTimers →
vi.useFakeTimers/advanceTimersToNextTimer/runAllTimers as appropriate, replace
jest.fn/jest.spyOn/jest.mock → vi.fn/vi.spyOn/vi.mock, and re-run the
verification script until no jest.* references remain.


import { EmailAddress, PhoneNumber } from '../../../../core/resources';
import { sortIdentificationBasedOnVerification } from '../utils';
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';
import { renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { useFormattedPhoneNumber } from '../useFormattedPhoneNumber';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createInternalTheme } from '../createInternalTheme';

describe('createInternalTheme', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore getComputedStyle after each test to avoid global pollution

window.getComputedStyle is mocked but never restored; reassigning global.window to the same originalWindow doesn’t undo the mock. This can leak into other tests. Prefer a spy + restore.

Apply:

-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@
- const originalWindow = window;- const mockGetComputedStyle = vi.fn();+ const mockGetComputedStyle = vi.fn();
@@
- beforeEach(() => {- // Mock window.getComputedStyle- mockGetComputedStyle.mockReset();- Object.defineProperty(window, 'getComputedStyle', {- value: mockGetComputedStyle,- writable: true,- });- });+ beforeEach(() => {+ mockGetComputedStyle.mockReset();+ vi.spyOn(window, 'getComputedStyle').mockImplementation(+ mockGetComputedStyle as unknown as typeof window.getComputedStyle,+ );+ });
@@
- afterEach(() => {- // Restore window- Object.defineProperty(global, 'window', {- value: originalWindow,- writable: true,- });- });+ afterEach(() => {+ vi.restoreAllMocks();+ });

Also applies to: 8-17, 19-25

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/hooks/__tests__/useDirection.spec.ts around lines 2
and also covering 8-17 and 19-25, the test suite mocks window.getComputedStyle
but never restores it causing global test pollution; replace the direct
reassignment with a spy (e.g., vi.spyOn(window, 'getComputedStyle')) and then
call mockRestore() (or .mockRestore()) in afterEach to restore the original
implementation, ensuring every test cleans up its spy/mocking in a
finally/afterEach block so other tests are unaffected.


import { useDirection } from '../useDirection';

describe('useDirection', () => {
const originalWindow = window;
const mockGetComputedStyle = jest.fn();
const mockGetComputedStyle = vi.fn();

beforeEach(() => {
// Mock window.getComputedStyle
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createCssVariables } from '../createCssVariables';
import { createVariants } from '../createVariants';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatSafeIdentifier } from '../formatSafeIdentifier';

describe('formatSafeIdentifier', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatToCompactNumber } from '../intl';

describe('formatToCompactNumber()', function () {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/true-items-burn.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Empty changeset will likely fail the Changesets check — declare a “none” bump or remove it

Use a no-release changeset so CI passes, e.g.:

-----------+---+"@clerk/clerk-js": none+---++chore(clerk-js): convert additional tests to Vitest (no release)

If the published package name differs, replace "@clerk/clerk-js" accordingly.


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"PWD: $(pwd)"echoecho"=== .changeset directory listing ==="
ls -la .changeset ||trueechoecho"=== Content of .changeset/true-items-burn.md ==="if [ -f .changeset/true-items-burn.md ];then
sed -n '1,200p' .changeset/true-items-burn.md ||trueelseecho"file not found"fiechoecho"=== Root package.json (if present) ==="if [ -f package.json ];then
sed -n '1,200p' package.json ||trueelseecho"no root package.json"fiechoecho"=== package.json files (depth 3) ==="
find . -maxdepth 3 -type f -name package.json -print -exec sed -n '1,80p' {} \;||true

Length of output: 74924


Empty changeset — add a no-release bump or remove the file (CI will fail)

.changedset/true-items-burn.md (lines 1–2) contains only frontmatter ("---" / "---"). Replace with a no-release changeset or delete the file. Example (use the correct package name if different):


"@clerk/clerk-js": none

chore(clerk-js): convert additional tests to Vitest (no release)

🤖 Prompt for AI Agents
.changeset/true-items-burn.md lines 1-2 contain only empty frontmatter which
will cause CI to fail; replace the file content with a valid no-release
changeset or delete the file. Edit the file to include a YAML frontmatter block
with the package name mapped to "none" (or the correct package name if
different) and a short summary message below, for example a minimal changeset
that sets "@your/package": none and a one-line description, or remove the file
entirely if no changeset is needed.

Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getCookieDomain } from '../../getCookieDomain';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createClientUatCookie } from '../clientUat';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getCookieDomain');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getCookieDomain');
vi.mock('../../getSecureAttribute');

describe('createClientUatCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockExpires = new Date('2024-12-31');
const mockDomain = 'test.domain';
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getCookieDomain as jest.Mock).mockReturnValue(mockDomain);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getCookieDomain as ReturnType<typeof vi.fn>).mockReturnValue(mockDomain);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -60,7 +61,7 @@ describe('createClientUatCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createClientUatCookie(mockCookieSuffix);
cookieHandler.set({
id: 'test-client',
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createSessionCookie } from '../session';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getSecureAttribute');

describe('createSessionCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockToken = 'test-token';
const mockExpires = new Date('2024-12-31');
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -52,7 +53,7 @@ describe('createSessionCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createSessionCookie(mockCookieSuffix);
cookieHandler.set(mockToken);

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Organization } from '../internal';

describe('Organization', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationDomain } from '../internal';

describe('OrganizationDomain', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationInvitation } from '../internal';

describe('OrganizationInvitation', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembership } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembershipRequest } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationSuggestion } from '../internal';

describe('OrganizationSuggestion', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { PublicUserData } from '../PublicUserData';

describe('PublicUserData', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Waitlist } from '../internal';

describe('Waitlist', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { populateCacheRemoveItem, populateCacheUpdateItem } from '../utils';

const staleInfiniteCache = [
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
import type { LoadedClerk, SignUpResource } from '@clerk/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { handleCombinedFlowTransfer, hasOptionalFields } from '../handleCombinedFlowTransfer';

// eslint-disable-next-line no-var -- Jest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: jest.Mock;
jest.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = jest.fn();
// eslint-disable-next-line no-var -- Vitest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: ReturnType<typeof vi.fn>;
vi.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = vi.fn();
return {
lazyCompleteSignUpFlow: () => {
return Promise.resolve(mockCompleteSignUpFlow);
},
};
});

const mockNavigate = jest.fn();
const mockHandleError = jest.fn();
const mockNavigate = vi.fn();
const mockHandleError = vi.fn();

describe('handleCombinedFlowTransfer', () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should call completeSignUpFlow', async () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockResolvedValue({}),
create: vi.fn().mockResolvedValue({}),
optionalFields: [],
},
},
Expand All@@ -40,7 +41,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockCompleteSignUpFlow).toHaveBeenCalled();
Expand All@@ -50,7 +51,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['phone_number'],
},
},
Expand All@@ -65,7 +66,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).not.toHaveBeenCalled();
Expand All@@ -77,7 +78,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['password'],
},
},
Expand All@@ -92,7 +93,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: true,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -104,7 +105,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: [],
},
},
Expand All@@ -119,7 +120,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -131,7 +132,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['first_name'],
},
},
Expand All@@ -146,7 +147,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { SignInResource } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import type { FormControlState } from '@/ui/utils/useFormControl';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { Attribute } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import { determineActiveFields, determineRequiredIdentifier, getInitialActiveIdentifier } from '../signUpFormHelpers';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { VerificationJSON } from '@clerk/types';
import { describe, expect, it } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

LGTM: Explicit Vitest imports are correct for this migration

Using import { describe, expect, it } from 'vitest' is consistent with Vitest and avoids relying on globals. No further changes needed here.

To ensure the broader migration is clean, run:


🏁 Script executed:

#!/bin/bashset -euo pipefail
# 1) Flag any lingering direct Jest API usages under clerk-js
rg -nP -C2 '\bjest\.(fn|spyOn|mock|useFakeTimers|advanceTimersByTime|runAllTimers|resetAllMocks|clearAllMocks)\b' packages/clerk-js ||true# 2) Identify tests that call describe/it/test/expect without importing vitest (if globals are disabled)# Adjust the glob if your tests live elsewhere.
fd -t f -a --glob 'packages/clerk-js/**/__tests__/**/*.{ts,tsx,js,jsx}' \
|whileread -r f;doif rg -nP '^\s*(describe|it|test|expect)\s*\('"$f"> /dev/null;thenif! rg -nP "from\s+['\"]vitest['\"]""$f"> /dev/null;thenecho"Potential missing vitest import: $f"fifidone

Length of output: 120643


LGTM for this file's explicit Vitest imports — migration is incomplete; critical jest -> vi changes required

The import in packages/clerk-js/src/ui/components/UserProfile/tests/utils.spec.ts is correct, but the scan found many direct Jest API usages that must be migrated to Vitest (vi) or handled via a compatibility shim.

  • Key offenders (examples): packages/clerk-js/src/ui/utils/test/runFakeTimers.ts (jest.useFakeTimers / jest.advanceTimersByTime / jest.runAllTimers), packages/clerk-js/src/ui/utils/test/mockHelpers.ts (jest.fn), packages/clerk-js/jest.setup.ts (global Jest setup using jest.fn), packages/clerk-js/src/testUtils.ts (jest.spyOn / jest.fn), packages/clerk-js/src/core/test/fixtures.ts (global.fetch = jest.fn), plus many tests using jest.useFakeTimers(), jest.mock(), jest.fn(), jest.spyOn, etc.
  • Action: replace jest.* calls with vi.* (or import { vi } from 'vitest' where needed) and convert the Jest setup to a Vitest setup (or enable a compatibility layer). Re-run the verification script until no lingering jest.* usages remain.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserProfile/__tests__/utils.spec.ts
around line 2, the file correctly imports Vitest but the repository still
contains many direct Jest API usages that must be migrated; replace all jest.*
usages with vi.* (or add import { vi } from 'vitest' where needed) across the
listed files (packages/clerk-js/src/ui/utils/test/runFakeTimers.ts,
packages/clerk-js/src/ui/utils/test/mockHelpers.ts,
packages/clerk-js/jest.setup.ts, packages/clerk-js/src/testUtils.ts,
packages/clerk-js/src/core/test/fixtures.ts and all __tests__), convert global
Jest setup into a Vitest setup file (or enable a compatibility shim), change
jest.useFakeTimers/advanceTimers/runAllTimers →
vi.useFakeTimers/advanceTimersToNextTimer/runAllTimers as appropriate, replace
jest.fn/jest.spyOn/jest.mock → vi.fn/vi.spyOn/vi.mock, and re-run the
verification script until no jest.* references remain.


import { EmailAddress, PhoneNumber } from '../../../../core/resources';
import { sortIdentificationBasedOnVerification } from '../utils';
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';
import { renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { useFormattedPhoneNumber } from '../useFormattedPhoneNumber';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createInternalTheme } from '../createInternalTheme';

describe('createInternalTheme', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore getComputedStyle after each test to avoid global pollution

window.getComputedStyle is mocked but never restored; reassigning global.window to the same originalWindow doesn’t undo the mock. This can leak into other tests. Prefer a spy + restore.

Apply:

-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@
- const originalWindow = window;- const mockGetComputedStyle = vi.fn();+ const mockGetComputedStyle = vi.fn();
@@
- beforeEach(() => {- // Mock window.getComputedStyle- mockGetComputedStyle.mockReset();- Object.defineProperty(window, 'getComputedStyle', {- value: mockGetComputedStyle,- writable: true,- });- });+ beforeEach(() => {+ mockGetComputedStyle.mockReset();+ vi.spyOn(window, 'getComputedStyle').mockImplementation(+ mockGetComputedStyle as unknown as typeof window.getComputedStyle,+ );+ });
@@
- afterEach(() => {- // Restore window- Object.defineProperty(global, 'window', {- value: originalWindow,- writable: true,- });- });+ afterEach(() => {+ vi.restoreAllMocks();+ });

Also applies to: 8-17, 19-25

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/hooks/__tests__/useDirection.spec.ts around lines 2
and also covering 8-17 and 19-25, the test suite mocks window.getComputedStyle
but never restores it causing global test pollution; replace the direct
reassignment with a spy (e.g., vi.spyOn(window, 'getComputedStyle')) and then
call mockRestore() (or .mockRestore()) in afterEach to restore the original
implementation, ensuring every test cleans up its spy/mocking in a
finally/afterEach block so other tests are unaffected.


import { useDirection } from '../useDirection';

describe('useDirection', () => {
const originalWindow = window;
const mockGetComputedStyle = jest.fn();
const mockGetComputedStyle = vi.fn();

beforeEach(() => {
// Mock window.getComputedStyle
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createCssVariables } from '../createCssVariables';
import { createVariants } from '../createVariants';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatSafeIdentifier } from '../formatSafeIdentifier';

describe('formatSafeIdentifier', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatToCompactNumber } from '../intl';

describe('formatToCompactNumber()', function () {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/true-items-burn.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Empty changeset will likely fail the Changesets check — declare a “none” bump or remove it

Use a no-release changeset so CI passes, e.g.:

-----------+---+"@clerk/clerk-js": none+---++chore(clerk-js): convert additional tests to Vitest (no release)

If the published package name differs, replace "@clerk/clerk-js" accordingly.


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"PWD: $(pwd)"echoecho"=== .changeset directory listing ==="
ls -la .changeset ||trueechoecho"=== Content of .changeset/true-items-burn.md ==="if [ -f .changeset/true-items-burn.md ];then
sed -n '1,200p' .changeset/true-items-burn.md ||trueelseecho"file not found"fiechoecho"=== Root package.json (if present) ==="if [ -f package.json ];then
sed -n '1,200p' package.json ||trueelseecho"no root package.json"fiechoecho"=== package.json files (depth 3) ==="
find . -maxdepth 3 -type f -name package.json -print -exec sed -n '1,80p' {} \;||true

Length of output: 74924


Empty changeset — add a no-release bump or remove the file (CI will fail)

.changedset/true-items-burn.md (lines 1–2) contains only frontmatter ("---" / "---"). Replace with a no-release changeset or delete the file. Example (use the correct package name if different):


"@clerk/clerk-js": none

chore(clerk-js): convert additional tests to Vitest (no release)

🤖 Prompt for AI Agents
.changeset/true-items-burn.md lines 1-2 contain only empty frontmatter which
will cause CI to fail; replace the file content with a valid no-release
changeset or delete the file. Edit the file to include a YAML frontmatter block
with the package name mapped to "none" (or the correct package name if
different) and a short summary message below, for example a minimal changeset
that sets "@your/package": none and a one-line description, or remove the file
entirely if no changeset is needed.

Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getCookieDomain } from '../../getCookieDomain';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createClientUatCookie } from '../clientUat';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getCookieDomain');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getCookieDomain');
vi.mock('../../getSecureAttribute');

describe('createClientUatCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockExpires = new Date('2024-12-31');
const mockDomain = 'test.domain';
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getCookieDomain as jest.Mock).mockReturnValue(mockDomain);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getCookieDomain as ReturnType<typeof vi.fn>).mockReturnValue(mockDomain);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -60,7 +61,7 @@ describe('createClientUatCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createClientUatCookie(mockCookieSuffix);
cookieHandler.set({
id: 'test-client',
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
import { createCookieHandler } from '@clerk/shared/cookie';
import { addYears } from '@clerk/shared/date';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { inCrossOriginIframe } from '../../../../utils';
import { getSecureAttribute } from '../../getSecureAttribute';
import { createSessionCookie } from '../session';

jest.mock('@clerk/shared/cookie');
jest.mock('@clerk/shared/date');
jest.mock('../../../../utils');
jest.mock('../../getSecureAttribute');
vi.mock('@clerk/shared/cookie');
vi.mock('@clerk/shared/date');
vi.mock('../../../../utils');
vi.mock('../../getSecureAttribute');

describe('createSessionCookie', () => {
const mockCookieSuffix = 'test-suffix';
const mockToken = 'test-token';
const mockExpires = new Date('2024-12-31');
const mockSet = jest.fn();
const mockRemove = jest.fn();
const mockGet = jest.fn();
const mockSet = vi.fn();
const mockRemove = vi.fn();
const mockGet = vi.fn();

beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
mockGet.mockReset();
(addYears as jest.Mock).mockReturnValue(mockExpires);
(inCrossOriginIframe as jest.Mock).mockReturnValue(false);
(getSecureAttribute as jest.Mock).mockReturnValue(true);
(createCookieHandler as jest.Mock).mockImplementation(() => ({
(addYears as ReturnType<typeof vi.fn>).mockReturnValue(mockExpires);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(false);
(getSecureAttribute as ReturnType<typeof vi.fn>).mockReturnValue(true);
(createCookieHandler as ReturnType<typeof vi.fn>).mockImplementation(() => ({
set: mockSet,
remove: mockRemove,
get: mockGet,
Expand DownExpand Up@@ -52,7 +53,7 @@ describe('createSessionCookie', () => {
});

it('should set cookies with None sameSite in cross-origin context', () => {
(inCrossOriginIframe as jest.Mock).mockReturnValue(true);
(inCrossOriginIframe as ReturnType<typeof vi.fn>).mockReturnValue(true);
const cookieHandler = createSessionCookie(mockCookieSuffix);
cookieHandler.set(mockToken);

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Organization } from '../internal';

describe('Organization', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationDomain } from '../internal';

describe('OrganizationDomain', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationInvitation } from '../internal';

describe('OrganizationInvitation', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembership } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationMembershipRequest } from '../internal';

describe('OrganizationMembership', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { OrganizationSuggestion } from '../internal';

describe('OrganizationSuggestion', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { PublicUserData } from '../PublicUserData';

describe('PublicUserData', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { Waitlist } from '../internal';

describe('Waitlist', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { populateCacheRemoveItem, populateCacheUpdateItem } from '../utils';

const staleInfiniteCache = [
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
import type { LoadedClerk, SignUpResource } from '@clerk/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { handleCombinedFlowTransfer, hasOptionalFields } from '../handleCombinedFlowTransfer';

// eslint-disable-next-line no-var -- Jest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: jest.Mock;
jest.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = jest.fn();
// eslint-disable-next-line no-var -- Vitest hoists mock calls to the top of the file, so var is needed.
var mockCompleteSignUpFlow: ReturnType<typeof vi.fn>;
vi.mock('../lazy-sign-up', () => {
mockCompleteSignUpFlow = vi.fn();
return {
lazyCompleteSignUpFlow: () => {
return Promise.resolve(mockCompleteSignUpFlow);
},
};
});

const mockNavigate = jest.fn();
const mockHandleError = jest.fn();
const mockNavigate = vi.fn();
const mockHandleError = vi.fn();

describe('handleCombinedFlowTransfer', () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should call completeSignUpFlow', async () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockResolvedValue({}),
create: vi.fn().mockResolvedValue({}),
optionalFields: [],
},
},
Expand All@@ -40,7 +41,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockCompleteSignUpFlow).toHaveBeenCalled();
Expand All@@ -50,7 +51,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['phone_number'],
},
},
Expand All@@ -65,7 +66,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).not.toHaveBeenCalled();
Expand All@@ -77,7 +78,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['password'],
},
},
Expand All@@ -92,7 +93,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: true,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -104,7 +105,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: [],
},
},
Expand All@@ -119,7 +120,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand All@@ -131,7 +132,7 @@ describe('handleCombinedFlowTransfer', () => {
const mockClerk = {
client: {
signUp: {
create: jest.fn().mockImplementation((...args) => Promise.resolve(args)),
create: vi.fn().mockImplementation((...args) => Promise.resolve(args)),
optionalFields: ['first_name'],
},
},
Expand All@@ -146,7 +147,7 @@ describe('handleCombinedFlowTransfer', () => {
clerk: mockClerk as unknown as LoadedClerk,
afterSignUpUrl: 'https://test.com',
passwordEnabled: false,
navigateOnSetActive: jest.fn(),
navigateOnSetActive: vi.fn(),
});

expect(mockNavigate).toHaveBeenCalled();
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { SignInResource } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import type { FormControlState } from '@/ui/utils/useFormControl';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { Attribute } from '@clerk/types';
import { describe, expect, it } from 'vitest';

import { determineActiveFields, determineRequiredIdentifier, getInitialActiveIdentifier } from '../signUpFormHelpers';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import type { VerificationJSON } from '@clerk/types';
import { describe, expect, it } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

LGTM: Explicit Vitest imports are correct for this migration

Using import { describe, expect, it } from 'vitest' is consistent with Vitest and avoids relying on globals. No further changes needed here.

To ensure the broader migration is clean, run:


🏁 Script executed:

#!/bin/bashset -euo pipefail
# 1) Flag any lingering direct Jest API usages under clerk-js
rg -nP -C2 '\bjest\.(fn|spyOn|mock|useFakeTimers|advanceTimersByTime|runAllTimers|resetAllMocks|clearAllMocks)\b' packages/clerk-js ||true# 2) Identify tests that call describe/it/test/expect without importing vitest (if globals are disabled)# Adjust the glob if your tests live elsewhere.
fd -t f -a --glob 'packages/clerk-js/**/__tests__/**/*.{ts,tsx,js,jsx}' \
|whileread -r f;doif rg -nP '^\s*(describe|it|test|expect)\s*\('"$f"> /dev/null;thenif! rg -nP "from\s+['\"]vitest['\"]""$f"> /dev/null;thenecho"Potential missing vitest import: $f"fifidone

Length of output: 120643


LGTM for this file's explicit Vitest imports — migration is incomplete; critical jest -> vi changes required

The import in packages/clerk-js/src/ui/components/UserProfile/tests/utils.spec.ts is correct, but the scan found many direct Jest API usages that must be migrated to Vitest (vi) or handled via a compatibility shim.

  • Key offenders (examples): packages/clerk-js/src/ui/utils/test/runFakeTimers.ts (jest.useFakeTimers / jest.advanceTimersByTime / jest.runAllTimers), packages/clerk-js/src/ui/utils/test/mockHelpers.ts (jest.fn), packages/clerk-js/jest.setup.ts (global Jest setup using jest.fn), packages/clerk-js/src/testUtils.ts (jest.spyOn / jest.fn), packages/clerk-js/src/core/test/fixtures.ts (global.fetch = jest.fn), plus many tests using jest.useFakeTimers(), jest.mock(), jest.fn(), jest.spyOn, etc.
  • Action: replace jest.* calls with vi.* (or import { vi } from 'vitest' where needed) and convert the Jest setup to a Vitest setup (or enable a compatibility layer). Re-run the verification script until no lingering jest.* usages remain.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/UserProfile/__tests__/utils.spec.ts
around line 2, the file correctly imports Vitest but the repository still
contains many direct Jest API usages that must be migrated; replace all jest.*
usages with vi.* (or add import { vi } from 'vitest' where needed) across the
listed files (packages/clerk-js/src/ui/utils/test/runFakeTimers.ts,
packages/clerk-js/src/ui/utils/test/mockHelpers.ts,
packages/clerk-js/jest.setup.ts, packages/clerk-js/src/testUtils.ts,
packages/clerk-js/src/core/test/fixtures.ts and all __tests__), convert global
Jest setup into a Vitest setup file (or enable a compatibility shim), change
jest.useFakeTimers/advanceTimers/runAllTimers →
vi.useFakeTimers/advanceTimersToNextTimer/runAllTimers as appropriate, replace
jest.fn/jest.spyOn/jest.mock → vi.fn/vi.spyOn/vi.mock, and re-run the
verification script until no jest.* references remain.


import { EmailAddress, PhoneNumber } from '../../../../core/resources';
import { sortIdentificationBasedOnVerification } from '../utils';
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';
import { renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { useFormattedPhoneNumber } from '../useFormattedPhoneNumber';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createInternalTheme } from '../createInternalTheme';

describe('createInternalTheme', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
import { renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore getComputedStyle after each test to avoid global pollution

window.getComputedStyle is mocked but never restored; reassigning global.window to the same originalWindow doesn’t undo the mock. This can leak into other tests. Prefer a spy + restore.

Apply:

-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@
- const originalWindow = window;- const mockGetComputedStyle = vi.fn();+ const mockGetComputedStyle = vi.fn();
@@
- beforeEach(() => {- // Mock window.getComputedStyle- mockGetComputedStyle.mockReset();- Object.defineProperty(window, 'getComputedStyle', {- value: mockGetComputedStyle,- writable: true,- });- });+ beforeEach(() => {+ mockGetComputedStyle.mockReset();+ vi.spyOn(window, 'getComputedStyle').mockImplementation(+ mockGetComputedStyle as unknown as typeof window.getComputedStyle,+ );+ });
@@
- afterEach(() => {- // Restore window- Object.defineProperty(global, 'window', {- value: originalWindow,- writable: true,- });- });+ afterEach(() => {+ vi.restoreAllMocks();+ });

Also applies to: 8-17, 19-25

🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/hooks/__tests__/useDirection.spec.ts around lines 2
and also covering 8-17 and 19-25, the test suite mocks window.getComputedStyle
but never restores it causing global test pollution; replace the direct
reassignment with a spy (e.g., vi.spyOn(window, 'getComputedStyle')) and then
call mockRestore() (or .mockRestore()) in afterEach to restore the original
implementation, ensuring every test cleans up its spy/mocking in a
finally/afterEach block so other tests are unaffected.


import { useDirection } from '../useDirection';

describe('useDirection', () => {
const originalWindow = window;
const mockGetComputedStyle = jest.fn();
const mockGetComputedStyle = vi.fn();

beforeEach(() => {
// Mock window.getComputedStyle
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { createCssVariables } from '../createCssVariables';
import { createVariants } from '../createVariants';

Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatSafeIdentifier } from '../formatSafeIdentifier';

describe('formatSafeIdentifier', () => {
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { formatToCompactNumber } from '../intl';

describe('formatToCompactNumber()', function () {
Expand Down
Loading