diff --git a/packages/clerk-js/package.json b/packages/clerk-js/package.json index 108636bc9e9..f45f2819bb6 100644 --- a/packages/clerk-js/package.json +++ b/packages/clerk-js/package.json @@ -46,10 +46,9 @@ "lint": "eslint src", "lint:attw": "attw --pack . --profile node16 --ignore-rules named-exports", "lint:publint": "publint || true", - "test": "jest", - "test:cache:clear": "jest --clearCache --useStderr", - "test:ci": "jest --maxWorkers=70%", - "test:coverage": "jest --collectCoverage && open coverage/lcov-report/index.html", + "test": "vitest run", + "test:ci": "vitest run --reporter=verbose --coverage", + "test:coverage": "vitest run --coverage", "watch": "rspack build --config rspack.config.js --env production --watch" }, "browserslist": "last 2 years", @@ -77,12 +76,15 @@ "regenerator-runtime": "0.13.11" }, "devDependencies": { + "@emotion/jest": "^11.13.0", "@rsdoctor/rspack-plugin": "^0.4.13", "@rspack/cli": "^1.2.8", "@rspack/core": "^1.2.8", "@rspack/plugin-react-refresh": "^1.0.1", "@svgr/webpack": "^6.5.1", "@types/webpack-env": "^1.18.8", + "jsdom": "^24.1.1", + "vite-plugin-svgr": "^4.2.0", "webpack-merge": "^5.10.0" }, "peerDependencies": { diff --git a/packages/clerk-js/src/__tests__/headless.test.ts b/packages/clerk-js/src/__tests__/headless.test.ts index 949b6e30805..07f8159d246 100644 --- a/packages/clerk-js/src/__tests__/headless.test.ts +++ b/packages/clerk-js/src/__tests__/headless.test.ts @@ -1,5 +1,5 @@ /** - * @jest-environment node + * @vitest-environment node */ describe('clerk/headless', () => { diff --git a/packages/clerk-js/src/__tests__/mocks/svgMock.tsx b/packages/clerk-js/src/__tests__/mocks/svgMock.tsx new file mode 100644 index 00000000000..87ea82c61fd --- /dev/null +++ b/packages/clerk-js/src/__tests__/mocks/svgMock.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +// A simple span component to mock SVG imports in Vitest +const SvgMock = React.forwardRef>((props, ref) => ( + +)); + +export const ReactComponent = SvgMock; +export default SvgMock; diff --git a/packages/clerk-js/src/core/__tests__/clerk.redirects.test.ts b/packages/clerk-js/src/core/__tests__/clerk.redirects.test.ts index c744fffd27f..874f73c7c01 100644 --- a/packages/clerk-js/src/core/__tests__/clerk.redirects.test.ts +++ b/packages/clerk-js/src/core/__tests__/clerk.redirects.test.ts @@ -2,28 +2,29 @@ import type { DevBrowser } from '../auth/devBrowser'; import { Clerk } from '../clerk'; import type { DisplayConfig } from '../resources/internal'; import { Client, Environment } from '../resources/internal'; +import { describe, it, expect, beforeEach, afterEach, afterAll, vi } from 'vitest'; -const mockClientFetch = jest.fn(); -const mockEnvironmentFetch = jest.fn(); +const mockClientFetch = vi.fn(); +const mockEnvironmentFetch = vi.fn(); -jest.mock('../resources/Client'); -jest.mock('../resources/Environment'); +vi.mock('../resources/Client'); +vi.mock('../resources/Environment'); // Because Jest, don't ask me why... -jest.mock('../auth/devBrowser', () => ({ +vi.mock('../auth/devBrowser', () => ({ createDevBrowser: (): DevBrowser => ({ - clear: jest.fn(), - setup: jest.fn(), - getDevBrowserJWT: jest.fn(() => 'deadbeef'), - setDevBrowserJWT: jest.fn(), - removeDevBrowserJWT: jest.fn(), + clear: vi.fn(), + setup: vi.fn(), + getDevBrowserJWT: vi.fn(() => 'deadbeef'), + setDevBrowserJWT: vi.fn(), + removeDevBrowserJWT: vi.fn(), }), })); -Client.getOrCreateInstance = jest.fn().mockImplementation(() => { +Client.getOrCreateInstance = vi.fn().mockImplementation(() => { return { fetch: mockClientFetch }; }); -Environment.getInstance = jest.fn().mockImplementation(() => { +Environment.getInstance = vi.fn().mockImplementation(() => { return { fetch: mockEnvironmentFetch }; }); @@ -59,14 +60,14 @@ const developmentPublishableKey = 'pk_test_Y2xlcmsuYWJjZWYuMTIzNDUuZGV2LmxjbGNsZ const productionPublishableKey = 'pk_live_Y2xlcmsuYWJjZWYuMTIzNDUucHJvZC5sY2xjbGVyay5jb20k'; describe('Clerk singleton - Redirects', () => { - const mockNavigate = jest.fn((to: string) => Promise.resolve(to)); + const mockNavigate = vi.fn((to: string) => Promise.resolve(to)); const mockedLoadOptions = { routerPush: mockNavigate, routerReplace: mockNavigate }; let mockWindowLocation; - let mockHref: jest.Mock; + let mockHref: vi.Mock; beforeEach(() => { - mockHref = jest.fn(); + mockHref = vi.fn(); mockWindowLocation = { host: 'test.host', hostname: 'test.host', diff --git a/packages/clerk-js/src/core/__tests__/fapiClient.test.ts b/packages/clerk-js/src/core/__tests__/fapiClient.test.ts index 5e4ef4723f4..90c91b542e9 100644 --- a/packages/clerk-js/src/core/__tests__/fapiClient.test.ts +++ b/packages/clerk-js/src/core/__tests__/fapiClient.test.ts @@ -1,4 +1,5 @@ import type { InstanceType } from '@clerk/types'; +import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from 'vitest'; import { SUPPORTED_FAPI_VERSION } from '../constants'; import { createFapiClient } from '../fapiClient'; @@ -25,10 +26,10 @@ type RecursivePartial = { }; // @ts-ignore -- We don't need to fully satisfy the fetch types for the sake of this mock -global.fetch = jest.fn(() => +global.fetch = vi.fn(() => Promise.resolve>({ headers: { - get: jest.fn(() => 'sess_43'), + get: vi.fn(() => 'sess_43'), }, json: () => Promise.resolve({ foo: 42 }), }), @@ -54,7 +55,7 @@ beforeAll(() => { }); beforeEach(() => { - (global.fetch as jest.Mock).mockClear(); + (global.fetch as vi.Mock).mockClear(); }); afterAll(() => { @@ -184,10 +185,10 @@ describe('request', () => { }); it('returns array response as array', async () => { - (global.fetch as jest.Mock).mockResolvedValueOnce( + (global.fetch as vi.Mock).mockResolvedValueOnce( Promise.resolve>({ headers: { - get: jest.fn(() => 'sess_43'), + get: vi.fn(() => 'sess_43'), }, json: () => Promise.resolve([{ foo: 42 }]), }), @@ -201,7 +202,7 @@ describe('request', () => { }); it('handles the empty body on 204 response, returning null', async () => { - (global.fetch as jest.Mock).mockResolvedValueOnce( + (global.fetch as vi.Mock).mockResolvedValueOnce( Promise.resolve>({ status: 204, json: () => { diff --git a/packages/clerk-js/src/core/__tests__/tokenCache.test.ts b/packages/clerk-js/src/core/__tests__/tokenCache.test.ts index 6dcf3833ab4..3208f118d82 100644 --- a/packages/clerk-js/src/core/__tests__/tokenCache.test.ts +++ b/packages/clerk-js/src/core/__tests__/tokenCache.test.ts @@ -1,10 +1,11 @@ import type { TokenResource } from '@clerk/types'; +import { describe, it, beforeAll, afterAll, expect, vi } from 'vitest'; import { Token } from '../resources/internal'; import { SessionTokenCache } from '../tokenCache'; // This is required since abstract TS methods are undefined in Jest -jest.mock('../resources/Base', () => { +vi.mock('../resources/Base', () => { class BaseResource {} return { @@ -17,11 +18,11 @@ const jwt = describe('MemoryTokenCache', () => { beforeAll(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); describe('clear()', () => { @@ -87,7 +88,7 @@ describe('MemoryTokenCache', () => { expect(isResolved).toBe(false); // Wait tokenResolver to resolve - jest.advanceTimersByTime(100); + vi.advanceTimersByTime(100); await tokenResolver; // Cache is not empty, retrieve the resolved tokenResolver @@ -98,7 +99,7 @@ describe('MemoryTokenCache', () => { }); // Advance the timer to force the JWT expiration - jest.advanceTimersByTime(60 * 1000); + vi.advanceTimersByTime(60 * 1000); // Cache is empty, tokenResolver has been removed due to JWT expiration expect(cache.get(key)).toBeUndefined(); @@ -125,11 +126,11 @@ describe('MemoryTokenCache', () => { expect(cache.get(key)).toMatchObject(key); // 44s since token created - jest.advanceTimersByTime(45 * 1000); + vi.advanceTimersByTime(45 * 1000); expect(cache.get(key)).toMatchObject(key); // 46s since token created - jest.advanceTimersByTime(1 * 1000); + vi.advanceTimersByTime(1 * 1000); expect(cache.get(key)).toBeUndefined(); }); @@ -150,15 +151,15 @@ describe('MemoryTokenCache', () => { expect(cache.get(key)).toMatchObject(key); // 45s since token created - jest.advanceTimersByTime(45 * 1000); + vi.advanceTimersByTime(45 * 1000); expect(cache.get(key, 0)).toMatchObject(key); // 54s since token created - jest.advanceTimersByTime(9 * 1000); + vi.advanceTimersByTime(9 * 1000); expect(cache.get(key, 0)).toMatchObject(key); // 55s since token created - jest.advanceTimersByTime(1 * 1000); + vi.advanceTimersByTime(1 * 1000); expect(cache.get(key, 0)).toBeUndefined(); }); }); diff --git a/packages/clerk-js/src/core/auth/__tests__/cookieSuffix.test.ts b/packages/clerk-js/src/core/auth/__tests__/cookieSuffix.test.ts index 1adb90dfbbb..c50eda73114 100644 --- a/packages/clerk-js/src/core/auth/__tests__/cookieSuffix.test.ts +++ b/packages/clerk-js/src/core/auth/__tests__/cookieSuffix.test.ts @@ -1,8 +1,10 @@ -jest.mock('@clerk/shared/keys', () => { - return { getCookieSuffix: jest.fn() }; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; + +vi.mock('@clerk/shared/keys', () => { + return { getCookieSuffix: vi.fn() }; }); -jest.mock('@clerk/shared/logger', () => { - return { logger: { logOnce: jest.fn() } }; +vi.mock('@clerk/shared/logger', () => { + return { logger: { logOnce: vi.fn() } }; }); import { getCookieSuffix as getSharedCookieSuffix } from '@clerk/shared/keys'; import { logger } from '@clerk/shared/logger'; @@ -11,12 +13,12 @@ import { getCookieSuffix } from '../cookieSuffix'; describe('getCookieSuffix', () => { beforeEach(() => { - (getSharedCookieSuffix as jest.Mock).mockRejectedValue(new Error('mocked error for insecure context')); + (getSharedCookieSuffix as vi.Mock).mockRejectedValue(new Error('mocked error for insecure context')); }); afterEach(() => { - (getSharedCookieSuffix as jest.Mock).mockReset(); - (logger.logOnce as jest.Mock).mockReset(); + (getSharedCookieSuffix as vi.Mock).mockReset(); + (logger.logOnce as vi.Mock).mockReset(); }); describe('getCookieSuffix(publishableKey, subtle?)', () => { diff --git a/packages/clerk-js/src/core/auth/__tests__/devBrowser.test.ts b/packages/clerk-js/src/core/auth/__tests__/devBrowser.test.ts index 787824c65e2..26fbe011864 100644 --- a/packages/clerk-js/src/core/auth/__tests__/devBrowser.test.ts +++ b/packages/clerk-js/src/core/auth/__tests__/devBrowser.test.ts @@ -1,3 +1,5 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import type { FapiClient } from '../../fapiClient'; import { createDevBrowser } from '../devBrowser'; @@ -8,7 +10,7 @@ type RecursivePartial = { describe('Thrown errors', () => { beforeEach(() => { // @ts-ignore - global.fetch = jest.fn(() => + global.fetch = vi.fn(() => Promise.resolve>({ ok: false, json: () => @@ -29,17 +31,17 @@ describe('Thrown errors', () => { afterEach(() => { // @ts-ignore - global.fetch?.mockClear(); + vi.mocked(global.fetch)?.mockClear(); }); // Note: The test runs without any initial or mocked values on __clerk_db_jwt cookies. // It is expected to modify the test accordingly if cookies are mocked for future extra testing. it('throws any FAPI errors during dev browser creation', async () => { - const mockCreateFapiClient = jest.fn().mockImplementation(() => { + const mockCreateFapiClient = vi.fn().mockImplementation(() => { return { - buildUrl: jest.fn(() => 'https://white-koala-42.clerk.accounts.dev/dev_browser'), - onAfterResponse: jest.fn(), - onBeforeRequest: jest.fn(), + buildUrl: vi.fn(() => 'https://white-koala-42.clerk.accounts.dev/dev_browser'), + onAfterResponse: vi.fn(), + onBeforeRequest: vi.fn(), }; }); diff --git a/packages/clerk-js/src/core/auth/__tests__/getCookieDomain.test.ts b/packages/clerk-js/src/core/auth/__tests__/getCookieDomain.test.ts index 5f2b335f22e..d12f504cfb3 100644 --- a/packages/clerk-js/src/core/auth/__tests__/getCookieDomain.test.ts +++ b/packages/clerk-js/src/core/auth/__tests__/getCookieDomain.test.ts @@ -1,3 +1,5 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import type { getCookieDomain as _getCookieDomain } from '../getCookieDomain'; type CookieHandler = NonNullable[1]>; @@ -6,7 +8,7 @@ describe('getCookieDomain', () => { let getCookieDomain: typeof _getCookieDomain; beforeEach(async () => { // We're dynamically importing getCookieDomain here to reset the module-level cache - jest.resetModules(); + vi.resetModules(); getCookieDomain = await import('../getCookieDomain').then(m => m.getCookieDomain); }); @@ -20,14 +22,14 @@ describe('getCookieDomain', () => { // assume that the Public Suffix List is correctly handled by the browser. const hostname = 'app.fr.hosting.co.uk'; const handler: CookieHandler = { - get: jest + get: vi .fn() .mockReturnValueOnce(undefined) .mockReturnValueOnce(undefined) .mockReturnValueOnce(undefined) .mockReturnValueOnce('1'), - set: jest.fn().mockReturnValue(undefined), - remove: jest.fn().mockReturnValue(undefined), + set: vi.fn().mockReturnValue(undefined), + remove: vi.fn().mockReturnValue(undefined), }; const result = getCookieDomain(hostname, handler); expect(result).toBe(hostname); @@ -53,9 +55,9 @@ describe('getCookieDomain', () => { it('returns undefined if the domain could not be determined', () => { const handler: CookieHandler = { - get: jest.fn().mockReturnValue(undefined), - set: jest.fn().mockReturnValue(undefined), - remove: jest.fn().mockReturnValue(undefined), + get: vi.fn().mockReturnValue(undefined), + set: vi.fn().mockReturnValue(undefined), + remove: vi.fn().mockReturnValue(undefined), }; const hostname = 'app.hello.co.uk'; const result = getCookieDomain(hostname, handler); @@ -65,9 +67,9 @@ describe('getCookieDomain', () => { it('uses cached value if there is one', () => { const hostname = 'clerk.com'; const handler: CookieHandler = { - get: jest.fn().mockReturnValue('1'), - set: jest.fn().mockReturnValue(undefined), - remove: jest.fn().mockReturnValue(undefined), + get: vi.fn().mockReturnValue('1'), + set: vi.fn().mockReturnValue(undefined), + remove: vi.fn().mockReturnValue(undefined), }; expect(getCookieDomain(hostname, handler)).toBe(hostname); expect(getCookieDomain(hostname, handler)).toBe(hostname); diff --git a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts index 36b2b699de6..97f9b1eff0c 100644 --- a/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts +++ b/packages/clerk-js/src/core/auth/__tests__/getSecureAttribute.test.ts @@ -1,10 +1,12 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import { getSecureAttribute } from '../getSecureAttribute'; describe('getSecureAttribute', () => { let windowSpy: any; beforeEach(() => { - windowSpy = jest.spyOn(window, 'window', 'get'); + windowSpy = vi.spyOn(window, 'window', 'get'); }); afterEach(() => { diff --git a/packages/clerk-js/src/core/fraudProtection.test.ts b/packages/clerk-js/src/core/fraudProtection.test.ts index 98118289571..39600674717 100644 --- a/packages/clerk-js/src/core/fraudProtection.test.ts +++ b/packages/clerk-js/src/core/fraudProtection.test.ts @@ -1,13 +1,14 @@ import { FraudProtection } from './fraudProtection'; import type { Clerk, Client } from './resources/internal'; import { ClerkAPIResponseError } from './resources/internal'; +import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; describe('FraudProtectionService', () => { let sut: FraudProtection; let mockClerk: Clerk; let mockClient: typeof Client; let solveCaptcha: any; - let mockManagedInModal: jest.Mock; + let mockManagedInModal: Mock; function MockCaptchaChallenge() { // @ts-ignore - we don't need to implement the entire class @@ -22,14 +23,14 @@ describe('FraudProtectionService', () => { }; beforeEach(() => { - mockManagedInModal = jest.fn().mockResolvedValue( + mockManagedInModal = vi.fn().mockResolvedValue( new Promise(r => { solveCaptcha = r; }), ); const mockClientInstance = { - __internal_sendCaptchaToken: jest.fn().mockResolvedValue({}), + __internal_sendCaptchaToken: vi.fn().mockResolvedValue({}), }; mockClient = { @@ -45,7 +46,7 @@ describe('FraudProtectionService', () => { }); it('does not handle requests that did not throw', async () => { - const fn1 = jest.fn().mockResolvedValue('result'); + const fn1 = vi.fn().mockResolvedValue('result'); const fn1res = sut.execute(mockClerk, fn1); @@ -64,17 +65,17 @@ describe('FraudProtectionService', () => { data: [{ code: 'no-idea' } as any], status: 401, }); - const fn1 = jest.fn().mockRejectedValueOnce(unrelatedError); + const fn1 = vi.fn().mockRejectedValueOnce(unrelatedError); const fn1res = sut.execute(mockClerk, fn1); - expect(fn1res).rejects.toEqual(unrelatedError); + await expect(fn1res).rejects.toEqual(unrelatedError); expect(mockManagedInModal).toHaveBeenCalledTimes(0); expect(mockClient.getOrCreateInstance().__internal_sendCaptchaToken).toHaveBeenCalledTimes(0); expect(fn1).toHaveBeenCalledTimes(1); }); it('handles parallel requests that began at the same time by handling any requests that returned requires_captcha', async () => { - const fn1 = jest.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValueOnce('result1'); - const fn2 = jest.fn().mockResolvedValue('result2'); + const fn1 = vi.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValueOnce('result1'); + const fn2 = vi.fn().mockResolvedValue('result2'); const fn1res = sut.execute(mockClerk, fn1); const fn2res = sut.execute(mockClerk, fn2); @@ -93,8 +94,8 @@ describe('FraudProtectionService', () => { }); it('handles parallel requests that returned 401 requires_captcha', async () => { - const fn1 = jest.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValueOnce('result1'); - const fn2 = jest.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValueOnce('result2'); + const fn1 = vi.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValueOnce('result1'); + const fn2 = vi.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValueOnce('result2'); const fn1res = sut.execute(mockClerk, fn1); const fn2res = sut.execute(mockClerk, fn2); @@ -115,8 +116,8 @@ describe('FraudProtectionService', () => { }); it('handles requests that were made in close succession by blocking all other requests if the first returns requires_captcha', async () => { - const fn1 = jest.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValueOnce('result1'); - const fn2 = jest.fn().mockResolvedValue('result2'); + const fn1 = vi.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValueOnce('result1'); + const fn2 = vi.fn().mockResolvedValue('result2'); // Start the first request const fn1res = sut.execute(mockClerk, fn1); @@ -147,10 +148,10 @@ describe('FraudProtectionService', () => { }); // both with fail in parallel but fn2 will temporarily be blocked from retrying - const fn1 = jest.fn().mockRejectedValueOnce(createCaptchaError()).mockRejectedValueOnce(unrelatedError); - const fn2 = jest.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValue('result2'); + const fn1 = vi.fn().mockRejectedValueOnce(createCaptchaError()).mockRejectedValueOnce(unrelatedError); + const fn2 = vi.fn().mockRejectedValueOnce(createCaptchaError()).mockResolvedValue('result2'); // fn3 will be blocked until the captcha is solved - const fn3 = jest.fn().mockResolvedValue('result3'); + const fn3 = vi.fn().mockResolvedValue('result3'); const fn1res = sut.execute(mockClerk, fn1); const fn2res = sut.execute(mockClerk, fn2); @@ -163,7 +164,7 @@ describe('FraudProtectionService', () => { solveCaptcha(); // fn1 rejects - expect(fn1res).rejects.toEqual(unrelatedError); + await expect(fn1res).rejects.toEqual(unrelatedError); // but the other requests will be unblocked and retried await Promise.all([fn2res, fn3res]); diff --git a/packages/clerk-js/src/core/resources/__tests__/AuthConfig.test.ts b/packages/clerk-js/src/core/resources/__tests__/AuthConfig.test.ts index 95a50f77110..3ba8adf19be 100644 --- a/packages/clerk-js/src/core/resources/__tests__/AuthConfig.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/AuthConfig.test.ts @@ -1,8 +1,9 @@ import { unixEpochToDate } from '../../../utils/date'; import { AuthConfig } from '../AuthConfig'; +import { describe, expect, it, vi } from 'vitest'; -jest.mock('../../../utils/date', () => ({ - unixEpochToDate: jest.fn(timestamp => new Date(timestamp)), +vi.mock('../../../utils/date', () => ({ + unixEpochToDate: vi.fn(timestamp => new Date(timestamp)), })); describe('AuthConfig', () => { diff --git a/packages/clerk-js/src/core/resources/__tests__/Base.test.ts b/packages/clerk-js/src/core/resources/__tests__/Base.test.ts index bf7f8d6c57f..0f65eb76f3f 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Base.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Base.test.ts @@ -1,4 +1,5 @@ import { BaseResource } from '../internal'; +import { describe, expect, it, vi } from 'vitest'; class TestResource extends BaseResource { constructor() { @@ -20,7 +21,7 @@ describe('BaseResource', () => { // @ts-expect-error - We're not about to mock the entire FapiClient getFapiClient: () => { return { - request: jest.fn().mockResolvedValue({ + request: vi.fn().mockResolvedValue({ payload: {}, status: 429, statusText: 'Too Many Requests', @@ -28,7 +29,7 @@ describe('BaseResource', () => { }), }; }, - __internal_setCountry: jest.fn(), + __internal_setCountry: vi.fn(), }; const resource = new TestResource(); const errResponse = await resource.fetch().catch(err => err); @@ -41,7 +42,7 @@ describe('BaseResource', () => { // @ts-expect-error - We're not about to mock the entire FapiClient getFapiClient: () => { return { - request: jest.fn().mockResolvedValue({ + request: vi.fn().mockResolvedValue({ payload: {}, status: 429, statusText: 'Too Many Requests', @@ -49,7 +50,7 @@ describe('BaseResource', () => { }), }; }, - __internal_setCountry: jest.fn(), + __internal_setCountry: vi.fn(), }; const resource = new TestResource(); const errResponse = await resource.fetch().catch(err => err); diff --git a/packages/clerk-js/src/core/resources/__tests__/Client.test.ts b/packages/clerk-js/src/core/resources/__tests__/Client.test.ts index 434f09c24c4..8a4eb2432a2 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Client.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Client.test.ts @@ -1,4 +1,5 @@ import type { ClientJSON, ClientJSONSnapshot } from '@clerk/types'; +import { describe, expect, it, vi } from 'vitest'; import { createSession, createSignIn, createSignUp, createUser } from '../../test/fixtures'; import { BaseResource, Client } from '../internal'; @@ -16,12 +17,12 @@ describe('Client Singleton', () => { sign_in: createSignIn({ id: 'test_sign_in_id' }, user), sign_up: createSignUp({ id: 'test_sign_up_id' }), // This is only for testing purposes, this will never happen sessions: [session], - created_at: jest.now() - 1000, - updated_at: jest.now(), + created_at: Date.now() - 1000, + updated_at: Date.now(), } as any; // @ts-expect-error This is a private method that we are mocking - BaseResource._baseFetch = jest.fn(); + BaseResource._baseFetch = vi.fn(); const client = Client.getOrCreateInstance().fromJSON(clientObjectJSON); await client.__internal_sendCaptchaToken({ captcha_token: 'test_captcha_token' }); @@ -46,12 +47,12 @@ describe('Client Singleton', () => { sign_in: createSignIn({ id: 'test_sign_in_id' }, user), sign_up: createSignUp({ id: 'test_sign_up_id' }), // This is only for testing purposes, this will never happen sessions: [session], - created_at: jest.now() - 1000, - updated_at: jest.now(), + created_at: Date.now() - 1000, + updated_at: Date.now(), } as any; // @ts-expect-error This is a private method that we are mocking - BaseResource._baseFetch = jest.fn().mockResolvedValueOnce(Promise.resolve(null)); + BaseResource._baseFetch = vi.fn().mockResolvedValueOnce(Promise.resolve(null)); const client = Client.getOrCreateInstance().fromJSON(clientObjectJSON); await client.__internal_sendCaptchaToken({ captcha_token: 'test_captcha_token' }); @@ -78,14 +79,14 @@ describe('Client Singleton', () => { sign_in: createSignIn({ id: 'test_sign_in_id' }, user), sign_up: createSignUp({ id: 'test_sign_up_id' }), // This is only for testing purposes, this will never happen sessions: [session], - created_at: jest.now() - 1000, - updated_at: jest.now(), + created_at: Date.now() - 1000, + updated_at: Date.now(), }; const destroyedSession = createSession( { id: 'test_session_id', - abandon_at: jest.now(), + abandon_at: Date.now(), status: 'ended', last_active_token: undefined, }, @@ -99,12 +100,12 @@ describe('Client Singleton', () => { sign_in: null, sign_up: null, sessions: [destroyedSession], - created_at: jest.now() - 1000, - updated_at: jest.now(), + created_at: Date.now() - 1000, + updated_at: Date.now(), }; // @ts-expect-error This is a private method that we are mocking - BaseResource._fetch = jest.fn().mockReturnValue( + BaseResource._fetch = vi.fn().mockReturnValue( Promise.resolve({ client: null, response: clientObjectDeletedJSON, diff --git a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts index 4fcb37f622d..7bf469c9e6d 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts @@ -1,4 +1,5 @@ import type { EnvironmentJSONSnapshot } from '@clerk/types'; +import { describe, expect, it } from 'vitest'; import { Environment } from '../internal'; diff --git a/packages/clerk-js/src/core/resources/__tests__/ExternalAccount.test.ts b/packages/clerk-js/src/core/resources/__tests__/ExternalAccount.test.ts index c61986913bb..d19bd96cf33 100644 --- a/packages/clerk-js/src/core/resources/__tests__/ExternalAccount.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/ExternalAccount.test.ts @@ -1,4 +1,5 @@ import { BaseResource, ExternalAccount } from '../internal'; +import { describe, expect, it, vi } from 'vitest'; describe('External account', () => { it('reauthorize', async () => { @@ -10,7 +11,7 @@ describe('External account', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: externalAccountJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: externalAccountJSON })); const externalAccount = new ExternalAccount({ id: targetId }, '/me/external_accounts'); await externalAccount.reauthorize({ additionalScopes: ['read', 'write'], redirectUrl: 'https://test.com' }); @@ -36,7 +37,7 @@ describe('External account', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: deletedObjectJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: deletedObjectJSON })); const externalAccount = new ExternalAccount({ id: targetId }, '/me/external_accounts'); await externalAccount.destroy(); diff --git a/packages/clerk-js/src/core/resources/__tests__/Image.test.ts b/packages/clerk-js/src/core/resources/__tests__/Image.test.ts index c5118f68309..0047e863e63 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Image.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Image.test.ts @@ -1,5 +1,6 @@ import { Image } from '../Image'; import { BaseResource } from '../internal'; +import { describe, expect, it, vi } from 'vitest'; describe('Image', () => { it('.create returns the newly created image', async () => { @@ -10,7 +11,7 @@ describe('Image', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue( + BaseResource._fetch = vi.fn().mockReturnValue( Promise.resolve({ client: {}, response: mockResponse, diff --git a/packages/clerk-js/src/core/resources/__tests__/Session.test.ts b/packages/clerk-js/src/core/resources/__tests__/Session.test.ts index d7b42229651..dd1a93f8f6f 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Session.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Session.test.ts @@ -1,4 +1,5 @@ import type { InstanceType, OrganizationJSON, SessionJSON } from '@clerk/types'; +import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { eventBus } from '../../events'; import { createFapiClient } from '../../fapiClient'; @@ -23,7 +24,7 @@ describe('Session', () => { let dispatchSpy; beforeEach(() => { - dispatchSpy = jest.spyOn(eventBus, 'emit'); + dispatchSpy = vi.spyOn(eventBus, 'emit'); BaseResource.clerk = clerkMock() as any; }); @@ -169,7 +170,7 @@ describe('Session', () => { writable: true, value: false, }); - warnSpy = jest.spyOn(console, 'warn').mockReturnValue(); + warnSpy = vi.spyOn(console, 'warn').mockReturnValue(); }); afterEach(() => { @@ -221,7 +222,7 @@ describe('Session', () => { await session.getToken(); - expect((BaseResource.fapiClient.request as jest.Mock).mock.calls[0][0]).toMatchObject({ + expect((BaseResource.fapiClient.request as Mock).mock.calls[0][0]).toMatchObject({ body: { organizationId: 'newActiveOrganization' }, }); }); diff --git a/packages/clerk-js/src/core/resources/__tests__/Token.test.ts b/packages/clerk-js/src/core/resources/__tests__/Token.test.ts index 94e5e473354..ccf543e3211 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Token.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Token.test.ts @@ -1,4 +1,5 @@ import type { InstanceType } from '@clerk/types'; +import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { SUPPORTED_FAPI_VERSION } from '../../constants'; import { createFapiClient } from '../../fapiClient'; @@ -17,7 +18,7 @@ const baseFapiClientOptions = { describe('Token', () => { describe('create', () => { afterEach(() => { - (global.fetch as jest.Mock)?.mockClear(); + (global.fetch as Mock)?.mockClear(); BaseResource.clerk = null as any; }); @@ -49,7 +50,7 @@ describe('Token', () => { writable: true, value: false, }); - warnSpy = jest.spyOn(console, 'warn').mockReturnValue(); + warnSpy = vi.spyOn(console, 'warn').mockReturnValue(); }); afterEach(() => { diff --git a/packages/clerk-js/src/core/resources/__tests__/User.test.ts b/packages/clerk-js/src/core/resources/__tests__/User.test.ts index bfec0f83123..04eda1aa6d4 100644 --- a/packages/clerk-js/src/core/resources/__tests__/User.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/User.test.ts @@ -1,4 +1,5 @@ import type { UserJSON } from '@clerk/types'; +import { describe, expect, it, vi } from 'vitest'; import { BaseResource } from '../internal'; import { User } from '../User'; @@ -14,7 +15,7 @@ describe('User', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: externalAccountJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: externalAccountJSON })); const user = new User({ email_addresses: [], @@ -49,7 +50,7 @@ describe('User', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON })); const user = new User({ email_addresses: [], @@ -148,7 +149,7 @@ describe('User', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: totpJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: totpJSON })); const user = new User({ email_addresses: [], @@ -176,7 +177,7 @@ describe('User', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: totpJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: totpJSON })); const user = new User({ email_addresses: [], @@ -203,7 +204,7 @@ describe('User', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: deletedObjectJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: deletedObjectJSON })); const user = new User({ email_addresses: [], @@ -229,7 +230,7 @@ describe('User', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: backupCodeJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: backupCodeJSON })); const user = new User({ email_addresses: [], @@ -270,7 +271,7 @@ describe('User', () => { it('.updatePassword triggers a request to change password', async () => { // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: {} })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: {} })); const user = new User({} as unknown as UserJSON); const params = { @@ -289,7 +290,7 @@ describe('User', () => { it('.removePassword triggers a request to remove password', async () => { // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: {} })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: {} })); const user = new User({} as unknown as UserJSON); const params = { diff --git a/packages/clerk-js/src/core/resources/__tests__/UserSettings.test.ts b/packages/clerk-js/src/core/resources/__tests__/UserSettings.test.ts index 94ae59e76bf..3a623a1bebb 100644 --- a/packages/clerk-js/src/core/resources/__tests__/UserSettings.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/UserSettings.test.ts @@ -1,4 +1,5 @@ import type { UserSettingsJSON } from '@clerk/types'; +import { describe, expect, it } from 'vitest'; import { UserSettings } from '../internal'; diff --git a/packages/clerk-js/src/core/resources/__tests__/Web3Wallet.test.ts b/packages/clerk-js/src/core/resources/__tests__/Web3Wallet.test.ts index 0bb32228eb5..9848dbe4561 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Web3Wallet.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Web3Wallet.test.ts @@ -1,4 +1,5 @@ import type { Web3WalletJSON } from '@clerk/types'; +import { describe, expect, it, vi } from 'vitest'; import { BaseResource, Web3Wallet } from '../internal'; @@ -10,7 +11,7 @@ describe('Web3 wallet', () => { } as Web3WalletJSON; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON })); const web3Wallet = new Web3Wallet(web3WalletJSON, '/me/web3_wallets'); await web3Wallet.create(); @@ -33,7 +34,7 @@ describe('Web3 wallet', () => { } as Web3WalletJSON; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON })); const web3Wallet = new Web3Wallet(web3WalletJSON, '/me/web3_wallets'); await web3Wallet.prepareVerification({ strategy: 'web3_metamask_signature' }); @@ -56,7 +57,7 @@ describe('Web3 wallet', () => { } as Web3WalletJSON; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON })); const web3Wallet = new Web3Wallet(web3WalletJSON, '/me/web3_wallets'); await web3Wallet.attemptVerification({ signature: 'mock-signature' }); @@ -81,7 +82,7 @@ describe('Web3 wallet', () => { }; // @ts-ignore - BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: deletedObjectJSON })); + BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: deletedObjectJSON })); const web3Wallet = new Web3Wallet({ id: targetId }, '/me/web3_wallets'); await web3Wallet.destroy(); diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Client.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Client.test.ts.snap index e7578beaa23..3d46ccda27e 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Client.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Client.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Client Singleton __internal_toSnapshot() 1`] = ` +exports[`Client Singleton > __internal_toSnapshot() 1`] = ` { "captcha_bypass": false, "cookie_expires_at": null, @@ -204,7 +204,7 @@ exports[`Client Singleton __internal_toSnapshot() 1`] = ` } `; -exports[`Client Singleton has the same initial properties 1`] = ` +exports[`Client Singleton > has the same initial properties 1`] = ` Client { "captchaBypass": false, "cookieExpiresAt": null, diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap index 1bfc20011b4..b80831b1354 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap @@ -1,4 +1,794 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Environment > __internal_toSnapshot() 1`] = ` +{ + "auth_config": { + "claimed_at": null, + "id": "", + "object": "auth_config", + "reverification": true, + "single_session_mode": true, + }, + "commerce_settings": { + "billing": { + "enabled": false, + "has_paid_org_plans": false, + "has_paid_user_plans": false, + "stripe_publishable_key": "", + }, + }, + "display_config": { + "after_create_organization_url": "", + "after_join_waitlist_url": "", + "after_leave_organization_url": "", + "after_sign_in_url": "", + "after_sign_out_all_url": "", + "after_sign_out_one_url": "", + "after_sign_up_url": "", + "after_switch_session_url": "", + "application_name": "", + "branded": true, + "captcha_heartbeat": false, + "captcha_heartbeat_interval_ms": undefined, + "captcha_oauth_bypass": [], + "captcha_provider": "turnstile", + "captcha_public_key": null, + "captcha_public_key_invisible": null, + "captcha_widget_type": null, + "clerk_js_version": "5", + "create_organization_url": "", + "favicon_image_url": "", + "google_one_tap_client_id": undefined, + "home_url": "", + "id": "display_config_DUMMY_ID", + "instance_environment_type": "development", + "logo_image_url": "", + "object": "display_config", + "organization_profile_url": "", + "preferred_sign_in_strategy": "password", + "privacy_policy_url": "", + "show_devmode_warning": true, + "sign_in_url": "", + "sign_up_url": "", + "support_email": "", + "terms_url": "", + "theme": { + "accounts": { + "background_color": "#ffffff", + }, + "buttons": { + "font_color": "#ffffff", + "font_family": "sans-serif", + "font_weight": "600", + }, + "general": { + "background_color": "#ffffff", + "border_radius": "0.5em", + "box_shadow": "0 2px 8px rgba(0, 0, 0, 0.2)", + "color": "#6c47ff", + "font_color": "#151515", + "font_family": "sans-serif", + "label_font_weight": "600", + "padding": "1em", + }, + }, + "user_profile_url": "", + "waitlist_url": "", + }, + "id": "", + "maintenance_mode": false, + "object": "environment", + "organization_settings": { + "actions": { + "admin_delete": true, + }, + "domains": { + "default_role": null, + "enabled": false, + "enrollment_modes": [], + }, + "enabled": false, + "max_allowed_memberships": 5, + }, + "user_settings": { + "actions": { + "create_organization": true, + "create_organizations_limit": null, + "delete_self": true, + }, + "attributes": { + "authenticator_app": { + "enabled": false, + "first_factors": [], + "name": "authenticator_app", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "backup_code": { + "enabled": false, + "first_factors": [], + "name": "backup_code", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "email_address": { + "enabled": true, + "first_factors": [ + "email_code", + ], + "name": "email_address", + "required": true, + "second_factors": [], + "used_for_first_factor": true, + "used_for_second_factor": false, + "verifications": [ + "email_code", + ], + "verify_at_sign_up": true, + }, + "first_name": { + "enabled": false, + "first_factors": [], + "name": "first_name", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "last_name": { + "enabled": false, + "first_factors": [], + "name": "last_name", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "passkey": { + "enabled": false, + "first_factors": [], + "name": "passkey", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "password": { + "enabled": true, + "first_factors": [], + "name": "password", + "required": true, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "phone_number": { + "enabled": false, + "first_factors": [], + "name": "phone_number", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "ticket": { + "enabled": true, + "first_factors": [], + "name": "ticket", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "username": { + "enabled": false, + "first_factors": [], + "name": "username", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "web3_wallet": { + "enabled": false, + "first_factors": [], + "name": "web3_wallet", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + }, + "passkey_settings": { + "allow_autofill": true, + "show_sign_in_button": true, + }, + "password_settings": { + "allowed_special_characters": "!"#$%&'()*+,-./:;<=>?@[]^_\`{|}~", + "disable_hibp": false, + "enforce_hibp_on_sign_in": true, + "max_length": 72, + "min_length": 8, + "min_zxcvbn_strength": 0, + "require_lowercase": false, + "require_numbers": false, + "require_special_char": false, + "require_uppercase": false, + "show_zxcvbn": false, + }, + "saml": { + "enabled": false, + }, + "sign_in": { + "second_factor": { + "required": false, + }, + }, + "sign_up": { + "captcha_enabled": false, + "captcha_widget_type": "smart", + "custom_action_required": false, + "legal_consent_enabled": false, + "mode": "public", + "progressive": true, + }, + "social": { + "oauth_google": { + "authenticatable": true, + "block_email_subaddresses": true, + "deprecated": false, + "enabled": true, + "logo_url": "https://img.clerk.com/static/google.png", + "name": "Google", + "not_selectable": false, + "required": false, + "strategy": "oauth_google", + }, + }, + }, +} +`; + +exports[`Environment > defaults values when instantiated without arguments 1`] = ` +Environment { + "__experimental_commerceSettings": __experimental_CommerceSettings { + "billing": { + "enabled": false, + "hasPaidOrgPlans": false, + "hasPaidUserPlans": false, + "stripePublishableKey": "", + }, + "pathRoot": "", + }, + "authConfig": AuthConfig { + "claimedAt": null, + "pathRoot": "", + "reverification": false, + "singleSessionMode": false, + }, + "displayConfig": DisplayConfig { + "afterCreateOrganizationUrl": "", + "afterJoinWaitlistUrl": "", + "afterLeaveOrganizationUrl": "", + "afterSignInUrl": "", + "afterSignOutAllUrl": "", + "afterSignOutOneUrl": "", + "afterSignOutUrl": "", + "afterSignUpUrl": "", + "afterSwitchSessionUrl": "", + "applicationName": "", + "backendHost": "", + "branded": false, + "captchaHeartbeat": false, + "captchaOauthBypass": [ + "oauth_google", + "oauth_microsoft", + "oauth_apple", + ], + "captchaProvider": "turnstile", + "captchaPublicKey": null, + "captchaPublicKeyInvisible": null, + "captchaWidgetType": null, + "createOrganizationUrl": "", + "faviconImageUrl": "", + "homeUrl": "", + "id": "", + "instanceEnvironmentType": "", + "logoImageUrl": "", + "organizationProfileUrl": "", + "pathRoot": "", + "preferredSignInStrategy": "password", + "privacyPolicyUrl": "", + "showDevModeWarning": false, + "signInUrl": "", + "signUpUrl": "", + "supportEmail": "", + "termsUrl": "", + "theme": {}, + "userProfileUrl": "", + "waitlistUrl": "", + }, + "isDevelopmentOrStaging": [Function], + "isProduction": [Function], + "isSingleSession": [Function], + "maintenanceMode": false, + "onWindowLocationHost": [Function], + "organizationSettings": OrganizationSettings { + "actions": { + "adminDelete": false, + }, + "domains": { + "defaultRole": null, + "enabled": false, + "enrollmentModes": [], + }, + "enabled": false, + "maxAllowedMemberships": 1, + "pathRoot": "", + }, + "pathRoot": "/environment", + "userSettings": UserSettings { + "actions": { + "create_organization": false, + "delete_self": false, + }, + "attributes": { + "authenticator_app": { + "enabled": false, + "first_factors": [], + "name": "authenticator_app", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "backup_code": { + "enabled": false, + "first_factors": [], + "name": "backup_code", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "email_address": { + "enabled": true, + "first_factors": [ + "email_code", + ], + "name": "email_address", + "required": true, + "second_factors": [], + "used_for_first_factor": true, + "used_for_second_factor": false, + "verifications": [ + "email_code", + ], + "verify_at_sign_up": true, + }, + "first_name": { + "enabled": false, + "first_factors": [], + "name": "first_name", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "last_name": { + "enabled": false, + "first_factors": [], + "name": "last_name", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "passkey": { + "enabled": false, + "first_factors": [], + "name": "passkey", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "password": { + "enabled": true, + "first_factors": [], + "name": "password", + "required": true, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "phone_number": { + "enabled": false, + "first_factors": [], + "name": "phone_number", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "username": { + "enabled": false, + "first_factors": [], + "name": "username", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "web3_wallet": { + "enabled": false, + "first_factors": [], + "name": "web3_wallet", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + }, + "enterpriseSSO": { + "enabled": false, + }, + "id": undefined, + "passkeySettings": { + "allow_autofill": false, + "show_sign_in_button": false, + }, + "passwordSettings": {}, + "pathRoot": "", + "saml": { + "enabled": false, + }, + "signIn": { + "second_factor": { + "enabled": false, + "required": false, + }, + }, + "signUp": { + "allowlist_only": false, + "captcha_enabled": false, + "legal_consent_enabled": false, + "mode": "public", + "progressive": true, + }, + "social": {}, + "usernameSettings": {}, + }, +} +`; + +exports[`Environment > has the same initial properties 1`] = ` +Environment { + "__experimental_commerceSettings": __experimental_CommerceSettings { + "billing": { + "enabled": false, + "hasPaidOrgPlans": false, + "hasPaidUserPlans": false, + "stripePublishableKey": "", + }, + "pathRoot": "", + }, + "authConfig": AuthConfig { + "claimedAt": null, + "pathRoot": "", + "reverification": true, + "singleSessionMode": true, + }, + "displayConfig": DisplayConfig { + "afterCreateOrganizationUrl": "", + "afterJoinWaitlistUrl": "", + "afterLeaveOrganizationUrl": "", + "afterSignInUrl": "", + "afterSignOutAllUrl": "", + "afterSignOutOneUrl": "", + "afterSignOutUrl": "", + "afterSignUpUrl": "", + "afterSwitchSessionUrl": "", + "applicationName": "", + "backendHost": "", + "branded": true, + "captchaHeartbeat": false, + "captchaHeartbeatIntervalMs": undefined, + "captchaOauthBypass": [], + "captchaProvider": "turnstile", + "captchaPublicKey": null, + "captchaPublicKeyInvisible": null, + "captchaWidgetType": null, + "clerkJSVersion": "5", + "createOrganizationUrl": "", + "faviconImageUrl": "", + "googleOneTapClientId": undefined, + "homeUrl": "", + "id": "display_config_DUMMY_ID", + "instanceEnvironmentType": "development", + "logoImageUrl": "", + "organizationProfileUrl": "", + "pathRoot": "", + "preferredSignInStrategy": "password", + "privacyPolicyUrl": "", + "showDevModeWarning": true, + "signInUrl": "", + "signUpUrl": "", + "supportEmail": "", + "termsUrl": "", + "theme": { + "accounts": { + "background_color": "#ffffff", + }, + "buttons": { + "font_color": "#ffffff", + "font_family": "sans-serif", + "font_weight": "600", + }, + "general": { + "background_color": "#ffffff", + "border_radius": "0.5em", + "box_shadow": "0 2px 8px rgba(0, 0, 0, 0.2)", + "color": "#6c47ff", + "font_color": "#151515", + "font_family": "sans-serif", + "label_font_weight": "600", + "padding": "1em", + }, + }, + "userProfileUrl": "", + "waitlistUrl": "", + }, + "isDevelopmentOrStaging": [Function], + "isProduction": [Function], + "isSingleSession": [Function], + "maintenanceMode": false, + "onWindowLocationHost": [Function], + "organizationSettings": OrganizationSettings { + "actions": { + "adminDelete": true, + }, + "domains": { + "defaultRole": null, + "enabled": false, + "enrollmentModes": [], + }, + "enabled": false, + "forceOrganizationSelection": false, + "maxAllowedMemberships": 5, + "pathRoot": "", + }, + "pathRoot": "/environment", + "userSettings": UserSettings { + "actions": { + "create_organization": true, + "create_organizations_limit": null, + "delete_self": true, + }, + "attributes": { + "authenticator_app": { + "enabled": false, + "first_factors": [], + "name": "authenticator_app", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "backup_code": { + "enabled": false, + "first_factors": [], + "name": "backup_code", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "email_address": { + "enabled": true, + "first_factors": [ + "email_code", + ], + "name": "email_address", + "required": true, + "second_factors": [], + "used_for_first_factor": true, + "used_for_second_factor": false, + "verifications": [ + "email_code", + ], + "verify_at_sign_up": true, + }, + "first_name": { + "enabled": false, + "first_factors": [], + "name": "first_name", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "last_name": { + "enabled": false, + "first_factors": [], + "name": "last_name", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "passkey": { + "enabled": false, + "first_factors": [], + "name": "passkey", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "password": { + "enabled": true, + "first_factors": [], + "name": "password", + "required": true, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "phone_number": { + "enabled": false, + "first_factors": [], + "name": "phone_number", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "ticket": { + "enabled": true, + "first_factors": [], + "name": "ticket", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "username": { + "enabled": false, + "first_factors": [], + "name": "username", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "web3_wallet": { + "enabled": false, + "first_factors": [], + "name": "web3_wallet", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + }, + "enterpriseSSO": { + "enabled": false, + }, + "id": undefined, + "passkeySettings": { + "allow_autofill": true, + "show_sign_in_button": true, + }, + "passwordSettings": { + "allowed_special_characters": "!"#$%&'()*+,-./:;<=>?@[]^_\`{|}~", + "disable_hibp": false, + "enforce_hibp_on_sign_in": true, + "max_length": 72, + "min_length": 8, + "min_zxcvbn_strength": 0, + "require_lowercase": false, + "require_numbers": false, + "require_special_char": false, + "require_uppercase": false, + "show_zxcvbn": false, + }, + "pathRoot": "", + "saml": { + "enabled": false, + }, + "signIn": { + "second_factor": { + "required": false, + }, + }, + "signUp": { + "captcha_enabled": false, + "captcha_widget_type": "smart", + "custom_action_required": false, + "legal_consent_enabled": false, + "mode": "public", + "progressive": true, + }, + "social": { + "oauth_google": { + "authenticatable": true, + "block_email_subaddresses": true, + "deprecated": false, + "enabled": true, + "logo_url": "https://img.clerk.com/static/google.png", + "name": "Google", + "not_selectable": false, + "required": false, + "strategy": "oauth_google", + }, + }, + "usernameSettings": {}, + }, +} +`; exports[`Environment __internal_toSnapshot() 1`] = ` { diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Organization.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Organization.test.ts.snap index 39122a1625d..d7dbc5f52ff 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Organization.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Organization.test.ts.snap @@ -1,4 +1,40 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Organization > has the same initial properties 1`] = ` +Organization { + "__experimental_getSubscriptions": [Function], + "addMember": [Function], + "adminDeleteEnabled": true, + "createDomain": [Function], + "createdAt": 1970-01-01T00:00:12.345Z, + "destroy": [Function], + "getDomain": [Function], + "getDomains": [Function], + "getInvitations": [Function], + "getMembershipRequests": [Function], + "getMemberships": [Function], + "getRoles": [Function], + "hasImage": true, + "id": "test_id", + "imageUrl": "https://img.clerk.com/eyJ0eXBlIjoiZGVmYXVsdCIsImlpZCI6Imluc18xbHlXRFppb2JyNjAwQUtVZVFEb1NsckVtb00iLCJyaWQiOiJ1c2VyXzJKbElJQTN2VXNjWXh1N2VUMnhINmFrTGgxOCIsImluaXRpYWxzIjoiREsifQ?width=160", + "inviteMember": [Function], + "inviteMembers": [Function], + "maxAllowedMemberships": 3, + "membersCount": 1, + "name": "test_name", + "pathRoot": "/organizations", + "pendingInvitationsCount": 10, + "publicMetadata": { + "public": "metadata", + }, + "removeMember": [Function], + "setLogo": [Function], + "slug": "test_slug", + "update": [Function], + "updateMember": [Function], + "updatedAt": 1970-01-01T00:00:05.678Z, +} +`; exports[`Organization has the same initial properties 1`] = ` Organization { diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationDomain.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationDomain.test.ts.snap index 3161fb5d3e4..702b4ea958f 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationDomain.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationDomain.test.ts.snap @@ -1,4 +1,45 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`OrganizationDomain > has the same initial nullable properties 1`] = ` +OrganizationDomain { + "affiliationEmailAddress": null, + "attemptAffiliationVerification": [Function], + "delete": [Function], + "enrollmentMode": "manual_invitation", + "id": "test_domain_id", + "name": "clerk.com", + "organizationId": "test_org_id", + "pathRoot": "", + "prepareAffiliationVerification": [Function], + "totalPendingInvitations": 0, + "totalPendingSuggestions": 0, + "updateEnrollmentMode": [Function], + "verification": null, +} +`; + +exports[`OrganizationDomain > has the same initial properties 1`] = ` +OrganizationDomain { + "affiliationEmailAddress": "some@clerk.com", + "attemptAffiliationVerification": [Function], + "delete": [Function], + "enrollmentMode": "manual_invitation", + "id": "test_domain_id", + "name": "clerk.com", + "organizationId": "test_org_id", + "pathRoot": "", + "prepareAffiliationVerification": [Function], + "totalPendingInvitations": 0, + "totalPendingSuggestions": 0, + "updateEnrollmentMode": [Function], + "verification": { + "attempts": 1, + "expiresAt": 1970-01-01T00:00:12.345Z, + "status": "verified", + "strategy": "email_code", + }, +} +`; exports[`OrganizationDomain has the same initial nullable properties 1`] = ` OrganizationDomain { diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationInvitation.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationInvitation.test.ts.snap index 0821db95738..6a6357939b5 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationInvitation.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationInvitation.test.ts.snap @@ -1,4 +1,21 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`OrganizationInvitation > has the same initial properties 1`] = ` +OrganizationInvitation { + "createdAt": 1970-01-01T00:00:12.345Z, + "emailAddress": "test_email", + "id": "test_id", + "organizationId": "test_organization_id", + "pathRoot": "", + "publicMetadata": { + "public": "metadata", + }, + "revoke": [Function], + "role": "basic_member", + "status": "pending", + "updatedAt": 1970-01-01T00:00:05.678Z, +} +`; exports[`OrganizationInvitation has the same initial properties 1`] = ` OrganizationInvitation { diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationMembership.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationMembership.test.ts.snap index 1196d8108d4..caa0c365ebc 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationMembership.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationMembership.test.ts.snap @@ -1,4 +1,61 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`OrganizationMembership > has the same initial properties 1`] = ` +OrganizationMembership { + "createdAt": 1970-01-01T00:00:12.345Z, + "destroy": [Function], + "id": "test_id", + "organization": Organization { + "__experimental_getSubscriptions": [Function], + "addMember": [Function], + "adminDeleteEnabled": true, + "createDomain": [Function], + "createdAt": 1970-01-01T00:00:12.345Z, + "destroy": [Function], + "getDomain": [Function], + "getDomains": [Function], + "getInvitations": [Function], + "getMembershipRequests": [Function], + "getMemberships": [Function], + "getRoles": [Function], + "hasImage": true, + "id": "test_org_id", + "imageUrl": "https://img.clerk.com/eyJ0eXBlIjoiZGVmYXVsdCIsImlpZCI6Imluc18xbHlXRFppb2JyNjAwQUtVZVFEb1NsckVtb00iLCJyaWQiOiJ1c2VyXzJKbElJQTN2VXNjWXh1N2VUMnhINmFrTGgxOCIsImluaXRpYWxzIjoiREsifQ?width=160", + "inviteMember": [Function], + "inviteMembers": [Function], + "maxAllowedMemberships": 3, + "membersCount": 1, + "name": "test_name", + "pathRoot": "/organizations", + "pendingInvitationsCount": 10, + "publicMetadata": { + "public": "metadata", + }, + "removeMember": [Function], + "setLogo": [Function], + "slug": "test_slug", + "update": [Function], + "updateMember": [Function], + "updatedAt": 1970-01-01T00:01:07.890Z, + }, + "pathRoot": "", + "permissions": [], + "publicMetadata": { + "foo": "bar", + }, + "publicUserData": PublicUserData { + "firstName": "test_first_name", + "hasImage": true, + "identifier": "test@identifier.gr", + "imageUrl": " https://img.clerk.com/eyJ0eXBlIjoicHJveHkiLCJzcmMiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NLTmR2TUtFQzN5cUVpMVFjV0UzQjExbF9WUEVOWW5manlLMlVQd0tCSWw9czEwMDAtYyIsInMiOiJkRkowS3dTSkRINndiODE5cXJTUUxxaWF1ZS9QcHdndC84L0lUUlpYNHpnIn0?width=160", + "lastName": "test_last_name", + "userId": undefined, + }, + "role": "admin", + "update": [Function], + "updatedAt": 1970-01-01T00:00:05.678Z, +} +`; exports[`OrganizationMembership has the same initial properties 1`] = ` OrganizationMembership { diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationMembershipRequest.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationMembershipRequest.test.ts.snap index 57cabd7dc00..9b081a708da 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationMembershipRequest.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationMembershipRequest.test.ts.snap @@ -1,4 +1,25 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`OrganizationMembership > has the same initial properties 1`] = ` +OrganizationMembershipRequest { + "accept": [Function], + "createdAt": 1970-01-01T00:00:12.345Z, + "id": "test_id", + "organizationId": "test_org_id", + "pathRoot": "", + "publicUserData": PublicUserData { + "firstName": "test_first_name", + "hasImage": true, + "identifier": "test@identifier.gr", + "imageUrl": "https://clerk.com", + "lastName": "test_last_name", + "userId": undefined, + }, + "reject": [Function], + "status": "pending", + "updatedAt": 1970-01-01T00:00:05.678Z, +} +`; exports[`OrganizationMembership has the same initial properties 1`] = ` OrganizationMembershipRequest { diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationSuggestion.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationSuggestion.test.ts.snap index dfa80b7f929..1101eb72cfa 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationSuggestion.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/OrganizationSuggestion.test.ts.snap @@ -1,4 +1,22 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`OrganizationSuggestion > has the same initial properties 1`] = ` +OrganizationSuggestion { + "accept": [Function], + "createdAt": 1970-01-01T00:00:12.345Z, + "id": "test_id", + "pathRoot": "", + "publicOrganizationData": { + "hasImage": true, + "id": "test_org_id", + "imageUrl": "test_image_url", + "name": "Test org", + "slug": "test-org", + }, + "status": "pending", + "updatedAt": 1970-01-01T00:00:05.678Z, +} +`; exports[`OrganizationSuggestion has the same initial properties 1`] = ` OrganizationSuggestion { diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/PublicUserData.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/PublicUserData.test.ts.snap index 01673ae963e..f130022cfd4 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/PublicUserData.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/PublicUserData.test.ts.snap @@ -1,3 +1,5 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`PublicUserData > JSON.stringify returns the same object structure 1`] = `"{"firstName":"John","lastName":"Doe","imageUrl":"https://example.com/image.jpg","hasImage":true,"identifier":"john-doe","userId":"123"}"`; exports[`PublicUserData JSON.stringify returns the same object structure 1`] = `"{"firstName":"John","lastName":"Doe","imageUrl":"https://example.com/image.jpg","hasImage":true,"identifier":"john-doe","userId":"123"}"`; diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Session.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Session.test.ts.snap index 7ad0e5c28bd..2fea28d442a 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Session.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Session.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Session getToken() dispatches token:update event on getToken with active organization 1`] = ` +exports[`Session > getToken() > dispatches token:update event on getToken with active organization 1`] = ` [ "token:update", { @@ -34,14 +34,14 @@ exports[`Session getToken() dispatches token:update event on getToken with activ ] `; -exports[`Session getToken() dispatches token:update event on getToken with active organization 2`] = ` +exports[`Session > getToken() > dispatches token:update event on getToken with active organization 2`] = ` [ "session:tokenResolved", null, ] `; -exports[`Session getToken() dispatches token:update event on getToken without active organization 1`] = ` +exports[`Session > getToken() > dispatches token:update event on getToken without active organization 1`] = ` [ "token:update", { @@ -75,7 +75,7 @@ exports[`Session getToken() dispatches token:update event on getToken without ac ] `; -exports[`Session getToken() dispatches token:update event on getToken without active organization 2`] = ` +exports[`Session > getToken() > dispatches token:update event on getToken without active organization 2`] = ` [ "session:tokenResolved", null, diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/UserSettings.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/UserSettings.test.ts.snap index 5ed6282b2f3..f1a4f8673e5 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/UserSettings.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/UserSettings.test.ts.snap @@ -1,4 +1,157 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`UserSettings > defaults values when instantiated with no arguments 1`] = ` +UserSettings { + "actions": { + "create_organization": false, + "delete_self": false, + }, + "attributes": { + "authenticator_app": { + "enabled": false, + "first_factors": [], + "name": "authenticator_app", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "backup_code": { + "enabled": false, + "first_factors": [], + "name": "backup_code", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "email_address": { + "enabled": true, + "first_factors": [ + "email_code", + ], + "name": "email_address", + "required": true, + "second_factors": [], + "used_for_first_factor": true, + "used_for_second_factor": false, + "verifications": [ + "email_code", + ], + "verify_at_sign_up": true, + }, + "first_name": { + "enabled": false, + "first_factors": [], + "name": "first_name", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "last_name": { + "enabled": false, + "first_factors": [], + "name": "last_name", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "passkey": { + "enabled": false, + "first_factors": [], + "name": "passkey", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "password": { + "enabled": true, + "first_factors": [], + "name": "password", + "required": true, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "phone_number": { + "enabled": false, + "first_factors": [], + "name": "phone_number", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "username": { + "enabled": false, + "first_factors": [], + "name": "username", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + "web3_wallet": { + "enabled": false, + "first_factors": [], + "name": "web3_wallet", + "required": false, + "second_factors": [], + "used_for_first_factor": false, + "used_for_second_factor": false, + "verifications": [], + "verify_at_sign_up": false, + }, + }, + "enterpriseSSO": { + "enabled": false, + }, + "id": undefined, + "passkeySettings": { + "allow_autofill": false, + "show_sign_in_button": false, + }, + "passwordSettings": {}, + "pathRoot": "", + "saml": { + "enabled": false, + }, + "signIn": { + "second_factor": { + "enabled": false, + "required": false, + }, + }, + "signUp": { + "allowlist_only": false, + "captcha_enabled": false, + "legal_consent_enabled": false, + "mode": "public", + "progressive": true, + }, + "social": {}, + "usernameSettings": {}, +} +`; exports[`UserSettings defaults values when instantiated with no arguments 1`] = ` UserSettings { diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Waitlist.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Waitlist.test.ts.snap index 2666bc100d5..2850c179ff3 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Waitlist.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Waitlist.test.ts.snap @@ -1,4 +1,13 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Waitlist > has the same initial properties 1`] = ` +Waitlist { + "createdAt": 1970-01-01T00:00:12.345Z, + "id": "test_id", + "pathRoot": "/waitlist", + "updatedAt": 1970-01-01T00:00:05.678Z, +} +`; exports[`Waitlist has the same initial properties 1`] = ` Waitlist { diff --git a/packages/clerk-js/src/core/test/fixtures.ts b/packages/clerk-js/src/core/test/fixtures.ts index e8fc4d7bd06..f7d1587fc97 100644 --- a/packages/clerk-js/src/core/test/fixtures.ts +++ b/packages/clerk-js/src/core/test/fixtures.ts @@ -13,6 +13,7 @@ import type { SignUpJSON, UserJSON, } from '@clerk/types'; +import { vi } from 'vitest'; export const mockJwt = 'eyJhbGciOiJSUzI1NiIsImtpZCI6Imluc18yR0lvUWhiVXB5MGhYN0IyY1ZrdVRNaW5Yb0QiLCJ0eXAiOiJKV1QifQ.eyJhenAiOiJodHRwczovL2FjY291bnRzLmluc3BpcmVkLnB1bWEtNzQubGNsLmRldiIsImV4cCI6MTY2NjY0ODMxMCwiaWF0IjoxNjY2NjQ4MjUwLCJpc3MiOiJodHRwczovL2NsZXJrLmluc3BpcmVkLnB1bWEtNzQubGNsLmRldiIsIm5iZiI6MTY2NjY0ODI0MCwic2lkIjoic2Vzc18yR2JEQjRlbk5kQ2E1dlMxenBDM1h6Zzl0SzkiLCJzdWIiOiJ1c2VyXzJHSXBYT0VwVnlKdzUxcmtabjlLbW5jNlN4ciJ9.n1Usc-DLDftqA0Xb-_2w8IGs4yjCmwc5RngwbSRvwevuZOIuRoeHmE2sgCdEvjfJEa7ewL6EVGVcM557TWPW--g_J1XQPwBy8tXfz7-S73CEuyRFiR97L2AHRdvRtvGtwR-o6l8aHaFxtlmfWbQXfg4kFJz2UGe9afmh3U9-f_4JOZ5fa3mI98UMy1-bo20vjXeWQ9aGrqaxHQxjnzzC-1Kpi5LdPvhQ16H0dPB8MHRTSM5TAuLKTpPV7wqixmbtcc2-0k6b9FKYZNqRVTaIyV-lifZloBvdzlfOF8nW1VVH_fx-iW5Q3hovHFcJIULHEC1kcAYTubbxzpgeVQepGg'; @@ -175,9 +176,9 @@ export const createSession = (sessionParams: WithSessionParams = {}, user: Parti object: 'session', id: sessionParams.id, status: sessionParams.status, - expire_at: sessionParams.expire_at || jest.now() + 5000, + expire_at: sessionParams.expire_at || Date.now() + 5000, abandon_at: sessionParams.abandon_at, - last_active_at: sessionParams.last_active_at || jest.now(), + last_active_at: sessionParams.last_active_at || Date.now(), last_active_organization_id: sessionParams.last_active_organization_id, actor: sessionParams.actor, user: createUser({}), @@ -188,8 +189,8 @@ export const createSession = (sessionParams: WithSessionParams = {}, user: Parti has_image: user.has_image, identifier: user.email_addresses?.find(e => e.id === user.primary_email_address_id)?.email_address || '', }, - created_at: sessionParams.created_at || jest.now() - 1000, - updated_at: sessionParams.updated_at || jest.now(), + created_at: sessionParams.created_at || Date.now() - 1000, + updated_at: sessionParams.updated_at || Date.now(), last_active_token: { object: 'token', jwt: mockJwt, @@ -245,8 +246,8 @@ export const createSignUp = (signUpParams: Partial = {}) => { export const clerkMock = (params?: Partial) => { return { - getFapiClient: jest.fn().mockReturnValue({ - request: jest.fn().mockReturnValue({ payload: { object: 'token', jwt: mockJwt }, status: 200 }), + getFapiClient: vi.fn().mockReturnValue({ + request: vi.fn().mockReturnValue({ payload: { object: 'token', jwt: mockJwt }, status: 200 }), }), ...params, }; @@ -258,7 +259,7 @@ type RecursivePartial = { export const mockFetch = (ok = true, status = 200, responsePayload = {}) => { // @ts-ignore - global.fetch = jest.fn(() => { + global.fetch = vi.fn(() => { return Promise.resolve>({ status, statusText: status.toString(), @@ -270,7 +271,7 @@ export const mockFetch = (ok = true, status = 200, responsePayload = {}) => { export const mockNetworkFailedFetch = () => { // @ts-ignore - global.fetch = jest.fn(() => { + global.fetch = vi.fn(() => { return Promise.reject(new TypeError('Failed to fetch')); }); }; diff --git a/packages/clerk-js/src/testUtils.ts b/packages/clerk-js/src/testUtils.ts index 57d179a8979..53081f4b1bc 100644 --- a/packages/clerk-js/src/testUtils.ts +++ b/packages/clerk-js/src/testUtils.ts @@ -3,10 +3,11 @@ import { matchers } from '@emotion/jest'; import type { RenderOptions } from '@testing-library/react'; import { render as _render } from '@testing-library/react'; import UserEvent from '@testing-library/user-event'; +import { afterAll, beforeAll, describe, expect, vi, type SpyInstance } from 'vitest'; expect.extend(matchers); -Element.prototype.scrollIntoView = jest.fn(); +Element.prototype.scrollIntoView = vi.fn(); const render = (ui: React.ReactElement, options?: RenderOptions) => { const userEvent = UserEvent.setup({ delay: null }); @@ -30,14 +31,14 @@ const render = (ui: React.ReactElement, options?: RenderOptions) => { */ export const mockNativeRuntime = (fn: () => void) => { describe('native runtime', () => { - let spyDocument: jest.SpyInstance; - let spyNavigator: jest.SpyInstance; + let spyDocument: SpyInstance; + let spyNavigator: SpyInstance; beforeAll(() => { - spyDocument = jest.spyOn(globalThis, 'document', 'get'); + spyDocument = vi.spyOn(globalThis, 'document', 'get'); spyDocument.mockReturnValue(undefined); - spyNavigator = jest.spyOn(globalThis.navigator, 'product', 'get'); + spyNavigator = vi.spyOn(globalThis.navigator, 'product', 'get'); spyNavigator.mockReturnValue('ReactNative'); }); diff --git a/packages/clerk-js/src/ui/common/__tests__/redirects.test.ts b/packages/clerk-js/src/ui/common/__tests__/redirects.test.ts index 11c706e8c65..0e51110a642 100644 --- a/packages/clerk-js/src/ui/common/__tests__/redirects.test.ts +++ b/packages/clerk-js/src/ui/common/__tests__/redirects.test.ts @@ -4,9 +4,8 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { it('defaults to hash based routing strategy on empty routing', function () { expect( buildVerificationRedirectUrl({ ctx: { path: '', authQueryString: '' } as any, baseUrl: '', intent: 'sign-in' }), - ).toBe('http://localhost/#/verify'); + ).toBe('http://localhost:3000/#/verify'); }); - it('returns the magic link redirect url for components using path based routing ', function () { expect( buildVerificationRedirectUrl({ @@ -14,7 +13,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/verify'); + ).toBe('http://localhost:3000/verify'); expect( buildVerificationRedirectUrl({ @@ -22,7 +21,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/sign-in/verify'); + ).toBe('http://localhost:3000/sign-in/verify'); expect( buildVerificationRedirectUrl({ @@ -34,7 +33,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/verify?redirectUrl=https://clerk.com'); + ).toBe('http://localhost:3000/verify?redirectUrl=https://clerk.com'); expect( buildVerificationRedirectUrl({ @@ -46,7 +45,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/sign-in/verify?redirectUrl=https://clerk.com'); + ).toBe('http://localhost:3000/sign-in/verify?redirectUrl=https://clerk.com'); expect( buildVerificationRedirectUrl({ @@ -58,9 +57,8 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: 'https://accounts.clerk.com/sign-in', intent: 'sign-in', }), - ).toBe('http://localhost/sign-in/verify?redirectUrl=https://clerk.com'); + ).toBe('http://localhost:3000/sign-in/verify?redirectUrl=https://clerk.com'); }); - it('returns the magic link redirect url for components using hash based routing ', function () { expect( buildVerificationRedirectUrl({ @@ -71,7 +69,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/#/verify'); + ).toBe('http://localhost:3000/#/verify'); expect( buildVerificationRedirectUrl({ @@ -83,7 +81,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/#/verify'); + ).toBe('http://localhost:3000/#/verify'); expect( buildVerificationRedirectUrl({ @@ -95,7 +93,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/#/verify?redirectUrl=https://clerk.com'); + ).toBe('http://localhost:3000/#/verify?redirectUrl=https://clerk.com'); expect( buildVerificationRedirectUrl({ @@ -107,7 +105,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/#/verify?redirectUrl=https://clerk.com'); + ).toBe('http://localhost:3000/#/verify?redirectUrl=https://clerk.com'); expect( buildVerificationRedirectUrl({ @@ -119,9 +117,8 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: 'https://accounts.clerk.com/sign-in', intent: 'sign-in', }), - ).toBe('http://localhost/#/verify?redirectUrl=https://clerk.com'); + ).toBe('http://localhost:3000/#/verify?redirectUrl=https://clerk.com'); }); - it('returns the magic link redirect url for components using virtual routing ', function () { expect( buildVerificationRedirectUrl({ @@ -156,7 +153,7 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-up', }), - ).toBe('http://localhost/sign-up/create/verify'); + ).toBe('http://localhost:3000/sign-up/create/verify'); expect( buildVerificationRedirectUrl({ @@ -168,28 +165,28 @@ describe('buildVerificationRedirectUrl(routing, baseUrl)', () => { baseUrl: '', intent: 'sign-in', }), - ).toBe('http://localhost/sign-in/verify'); + ).toBe('http://localhost:3000/sign-in/verify'); }); }); describe('buildSSOCallbackURL(ctx, baseUrl)', () => { it('returns the SSO callback URL based on sign in|up component routing or the provided base URL', () => { // Default callback URLS - expect(buildSSOCallbackURL({}, '')).toBe('http://localhost/#/sso-callback'); - expect(buildSSOCallbackURL({}, 'http://test.host')).toBe('http://localhost/#/sso-callback'); + expect(buildSSOCallbackURL({}, '')).toBe('http://localhost:3000/#/sso-callback'); + expect(buildSSOCallbackURL({}, 'http://test.host')).toBe('http://localhost:3000/#/sso-callback'); expect(buildSSOCallbackURL({ authQueryString: 'redirect_url=%2Ffoo' }, 'http://test.host')).toBe( - 'http://localhost/#/sso-callback?redirect_url=%2Ffoo', + 'http://localhost:3000/#/sso-callback?redirect_url=%2Ffoo', ); // Components mounted with hash routing - expect(buildSSOCallbackURL({ routing: 'hash' }, 'http://test.host')).toBe('http://localhost/#/sso-callback'); + expect(buildSSOCallbackURL({ routing: 'hash' }, 'http://test.host')).toBe('http://localhost:3000/#/sso-callback'); expect(buildSSOCallbackURL({ routing: 'hash', authQueryString: 'redirect_url=%2Ffoo' }, 'http://test.host')).toBe( - 'http://localhost/#/sso-callback?redirect_url=%2Ffoo', + 'http://localhost:3000/#/sso-callback?redirect_url=%2Ffoo', ); // Components mounted with path routing expect(buildSSOCallbackURL({ routing: 'path', path: 'sign-in' }, 'http://test.host')).toBe( - 'http://localhost/sign-in/sso-callback', + 'http://localhost:3000/sign-in/sso-callback', ); expect( buildSSOCallbackURL( @@ -200,7 +197,7 @@ describe('buildSSOCallbackURL(ctx, baseUrl)', () => { }, 'http://test.host', ), - ).toBe('http://localhost/sign-in/sso-callback?redirect_url=%2Ffoo'); + ).toBe('http://localhost:3000/sign-in/sso-callback?redirect_url=%2Ffoo'); // Components mounted with virtual routing expect(buildSSOCallbackURL({ routing: 'virtual' }, 'http://test.host')).toBe('http://test.host/#/sso-callback'); @@ -219,6 +216,6 @@ describe('buildSSOCallbackURL(ctx, baseUrl)', () => { ssoCallbackUrl: 'http://test.host/ctx-sso-callback', routing: 'virtual', }), - ).toBe('http://localhost/#/sso-callback'); + ).toBe('http://localhost:3000/#/sso-callback'); }); }); diff --git a/packages/clerk-js/src/ui/components/SignUp/__tests__/SignUpStart.test.tsx b/packages/clerk-js/src/ui/components/SignUp/__tests__/SignUpStart.test.tsx index c8cf1ef9016..076c16f9c44 100644 --- a/packages/clerk-js/src/ui/components/SignUp/__tests__/SignUpStart.test.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/__tests__/SignUpStart.test.tsx @@ -1,5 +1,6 @@ import { OAUTH_PROVIDERS } from '@clerk/shared/oauth'; import type { SignUpResource } from '@clerk/types'; +import { vi } from 'vitest'; import { render, screen, waitFor } from '../../../../testUtils'; import { OptionsProvider } from '../../../contexts'; @@ -297,7 +298,7 @@ describe('SignUpStart', () => { }); Object.defineProperty(window, 'history', { writable: true, - value: { replaceState: jest.fn() }, + value: { replaceState: vi.fn() }, }); render( @@ -332,7 +333,7 @@ describe('SignUpStart', () => { }); Object.defineProperty(window, 'history', { writable: true, - value: { replaceState: jest.fn() }, + value: { replaceState: vi.fn() }, }); render( @@ -372,7 +373,7 @@ describe('SignUpStart', () => { }); Object.defineProperty(window, 'history', { writable: true, - value: { replaceState: jest.fn() }, + value: { replaceState: vi.fn() }, }); render( diff --git a/packages/clerk-js/src/ui/elements/__tests__/PlainInput.test.tsx b/packages/clerk-js/src/ui/elements/__tests__/PlainInput.test.tsx index ff7cd32b4b3..a9ad0fcfc69 100644 --- a/packages/clerk-js/src/ui/elements/__tests__/PlainInput.test.tsx +++ b/packages/clerk-js/src/ui/elements/__tests__/PlainInput.test.tsx @@ -1,4 +1,3 @@ -import { describe, it } from '@jest/globals'; import { fireEvent, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -135,7 +134,7 @@ describe('PlainInput', () => { await userEvent.click(getByRole('button', { name: /set error/i })); - expect(await findByText(/some error/i)).toBeInTheDocument(); + expect(await findByText(/Some Error/i)).toBeInTheDocument(); const label = getByLabelText(/some label/i); expect(label).toHaveAttribute('aria-invalid', 'true'); diff --git a/packages/clerk-js/src/ui/elements/__tests__/RadioGroup.test.tsx b/packages/clerk-js/src/ui/elements/__tests__/RadioGroup.test.tsx index dcb8f2951d3..59bf1d77d6c 100644 --- a/packages/clerk-js/src/ui/elements/__tests__/RadioGroup.test.tsx +++ b/packages/clerk-js/src/ui/elements/__tests__/RadioGroup.test.tsx @@ -1,4 +1,3 @@ -import { describe, it } from '@jest/globals'; import { fireEvent, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -167,7 +166,7 @@ describe('RadioGroup', () => { ); await userEvent.click(getByRole('button', { name: /set error/i })); - expect(await findByText(/some error/i)).toBeInTheDocument(); + expect(await findByText(/Some Error/i)).toBeInTheDocument(); const radios = getAllByRole('radio'); radios.forEach(radio => { diff --git a/packages/clerk-js/src/ui/hooks/__tests__/useDevMode.test.tsx b/packages/clerk-js/src/ui/hooks/__tests__/useDevMode.test.tsx index 59db4893c46..cf932e78391 100644 --- a/packages/clerk-js/src/ui/hooks/__tests__/useDevMode.test.tsx +++ b/packages/clerk-js/src/ui/hooks/__tests__/useDevMode.test.tsx @@ -1,20 +1,21 @@ import type { EnvironmentResource } from '@clerk/types'; import { renderHook } from '@testing-library/react'; +import { describe, expect, test, vi } from 'vitest'; import { useDevMode } from '../useDevMode'; -const mockUseEnvironment = jest.fn(); -const mockUseOptions = jest.fn(); -const mockUseAppearance = jest.fn(); +const mockUseEnvironment = vi.fn(); +const mockUseOptions = vi.fn(); +const mockUseAppearance = vi.fn(); -jest.mock('../../contexts', () => { +vi.mock('../../contexts', () => { return { useEnvironment: () => mockUseEnvironment(), useOptions: () => mockUseOptions(), }; }); -jest.mock('../../customizables', () => { +vi.mock('../../customizables', () => { return { useAppearance: () => mockUseAppearance(), }; diff --git a/packages/clerk-js/src/ui/hooks/__tests__/useSupportEmail.test.tsx b/packages/clerk-js/src/ui/hooks/__tests__/useSupportEmail.test.tsx index 7f31c0dc218..8b342df0090 100644 --- a/packages/clerk-js/src/ui/hooks/__tests__/useSupportEmail.test.tsx +++ b/packages/clerk-js/src/ui/hooks/__tests__/useSupportEmail.test.tsx @@ -1,16 +1,17 @@ import { renderHook } from '@testing-library/react'; +import { describe, expect, test, vi } from 'vitest'; import { useSupportEmail } from '../useSupportEmail'; -const mockUseOptions = jest.fn(); -const mockUseEnvironment = jest.fn(); +const mockUseOptions = vi.fn(); +const mockUseEnvironment = vi.fn(); -jest.mock('@clerk/shared/react', () => ({ +vi.mock('@clerk/shared/react', () => ({ useClerk: () => ({ publishableKey: 'pk_live_Y2xlcmsuY2xlcmsuY29tJA', }), })); -jest.mock('../../contexts', () => { +vi.mock('../../contexts', () => { return { useEnvironment: () => mockUseEnvironment(), useOptions: () => mockUseOptions(), diff --git a/packages/clerk-js/src/ui/localization/__tests__/applyTokensToString.test.ts b/packages/clerk-js/src/ui/localization/__tests__/applyTokensToString.test.ts index ed9ff255d7e..49ec57a07c8 100644 --- a/packages/clerk-js/src/ui/localization/__tests__/applyTokensToString.test.ts +++ b/packages/clerk-js/src/ui/localization/__tests__/applyTokensToString.test.ts @@ -1,3 +1,5 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { applyTokensToString } from '../applyTokensToString'; describe('applyTokensToString', function () { @@ -34,7 +36,7 @@ describe('applyTokensToString', function () { describe('Date related tokens and modifiers', () => { beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); + vi.spyOn(console, 'warn').mockImplementation(() => {}); }); const tokens = { diff --git a/packages/clerk-js/src/ui/router/__mocks__/RouteContext.tsx b/packages/clerk-js/src/ui/router/__mocks__/RouteContext.tsx index 7693d51fe48..35d50486d09 100644 --- a/packages/clerk-js/src/ui/router/__mocks__/RouteContext.tsx +++ b/packages/clerk-js/src/ui/router/__mocks__/RouteContext.tsx @@ -1,11 +1,12 @@ import { noop } from '@clerk/shared/utils'; +import { vi } from 'vitest'; export const useRouter = () => ({ - resolve: jest.fn(() => ({ + resolve: vi.fn(() => ({ toURL: { href: 'http://test.host/test-href', }, })), - matches: jest.fn(noop), - navigate: jest.fn(noop), + matches: vi.fn(noop), + navigate: vi.fn(noop), }); diff --git a/packages/clerk-js/src/ui/router/__tests__/HashRouter.test.tsx b/packages/clerk-js/src/ui/router/__tests__/HashRouter.test.tsx index 3848285b233..4fab74225c7 100644 --- a/packages/clerk-js/src/ui/router/__tests__/HashRouter.test.tsx +++ b/packages/clerk-js/src/ui/router/__tests__/HashRouter.test.tsx @@ -1,13 +1,14 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import type { Clerk } from '../../../core/clerk'; import { HashRouter, Route, useRouter } from '..'; -const mockNavigate = jest.fn(); +const mockNavigate = vi.fn(); -jest.mock('@clerk/shared/react', () => { +vi.mock('@clerk/shared/react', () => { return { useClerk: () => { return { diff --git a/packages/clerk-js/src/ui/router/__tests__/PathRouter.test.tsx b/packages/clerk-js/src/ui/router/__tests__/PathRouter.test.tsx index 52a2dff21a8..90ab5eae5d6 100644 --- a/packages/clerk-js/src/ui/router/__tests__/PathRouter.test.tsx +++ b/packages/clerk-js/src/ui/router/__tests__/PathRouter.test.tsx @@ -1,13 +1,14 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import type { Clerk } from '../../../core/clerk'; import { PathRouter, Route, useRouter } from '..'; -const mockNavigate = jest.fn(); +const mockNavigate = vi.fn(); -jest.mock('@clerk/shared/react', () => { +vi.mock('@clerk/shared/react', () => { return { useClerk: () => { return { diff --git a/packages/clerk-js/src/ui/router/__tests__/Switch.test.tsx b/packages/clerk-js/src/ui/router/__tests__/Switch.test.tsx index c27bd019140..1ebcdd0a7e3 100644 --- a/packages/clerk-js/src/ui/router/__tests__/Switch.test.tsx +++ b/packages/clerk-js/src/ui/router/__tests__/Switch.test.tsx @@ -1,13 +1,14 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; +import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'; import { HashRouter, Route, Switch } from '../../router'; -const mockNavigate = jest.fn(); +const mockNavigate = vi.fn(); -jest.mock('@clerk/shared/react', () => ({ +vi.mock('@clerk/shared/react', () => ({ useClerk: () => ({ - navigate: jest.fn(to => { + navigate: vi.fn(to => { mockNavigate(to); if (to) { // @ts-ignore @@ -29,7 +30,7 @@ const setWindowOrigin = (origin: string) => { describe('', () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); afterAll(() => { diff --git a/packages/clerk-js/src/ui/router/__tests__/VirtualRouter.test.tsx b/packages/clerk-js/src/ui/router/__tests__/VirtualRouter.test.tsx index 18b7eef065f..a9ecc9649e2 100644 --- a/packages/clerk-js/src/ui/router/__tests__/VirtualRouter.test.tsx +++ b/packages/clerk-js/src/ui/router/__tests__/VirtualRouter.test.tsx @@ -1,14 +1,15 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { Route, useRouter, VirtualRouter } from '..'; -const mockNavigate = jest.fn(); +const mockNavigate = vi.fn(); -jest.mock('@clerk/shared/react', () => ({ +vi.mock('@clerk/shared/react', () => ({ useClerk: () => ({ - navigate: jest.fn(to => { + navigate: vi.fn(to => { mockNavigate(to); if (to) { // @ts-ignore diff --git a/packages/clerk-js/src/ui/utils/__tests__/normalizeColorString.test.ts b/packages/clerk-js/src/ui/utils/__tests__/normalizeColorString.test.ts index 40ec028c83e..cc2ab505544 100644 --- a/packages/clerk-js/src/ui/utils/__tests__/normalizeColorString.test.ts +++ b/packages/clerk-js/src/ui/utils/__tests__/normalizeColorString.test.ts @@ -1,12 +1,14 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; + import { normalizeColorString } from '../normalizeColorString'; describe('normalizeColorString', () => { beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(() => {}) as jest.Mock; + vi.spyOn(console, 'warn').mockImplementation(() => {}) as vi.Mock; }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); // Hex color tests @@ -34,7 +36,7 @@ describe('normalizeColorString', () => { expect(normalizeColorString('#12')).toBe('#12'); expect(console.warn).toHaveBeenCalledTimes(1); - (console.warn as jest.Mock).mockClear(); + (console.warn as vi.Mock).mockClear(); expect(normalizeColorString('#12345')).toBe('#12345'); expect(console.warn).toHaveBeenCalledTimes(1); }); @@ -78,11 +80,11 @@ describe('normalizeColorString', () => { expect(normalizeColorString('')).toBe(''); expect(console.warn).toHaveBeenCalledTimes(1); - (console.warn as jest.Mock).mockClear(); + (console.warn as vi.Mock).mockClear(); expect(normalizeColorString('invalid')).toBe('invalid'); expect(console.warn).toHaveBeenCalledTimes(1); - (console.warn as jest.Mock).mockClear(); + (console.warn as vi.Mock).mockClear(); expect(normalizeColorString('rgb(255,0)')).toBe('rgb(255,0)'); expect(console.warn).toHaveBeenCalledTimes(1); }); @@ -91,7 +93,7 @@ describe('normalizeColorString', () => { expect(normalizeColorString(null as any)).toBe(''); expect(console.warn).toHaveBeenCalledTimes(1); - (console.warn as jest.Mock).mockClear(); + (console.warn as vi.Mock).mockClear(); expect(normalizeColorString(123 as any)).toBe(123 as any); expect(console.warn).toHaveBeenCalledTimes(1); }); diff --git a/packages/clerk-js/src/ui/utils/test/createFixtures.tsx b/packages/clerk-js/src/ui/utils/test/createFixtures.tsx index b6b11250989..8e19cee62f8 100644 --- a/packages/clerk-js/src/ui/utils/test/createFixtures.tsx +++ b/packages/clerk-js/src/ui/utils/test/createFixtures.tsx @@ -1,6 +1,7 @@ +// import { jest } from '@jest/globals'; import type { ClerkOptions, ClientJSON, EnvironmentJSON, LoadedClerk } from '@clerk/types'; -import { jest } from '@jest/globals'; import React from 'react'; +import { vi } from 'vitest'; import { Clerk as ClerkCtor } from '../../../core/clerk'; import { Client, Environment } from '../../../core/resources'; @@ -55,11 +56,11 @@ const unboundCreateFixtures = ( } const environmentMock = new Environment(baseEnvironment); - Environment.getInstance().fetch = jest.fn(() => Promise.resolve(environmentMock)); + Environment.getInstance().fetch = vi.fn(() => Promise.resolve(environmentMock)); // @ts-expect-error We cannot mess with the singleton when tests are running in parallel const clientMock = new Client(baseClient); - Client.getOrCreateInstance().fetch = jest.fn(() => Promise.resolve(clientMock)); + Client.getOrCreateInstance().fetch = vi.fn(() => Promise.resolve(clientMock)); // Use a FAPI value for local production instances to avoid triggering the devInit flow during testing const productionPublishableKey = 'pk_live_Y2xlcmsuYWJjZWYuMTIzNDUucHJvZC5sY2xjbGVyay5jb20k'; diff --git a/packages/clerk-js/src/ui/utils/test/mockHelpers.ts b/packages/clerk-js/src/ui/utils/test/mockHelpers.ts index 8e7cf2a3265..1ac4a244562 100644 --- a/packages/clerk-js/src/ui/utils/test/mockHelpers.ts +++ b/packages/clerk-js/src/ui/utils/test/mockHelpers.ts @@ -1,29 +1,29 @@ +// import { jest } from '@jest/globals'; import type { LoadedClerk } from '@clerk/types'; -import { jest } from '@jest/globals'; +import type { ActiveSessionResource } from '@clerk/types'; +import { vi, type Mocked } from 'vitest'; import type { RouteContextValue } from '../../router'; type FunctionLike = (...args: any) => any; -type DeepJestMocked = T extends FunctionLike - ? jest.Mocked +type DeepVitestMocked = T extends FunctionLike + ? Mocked : T extends object ? { - [k in keyof T]: DeepJestMocked; + [k in keyof T]: DeepVitestMocked; } : T; +// Removing jest.Mock type for now, relying on inference type MockMap = { - [K in { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never }[keyof T]]?: jest.Mock< - // @ts-expect-error -- the typing seems to be working in practice - T[K] - >; + [K in { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never }[keyof T]]?: ReturnType; }; -const mockProp = (obj: T, k: keyof T, mocks?: MockMap) => { +const mockProp = (obj: T, k: keyof T, mocks?: MockMap) => { if (typeof obj[k] === 'function') { - // @ts-ignore - obj[k] = mocks?.[k] ?? jest.fn(); + // @ts-ignore - Assume it's assignable for mocking + obj[k] = mocks?.[k] ?? vi.fn(); } }; @@ -39,38 +39,50 @@ const mockMethodsOf = | null = any>( } Object.keys(obj) .filter(key => !options?.exclude.includes(key as keyof T)) + // Pass the specific MockMap for the object T being mocked .forEach(k => mockProp(obj, k, options?.mocks)); }; -export const mockClerkMethods = (clerk: LoadedClerk): DeepJestMocked => { - mockMethodsOf(clerk); - mockMethodsOf(clerk.client.signIn); - mockMethodsOf(clerk.client.signUp); - clerk.client.sessions.forEach(session => { - mockMethodsOf(session, { - exclude: ['checkAuthorization'], - mocks: { - touch: jest.fn(() => Promise.resolve(session)), - }, - }); - mockMethodsOf(session.user); - session.user?.emailAddresses.forEach(m => mockMethodsOf(m)); - session.user?.phoneNumbers.forEach(m => mockMethodsOf(m)); - session.user?.externalAccounts.forEach(m => mockMethodsOf(m)); - session.user?.organizationMemberships.forEach(m => { - mockMethodsOf(m); - mockMethodsOf(m.organization); +export const mockClerkMethods = (clerk: LoadedClerk): DeepVitestMocked => { + // Cast clerk to any to allow mocking properties + const clerkAny = clerk as any; + + mockMethodsOf(clerkAny); + if (clerkAny.client) { + mockMethodsOf(clerkAny.client.signIn); + mockMethodsOf(clerkAny.client.signUp); + clerkAny.client.sessions?.forEach((session: ActiveSessionResource) => { + const sessionAny = session as any; + mockMethodsOf(sessionAny, { + exclude: ['checkAuthorization'], + mocks: { + // Ensure touch mock matches expected signature if available, otherwise basic mock + touch: vi.fn(() => Promise.resolve(session)), + }, + }); + if (sessionAny.user) { + mockMethodsOf(sessionAny.user); + sessionAny.user.emailAddresses?.forEach((m: any) => mockMethodsOf(m)); + sessionAny.user.phoneNumbers?.forEach((m: any) => mockMethodsOf(m)); + sessionAny.user.externalAccounts?.forEach((m: any) => mockMethodsOf(m)); + sessionAny.user.organizationMemberships?.forEach((m: any) => { + mockMethodsOf(m); + if (m.organization) { + mockMethodsOf(m.organization); + } + }); + sessionAny.user.passkeys?.forEach((m: any) => mockMethodsOf(m)); + } }); - session.user?.passkeys.forEach(m => mockMethodsOf(m)); - }); - mockProp(clerk, 'navigate'); - mockProp(clerk, 'setActive'); - mockProp(clerk, 'redirectWithAuth'); - mockProp(clerk, '__internal_navigateWithError'); - return clerk as any as DeepJestMocked; + } + mockProp(clerkAny, 'navigate'); + mockProp(clerkAny, 'setActive'); + mockProp(clerkAny, 'redirectWithAuth'); + mockProp(clerkAny, '__internal_navigateWithError'); + return clerkAny as DeepVitestMocked; }; -export const mockRouteContextValue = ({ queryString = '' }: Partial>) => { +export const mockRouteContextValue = ({ queryString = '' }: Partial>) => { return { basePath: '', startPath: '', @@ -80,12 +92,12 @@ export const mockRouteContextValue = ({ queryString = '' }: Partial Promise.resolve(true)), - resolve: jest.fn((to: string) => new URL(to, 'https://clerk.com')), - refresh: jest.fn(), + getMatchData: vi.fn(), + matches: vi.fn(), + baseNavigate: vi.fn(), + navigate: vi.fn(() => Promise.resolve(true)), + resolve: vi.fn((to: string) => new URL(to, 'https://clerk.com')), + refresh: vi.fn(), params: {}, - } as RouteContextValue; + } as RouteContextValue; // Keep original type assertion, DeepVitestMocked applied to input only }; diff --git a/packages/clerk-js/src/ui/utils/test/runFakeTimers.ts b/packages/clerk-js/src/ui/utils/test/runFakeTimers.ts index 5fda44aeca3..7766954fb0d 100644 --- a/packages/clerk-js/src/ui/utils/test/runFakeTimers.ts +++ b/packages/clerk-js/src/ui/utils/test/runFakeTimers.ts @@ -1,4 +1,5 @@ -import { jest } from '@jest/globals'; +import { vi } from 'vitest'; +// import { jest } from '@jest/globals'; import { act } from '@testing-library/react'; type WithAct = (fn: T) => T; @@ -9,9 +10,9 @@ const withAct = ((fn: any) => }); }) as WithAct; -const advanceTimersByTime = withAct(jest.advanceTimersByTime.bind(jest)); -const runAllTimers = withAct(jest.runAllTimers.bind(jest)); -const runOnlyPendingTimers = withAct(jest.runOnlyPendingTimers.bind(jest)); +const advanceTimersByTime = withAct(vi.advanceTimersByTime.bind(vi)); +const runAllTimers = withAct(vi.runAllTimers.bind(vi)); +const runOnlyPendingTimers = withAct(vi.runOnlyPendingTimers.bind(vi)); const createFakeTimersHelpers = () => { return { advanceTimersByTime, runAllTimers, runOnlyPendingTimers }; @@ -23,13 +24,13 @@ type RunFakeTimersCallback = (timers: FakeTimersHelpers) => void | Promise export const runFakeTimers = >( cb: T, ): R extends Promise ? Promise : void => { - jest.useFakeTimers(); + vi.useFakeTimers(); const res = cb(createFakeTimersHelpers()); if (res && 'then' in res) { // @ts-expect-error - return res.finally(() => jest.useRealTimers()); + return res.finally(() => vi.useRealTimers()); } - jest.useRealTimers(); + vi.useRealTimers(); // @ts-ignore return; }; diff --git a/packages/clerk-js/src/utils/__tests__/captcha.test.ts b/packages/clerk-js/src/utils/__tests__/captcha.test.ts index 0b64aaee20b..065794a9729 100644 --- a/packages/clerk-js/src/utils/__tests__/captcha.test.ts +++ b/packages/clerk-js/src/utils/__tests__/captcha.test.ts @@ -1,4 +1,5 @@ import { shouldRetryTurnstileErrorCode } from '../captcha/turnstile'; +import { describe, it } from 'vitest'; describe('shouldRetryTurnstileErrorCode', () => { it.each([ diff --git a/packages/clerk-js/src/utils/__tests__/completeSignUpFlow.test.ts b/packages/clerk-js/src/utils/__tests__/completeSignUpFlow.test.ts index f4c1fcb5607..6a62435ec87 100644 --- a/packages/clerk-js/src/utils/__tests__/completeSignUpFlow.test.ts +++ b/packages/clerk-js/src/utils/__tests__/completeSignUpFlow.test.ts @@ -1,10 +1,11 @@ import type { SignUpField, SignUpResource } from '@clerk/types'; +import { describe, it, vi, beforeEach } from 'vitest'; import { completeSignUpFlow } from '../completeSignUpFlow'; -const mockHandleComplete = jest.fn(); -const mockNavigate = jest.fn(); -const mockAuthenticateWithRedirect = jest.fn(); +const mockHandleComplete = vi.fn(); +const mockNavigate = vi.fn(); +const mockAuthenticateWithRedirect = vi.fn(); describe('completeSignUpFlow', () => { beforeEach(() => { diff --git a/packages/clerk-js/src/utils/__tests__/date.test.ts b/packages/clerk-js/src/utils/__tests__/date.test.ts index dfb60addb73..33c8e28a069 100644 --- a/packages/clerk-js/src/utils/__tests__/date.test.ts +++ b/packages/clerk-js/src/utils/__tests__/date.test.ts @@ -1,13 +1,14 @@ import { unixEpochToDate } from '../date'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; describe('date utilities', () => { beforeAll(() => { - jest.useFakeTimers('modern'); - jest.setSystemTime(); + vi.useFakeTimers(); + vi.setSystemTime(Date.now()); }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); describe('unixEpochToDate', () => { diff --git a/packages/clerk-js/src/utils/__tests__/dynamicParamParser.test.ts b/packages/clerk-js/src/utils/__tests__/dynamicParamParser.test.ts index 3f9ceafd853..8b8b267ba53 100644 --- a/packages/clerk-js/src/utils/__tests__/dynamicParamParser.test.ts +++ b/packages/clerk-js/src/utils/__tests__/dynamicParamParser.test.ts @@ -1,4 +1,4 @@ -import { describe } from '@jest/globals'; +import { describe, it } from 'vitest'; import { createDynamicParamParser } from '../dynamicParamParser'; diff --git a/packages/clerk-js/src/utils/__tests__/errors.test.ts b/packages/clerk-js/src/utils/__tests__/errors.test.ts index 9de84ae2216..85fb4ec3994 100644 --- a/packages/clerk-js/src/utils/__tests__/errors.test.ts +++ b/packages/clerk-js/src/utils/__tests__/errors.test.ts @@ -1,5 +1,6 @@ import type { ClerkAPIResponseError } from '../../core/resources/Error'; import { isError } from '../errors'; +import { describe, it } from 'vitest'; describe('isError(err, code)', () => { it('check if the the API response contains at least one error of the provided code', () => { diff --git a/packages/clerk-js/src/utils/__tests__/ignoreEventValue.test.ts b/packages/clerk-js/src/utils/__tests__/ignoreEventValue.test.ts index 0968e8c5101..61ca46ce558 100644 --- a/packages/clerk-js/src/utils/__tests__/ignoreEventValue.test.ts +++ b/packages/clerk-js/src/utils/__tests__/ignoreEventValue.test.ts @@ -1,4 +1,5 @@ import { ignoreEventValue } from '../ignoreEventValue'; +import { describe, it } from 'vitest'; const noop = (..._args: any[]): void => { // do nothing. diff --git a/packages/clerk-js/src/utils/__tests__/instance.test.ts b/packages/clerk-js/src/utils/__tests__/instance.test.ts index f0e4c1cbcef..67b8f9b0799 100644 --- a/packages/clerk-js/src/utils/__tests__/instance.test.ts +++ b/packages/clerk-js/src/utils/__tests__/instance.test.ts @@ -1,4 +1,5 @@ import { validateFrontendApi } from '../instance'; +import { describe, it } from 'vitest'; describe('validateFrontendApi(str)', () => { it.each([ diff --git a/packages/clerk-js/src/utils/__tests__/jwt.test.ts b/packages/clerk-js/src/utils/__tests__/jwt.test.ts index f7f5e564b1a..54a4b608d63 100644 --- a/packages/clerk-js/src/utils/__tests__/jwt.test.ts +++ b/packages/clerk-js/src/utils/__tests__/jwt.test.ts @@ -1,4 +1,5 @@ import { decode } from '../jwt'; +import { describe, it } from 'vitest'; const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NzU4NzY3OTAsImRhdGEiOiJmb29iYXIiLCJpYXQiOjE2NzU4NzY3MzB9.Z1BC47lImYvaAtluJlY-kBo0qOoAk42Xb-gNrB2SxJg'; diff --git a/packages/clerk-js/src/utils/__tests__/localStorage.test.ts b/packages/clerk-js/src/utils/__tests__/localStorage.test.ts index 792f0acfa7f..c86fb12d587 100644 --- a/packages/clerk-js/src/utils/__tests__/localStorage.test.ts +++ b/packages/clerk-js/src/utils/__tests__/localStorage.test.ts @@ -1,4 +1,5 @@ import { SafeLocalStorage } from '../localStorage'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; describe('SafeLocalStorage', () => { let mockStorage: { [key: string]: string } = {}; @@ -26,7 +27,7 @@ describe('SafeLocalStorage', () => { afterEach(() => { mockStorage = {}; - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); describe('setItem', () => { @@ -53,13 +54,13 @@ describe('SafeLocalStorage', () => { }); it('sets expiration when provided', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const now = Date.now(); SafeLocalStorage.setItem('test', 'value', 1000); const stored = JSON.parse(mockStorage['__clerk_test']); expect(stored.exp).toBe(now + 1000); - jest.useRealTimers(); + vi.useRealTimers(); }); it('stores complex objects correctly', () => { @@ -105,15 +106,15 @@ describe('SafeLocalStorage', () => { }); it('returns default value and removes item when expired', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); SafeLocalStorage.setItem('test', 'value', 1_000); // Advance time beyond expiration - jest.advanceTimersByTime(1_001); + vi.advanceTimersByTime(1_001); expect(SafeLocalStorage.getItem('test', 'default')).toBe('default'); expect(mockStorage['__clerk_test']).toBeUndefined(); - jest.useRealTimers(); + vi.useRealTimers(); }); it('handles malformed JSON data by returning default value', () => { diff --git a/packages/clerk-js/src/utils/__tests__/memoizeStateListenerCallback.test.ts b/packages/clerk-js/src/utils/__tests__/memoizeStateListenerCallback.test.ts index b553ae25003..1be794dde9f 100644 --- a/packages/clerk-js/src/utils/__tests__/memoizeStateListenerCallback.test.ts +++ b/packages/clerk-js/src/utils/__tests__/memoizeStateListenerCallback.test.ts @@ -1,5 +1,7 @@ // TODO: jest fails because of a circular dependency on Client -> Base -> Client // This circular dep is a known issue we plan to address soon. Enable the tests then +import { describe, it } from 'vitest'; + describe.skip('memoizeStateListenerCallback', () => { it.skip('runs', () => { // TODO diff --git a/packages/clerk-js/src/utils/__tests__/organization.test.ts b/packages/clerk-js/src/utils/__tests__/organization.test.ts index 4262f073372..eedee750b28 100644 --- a/packages/clerk-js/src/utils/__tests__/organization.test.ts +++ b/packages/clerk-js/src/utils/__tests__/organization.test.ts @@ -1,4 +1,5 @@ import { isOrganizationId } from '../organization'; +import { describe, it } from 'vitest'; describe('isOrganizationId(string)', () => { it('should return true for strings starting with `org_`', () => { diff --git a/packages/clerk-js/src/utils/__tests__/passkeys.test.ts b/packages/clerk-js/src/utils/__tests__/passkeys.test.ts index c78c0f40b49..65928afe4bb 100644 --- a/packages/clerk-js/src/utils/__tests__/passkeys.test.ts +++ b/packages/clerk-js/src/utils/__tests__/passkeys.test.ts @@ -4,6 +4,7 @@ import type { PublicKeyCredentialWithAuthenticatorAssertionResponse, PublicKeyCredentialWithAuthenticatorAttestationResponse, } from '@clerk/types'; +import { describe, it } from 'vitest'; import { bufferToBase64Url, diff --git a/packages/clerk-js/src/utils/__tests__/path.test.ts b/packages/clerk-js/src/utils/__tests__/path.test.ts index fd1ae1c2ac2..f65a1d52d96 100644 --- a/packages/clerk-js/src/utils/__tests__/path.test.ts +++ b/packages/clerk-js/src/utils/__tests__/path.test.ts @@ -1,4 +1,5 @@ import { joinPaths } from '../path'; +import { describe, it } from 'vitest'; describe('joinPaths(a, b)', () => { const cases = [ diff --git a/packages/clerk-js/src/utils/__tests__/queryStateParams.test.ts b/packages/clerk-js/src/utils/__tests__/queryStateParams.test.ts index e19a7b5fb2d..4101b05302d 100644 --- a/packages/clerk-js/src/utils/__tests__/queryStateParams.test.ts +++ b/packages/clerk-js/src/utils/__tests__/queryStateParams.test.ts @@ -1,4 +1,5 @@ import { appendModalState } from '../queryStateParams'; +import { describe, it } from 'vitest'; describe('appendModalState function', () => { it('returns url with query param', () => { diff --git a/packages/clerk-js/src/utils/__tests__/querystring.test.ts b/packages/clerk-js/src/utils/__tests__/querystring.test.ts index 75a5b54a322..bdb3265927c 100644 --- a/packages/clerk-js/src/utils/__tests__/querystring.test.ts +++ b/packages/clerk-js/src/utils/__tests__/querystring.test.ts @@ -1,4 +1,5 @@ import { camelToSnake } from '@clerk/shared/underscore'; +import { describe, it } from 'vitest'; import { getQueryParams, stringifyQueryParams } from '../querystring'; diff --git a/packages/clerk-js/src/utils/__tests__/redirectUrls.test.ts b/packages/clerk-js/src/utils/__tests__/redirectUrls.test.ts index 8f7a3c4c51c..11afc84b400 100644 --- a/packages/clerk-js/src/utils/__tests__/redirectUrls.test.ts +++ b/packages/clerk-js/src/utils/__tests__/redirectUrls.test.ts @@ -1,5 +1,6 @@ import { snakeToCamel } from '@clerk/shared/underscore'; import type { RedirectOptions } from '@clerk/types'; +import { describe, it, afterAll, beforeEach } from 'vitest'; import { RedirectUrls } from '../redirectUrls'; diff --git a/packages/clerk-js/src/utils/__tests__/resourceParams.test.ts b/packages/clerk-js/src/utils/__tests__/resourceParams.test.ts index 31c694eae25..91a9c611dc0 100644 --- a/packages/clerk-js/src/utils/__tests__/resourceParams.test.ts +++ b/packages/clerk-js/src/utils/__tests__/resourceParams.test.ts @@ -1,4 +1,5 @@ import type { UpdateUserParams } from '@clerk/types'; +import { describe, it } from 'vitest'; import { normalizeUnsafeMetadata } from '../resourceParams'; diff --git a/packages/clerk-js/src/utils/__tests__/url.test.ts b/packages/clerk-js/src/utils/__tests__/url.test.ts index 53176d95584..9e37b01cc08 100644 --- a/packages/clerk-js/src/utils/__tests__/url.test.ts +++ b/packages/clerk-js/src/utils/__tests__/url.test.ts @@ -1,5 +1,6 @@ import { logger } from '@clerk/shared/logger'; import type { SignUpResource } from '@clerk/types'; +import { afterAll, beforeEach, describe, expect, it, test, vi } from 'vitest'; import { buildURL, @@ -148,7 +149,7 @@ describe('hasBannedProtocol(url)', () => { describe('buildURL(options: URLParams, skipOrigin)', () => { it('builds a URL()', () => { - expect(buildURL({}, { stringify: true })).toBe('http://localhost/'); + expect(buildURL({}, { stringify: true })).toBe('http://localhost:3000/'); expect( buildURL( { @@ -158,7 +159,7 @@ describe('buildURL(options: URLParams, skipOrigin)', () => { }, { stringify: true }, ), - ).toBe('http://localhost/my-path?my-search#my-hash?my-hashed-search'); + ).toBe('http://localhost:3000/my-path?my-search#my-hash?my-hashed-search'); expect( buildURL( { @@ -499,7 +500,7 @@ describe('isAllowedRedirect', () => { ['..//evil.com', ['https://www.clerk.com'], false], ]; - const warnMock = jest.spyOn(logger, 'warnOnce'); + const warnMock = vi.spyOn(logger, 'warnOnce'); beforeEach(() => warnMock.mockClear()); afterAll(() => warnMock.mockRestore()); @@ -516,11 +517,7 @@ describe('createAllowedRedirectOrigins', () => { const allowedRedirectOriginsValuesUndefined = createAllowedRedirectOrigins(undefined, frontendApi, 'production'); const allowedRedirectOriginsValuesEmptyArray = createAllowedRedirectOrigins([], frontendApi, 'production'); - const expectedAllowedRedirectOrigins = [ - 'http://localhost', // Current location - `https://example.com`, // Primary domain - `https://*.example.com`, // Wildcard subdomains - ]; + const expectedAllowedRedirectOrigins = ['http://localhost:3000', `https://example.com`, `https://*.example.com`]; expect(allowedRedirectOriginsValuesUndefined).toEqual(expectedAllowedRedirectOrigins); expect(allowedRedirectOriginsValuesEmptyArray).toEqual(expectedAllowedRedirectOrigins); @@ -532,10 +529,10 @@ describe('createAllowedRedirectOrigins', () => { const allowedRedirectOriginsValuesEmptyArray = createAllowedRedirectOrigins([], frontendApi, 'development'); const expectedAllowedRedirectOrigins = [ - 'http://localhost', // Current location - `https://foo-bar-42.accounts.dev`, // Account Portal - `https://*.foo-bar-42.accounts.dev`, // Account Portal subdomains - `https://foo-bar-42.clerk.accounts.dev`, // Frontend API + 'http://localhost:3000', + `https://foo-bar-42.accounts.dev`, + `https://*.foo-bar-42.accounts.dev`, + `https://foo-bar-42.clerk.accounts.dev`, ]; expect(allowedRedirectOriginsValuesUndefined).toEqual(expectedAllowedRedirectOrigins); diff --git a/packages/clerk-js/vitest.config.ts b/packages/clerk-js/vitest.config.ts new file mode 100644 index 00000000000..9f48db81aa7 --- /dev/null +++ b/packages/clerk-js/vitest.config.ts @@ -0,0 +1,78 @@ +import { defineConfig } from 'vitest/config'; +import react from '@vitejs/plugin-react'; // Assuming React is used, based on dependencies +import svgr from 'vite-plugin-svgr'; // For SVG handling + +// https://vitest.dev/config/ +export default defineConfig({ + // Provide global variables like __PKG_NAME__ and __PKG_VERSION__ + // Replaces jest.globals + define: { + __PKG_NAME__: '\"@clerk/clerk-js\"', + __PKG_VERSION__: '\"test\"', // Or derive dynamically if needed + }, + plugins: [ + // @ts-expect-error - svgr types might be slightly off depending on versions + svgr(), + // Use the React plugin for JSX/TSX transformation + // This replaces ts-jest/swc + react({ + // Use babel for @emotion support if needed, otherwise remove this + // babel: { + // plugins: ['@emotion/babel-plugin'], + // }, + }), + ], + test: { + name: 'clerk-js', // Display name, replaces jest.displayName + globals: true, // Replaces jest.injectGlobals / @jest/globals + environment: 'jsdom', // Replaces jest.testEnvironment + setupFiles: './vitest.setup.ts', // Replaces jest.setupFiles / setupFilesAfterEnv + + // Replicate test file patterns + // Replaces jest.testRegex + include: [ + 'src/**/__tests__/**/*.test.{ts,tsx}', + 'src/ui/**/__tests__/**/*.test.{ts,tsx}', + 'src/**/(core|utils)/**/*.test.{ts,tsx}', + ], + + // Handle node_module transforms + // Replaces jest.transformIgnorePatterns + deps: { + inline: [ + '@formkit/auto-animate', // Ensure this is transformed + ], + }, + + // Coverage configuration + // Replaces jest.collectCoverage, coverageProvider, etc. + coverage: { + provider: 'v8', + enabled: true, + reporter: ['text', 'json', 'html'], + include: ['src/**/*.{ts,tsx}'], + exclude: [ + 'src/**/*.d.ts', + 'src/**/index.ts', + 'src/**/index.browser.ts', + 'src/**/index.headless.ts', + 'src/**/index.headless.browser.ts', + 'src/**/coverage/**', + 'src/**/dist/**', + 'src/**/node_modules/**', + 'src/(ui|utils|core)/__tests__/**', // Exclude test files themselves + ], + }, + + // Optional: Point to a specific tsconfig if needed + // tsconfig: 'tsconfig.test.json' + }, + // Optional: Replicate module resolution if needed + resolve: { + // Add alias for SVG imports to point to the mock + alias: { + '.svg$': './src/__tests__/mocks/svgMock.tsx', // Use regex to match any path ending in .svg + }, + // modules: ['node_modules', 'src'], // Replaces jest.moduleDirectories (Uncomment if needed) + }, +}); diff --git a/packages/clerk-js/vitest.setup.ts b/packages/clerk-js/vitest.setup.ts new file mode 100644 index 00000000000..03ea76b462c --- /dev/null +++ b/packages/clerk-js/vitest.setup.ts @@ -0,0 +1,96 @@ +import { afterAll, beforeAll, vi } from 'vitest'; +import crypto from 'node:crypto'; +import { TextDecoder, TextEncoder } from 'node:util'; +import '@testing-library/jest-dom/vitest'; // Use Vitest extensions for jest-dom +import React from 'react'; // Import React for the mock component + +// --- Setup from root jest.setup-after-env.ts --- + +// Store the original method +// eslint-disable-next-line @typescript-eslint/unbound-method +const ogToLocaleDateString = Date.prototype.toLocaleDateString; + +beforeAll(() => { + // Make sure our tests always use the same locale + Date.prototype.toLocaleDateString = function (...args: any[]) { + // Call original method with 'en-US' locale + return ogToLocaleDateString.call(this, 'en-US', args[1]); // Pass options if provided + }; + + // --- Setup from jest.jsdom-with-timezone.ts --- + // Set a default timezone (e.g., UTC) for consistency + process.env.TZ = 'UTC'; +}); + +afterAll(() => { + // Restore original Date method + Date.prototype.toLocaleDateString = ogToLocaleDateString; +}); + +// --- Setup from package jest.setup.ts --- + +// Mock Response class if not already defined by jsdom/happy-dom +class FakeResponse {} + +// Polyfill/mock global objects for the jsdom environment +if (typeof window !== 'undefined') { + Object.defineProperties(globalThis, { + TextDecoder: { value: TextDecoder }, + TextEncoder: { value: TextEncoder }, + Response: { value: FakeResponse }, + crypto: { value: crypto.webcrypto }, + }); + + // Mock ResizeObserver + window.ResizeObserver = + window.ResizeObserver || + vi.fn().mockImplementation(() => ({ + disconnect: vi.fn(), + observe: vi.fn(), + unobserve: vi.fn(), + })); + + // Mock matchMedia + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), + }); + + // Mock IntersectionObserver + //@ts-expect-error - Mocking class + globalThis.IntersectionObserver = class IntersectionObserver { + constructor() {} + disconnect() { + return null; + } + observe() { + return null; + } + takeRecords() { + return []; // Return empty array as per spec + } + unobserve() { + return null; + } + }; +} + +// Mock jest-chrome if its functionality is needed +// Example: Mocking chrome.runtime.sendMessage +// global.chrome = { +// runtime: { +// sendMessage: vi.fn(), +// // ... other chrome APIs needed +// }, +// // ... other chrome namespaces needed +// }; + +// Add any other global setup needed for your tests +console.log('ClerkJS Vitest setup complete.'); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1bcc0c05903..f8a35465745 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -498,6 +498,9 @@ importers: specifier: 0.13.11 version: 0.13.11 devDependencies: + '@emotion/jest': + specifier: ^11.13.0 + version: 11.13.0(@types/jest@29.5.12) '@rsdoctor/rspack-plugin': specifier: ^0.4.13 version: 0.4.13(@rspack/core@1.2.8(@swc/helpers@0.5.15))(webpack@5.94.0(esbuild@0.25.0)) @@ -516,6 +519,12 @@ importers: '@types/webpack-env': specifier: ^1.18.8 version: 1.18.8 + jsdom: + specifier: ^24.1.1 + version: 24.1.3 + vite-plugin-svgr: + specifier: ^4.2.0 + version: 4.3.0(rollup@4.39.0)(typescript@5.8.3)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.1)) webpack-merge: specifier: ^5.10.0 version: 5.10.0 @@ -3455,82 +3464,66 @@ packages: '@miniflare/cache@2.14.4': resolution: {integrity: sha512-ayzdjhcj+4mjydbNK7ZGDpIXNliDbQY4GPcY2KrYw0v1OSUdj5kZUkygD09fqoGRfAks0d91VelkyRsAXX8FQA==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/core@2.14.4': resolution: {integrity: sha512-FMmZcC1f54YpF4pDWPtdQPIO8NXfgUxCoR9uyrhxKJdZu7M6n8QKopPVNuaxR40jcsdxb7yKoQoFWnHfzJD9GQ==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/d1@2.14.4': resolution: {integrity: sha512-pMBVq9XWxTDdm+RRCkfXZP+bREjPg1JC8s8C0JTovA9OGmLQXqGTnFxIaS9vf1d8k3uSUGhDzPTzHr0/AUW1gA==} engines: {node: '>=16.7'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/durable-objects@2.14.4': resolution: {integrity: sha512-+JrmHP6gHHrjxV8S3axVw5lGHLgqmAGdcO/1HJUPswAyJEd3Ah2YnKhpo+bNmV4RKJCtEq9A2hbtVjBTD2YzwA==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/html-rewriter@2.14.4': resolution: {integrity: sha512-GB/vZn7oLbnhw+815SGF+HU5EZqSxbhIa3mu2L5MzZ2q5VOD5NHC833qG8c2GzDPhIaZ99ITY+ZJmbR4d+4aNQ==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/kv@2.14.4': resolution: {integrity: sha512-QlERH0Z+klwLg0xw+/gm2yC34Nnr/I0GcQ+ASYqXeIXBwjqOtMBa3YVQnocaD+BPy/6TUtSpOAShHsEj76R2uw==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/queues@2.14.4': resolution: {integrity: sha512-aXQ5Ik8Iq1KGMBzGenmd6Js/jJgqyYvjom95/N9GptCGpiVWE5F0XqC1SL5rCwURbHN+aWY191o8XOFyY2nCUA==} engines: {node: '>=16.7'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/r2@2.14.4': resolution: {integrity: sha512-4ctiZWh7Ty7LB3brUjmbRiGMqwyDZgABYaczDtUidblo2DxX4JZPnJ/ZAyxMPNJif32kOJhcg6arC2hEthR9Sw==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/runner-vm@2.14.4': resolution: {integrity: sha512-Nog0bB9SVhPbZAkTWfO4lpLAUsBXKEjlb4y+y66FJw77mPlmPlVdpjElCvmf8T3VN/pqh83kvELGM+/fucMf4g==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/shared-test-environment@2.14.4': resolution: {integrity: sha512-FdU2/8wEd00vIu+MfofLiHcfZWz+uCbE2VTL85KpyYfBsNGAbgRtzFMpOXdoXLqQfRu6MBiRwWpb2FbMrBzi7g==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/shared@2.14.4': resolution: {integrity: sha512-upl4RSB3hyCnITOFmRZjJj4A72GmkVrtfZTilkdq5Qe5TTlzsjVeDJp7AuNUM9bM8vswRo+N5jOiot6O4PVwwQ==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/sites@2.14.4': resolution: {integrity: sha512-O5npWopi+fw9W9Ki0gy99nuBbgDva/iXy8PDC4dAXDB/pz45nISDqldabk0rL2t4W2+lY6LXKzdOw+qJO1GQTA==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/storage-file@2.14.4': resolution: {integrity: sha512-JxcmX0hXf4cB0cC9+s6ZsgYCq+rpyUKRPCGzaFwymWWplrO3EjPVxKCcMxG44jsdgsII6EZihYUN2J14wwCT7A==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/storage-memory@2.14.4': resolution: {integrity: sha512-9jB5BqNkMZ3SFjbPFeiVkLi1BuSahMhc/W1Y9H0W89qFDrrD+z7EgRgDtHTG1ZRyi9gIlNtt9qhkO1B6W2qb2A==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/watcher@2.14.4': resolution: {integrity: sha512-PYn05ET2USfBAeXF6NZfWl0O32KVyE8ncQ/ngysrh3hoIV7l3qGGH7ubeFx+D8VWQ682qYhwGygUzQv2j1tGGg==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@miniflare/web-sockets@2.14.4': resolution: {integrity: sha512-stTxvLdJ2IcGOs76AnvGYAzGvx8JvQPRxC5DW0P5zdAAnhL33noqb5LKdPt3P37BKp9FzBKZHuihQI9oVqwm0g==} engines: {node: '>=16.13'} - deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4 '@modelcontextprotocol/sdk@1.7.0': resolution: {integrity: sha512-IYPe/FLpvF3IZrd/f5p5ffmWhMc3aEMuM2wGJASDqC2Ge7qatVCdbfPx3n/5xFeb19xN0j/911M2AaFuircsWA==} @@ -4574,6 +4567,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} engines: {node: '>=14'} @@ -4592,50 +4591,100 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-svg-dynamic-title@6.5.1': resolution: {integrity: sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-svg-em-dimensions@6.5.1': resolution: {integrity: sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-transform-react-native-svg@6.5.1': resolution: {integrity: sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-transform-svg-component@6.5.1': resolution: {integrity: sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==} engines: {node: '>=12'} peerDependencies: '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@svgr/babel-preset@6.5.1': resolution: {integrity: sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==} engines: {node: '>=10'} peerDependencies: '@babel/core': ^7.0.0-0 + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@svgr/core@6.5.1': resolution: {integrity: sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==} engines: {node: '>=10'} + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + '@svgr/hast-util-to-babel-ast@6.5.1': resolution: {integrity: sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==} engines: {node: '>=10'} + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + '@svgr/plugin-jsx@6.5.1': resolution: {integrity: sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==} engines: {node: '>=10'} peerDependencies: '@svgr/core': ^6.0.0 + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + '@svgr/plugin-svgo@6.5.1': resolution: {integrity: sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==} engines: {node: '>=10'} @@ -6873,6 +6922,15 @@ packages: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cosmiconfig@9.0.0: resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -13994,6 +14052,11 @@ packages: '@nuxt/kit': optional: true + vite-plugin-svgr@4.3.0: + resolution: {integrity: sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==} + peerDependencies: + vite: '>=2.6.0' + vite-plugin-vue-tracer@0.1.3: resolution: {integrity: sha512-+fN6oo0//dwZP9Ax9gRKeUroCqpQ43P57qlWgL0ljCIxAs+Rpqn/L4anIPZPgjDPga5dZH+ZJsshbF0PNJbm3Q==} peerDependencies: @@ -18955,6 +19018,10 @@ snapshots: dependencies: '@babel/core': 7.26.9 + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 @@ -18967,22 +19034,42 @@ snapshots: dependencies: '@babel/core': 7.26.9 + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-preset@6.5.1(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 @@ -18995,6 +19082,18 @@ snapshots: '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.26.9) '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.26.9) + '@svgr/babel-preset@8.1.0(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.26.9) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.26.9) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.26.9) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.26.9) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.26.9) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.26.9) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.26.9) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.26.9) + '@svgr/core@6.5.1': dependencies: '@babel/core': 7.26.9 @@ -19005,11 +19104,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@svgr/core@8.1.0(typescript@5.8.3)': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-preset': 8.1.0(@babel/core@7.26.9) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.8.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + '@svgr/hast-util-to-babel-ast@6.5.1': dependencies: '@babel/types': 7.26.9 entities: 4.5.0 + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.26.9 + entities: 4.5.0 + '@svgr/plugin-jsx@6.5.1(@svgr/core@6.5.1)': dependencies: '@babel/core': 7.26.9 @@ -19020,6 +19135,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.8.3))': + dependencies: + '@babel/core': 7.26.9 + '@svgr/babel-preset': 8.1.0(@babel/core@7.26.9) + '@svgr/core': 8.1.0(typescript@5.8.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + '@svgr/plugin-svgo@6.5.1(@svgr/core@6.5.1)': dependencies: '@svgr/core': 6.5.1 @@ -22115,6 +22240,15 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 + cosmiconfig@8.3.6(typescript@5.8.3): + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.8.3 + cosmiconfig@9.0.0(typescript@5.8.3): dependencies: env-paths: 2.2.1 @@ -22320,7 +22454,6 @@ snapshots: cssstyle@4.1.0: dependencies: rrweb-cssom: 0.7.1 - optional: true csstype@3.1.3: {} @@ -22387,7 +22520,6 @@ snapshots: dependencies: whatwg-mimetype: 4.0.0 whatwg-url: 14.0.0 - optional: true data-view-buffer@1.0.2: dependencies: @@ -24639,7 +24771,6 @@ snapshots: html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 - optional: true html-entities@2.5.2: {} @@ -24688,7 +24819,6 @@ snapshots: debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - optional: true http-proxy-middleware@2.0.6(@types/express@4.17.21): dependencies: @@ -25831,7 +25961,6 @@ snapshots: - bufferutil - supports-color - utf-8-validate - optional: true jsesc@3.0.2: {} @@ -28980,8 +29109,7 @@ snapshots: parseurl: 1.3.3 path-to-regexp: 8.2.0 - rrweb-cssom@0.7.1: - optional: true + rrweb-cssom@0.7.1: {} rslog@1.2.3: {} @@ -30117,7 +30245,6 @@ snapshots: tr46@5.0.0: dependencies: punycode: 2.3.1 - optional: true tree-dump@1.0.2(tslib@2.8.1): dependencies: @@ -31020,6 +31147,17 @@ snapshots: transitivePeerDependencies: - supports-color + vite-plugin-svgr@4.3.0(rollup@4.39.0)(typescript@5.8.3)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.1)): + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.39.0) + '@svgr/core': 8.1.0(typescript@5.8.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.8.3)) + vite: 6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.1) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + vite-plugin-vue-tracer@0.1.3(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)): dependencies: estree-walker: 3.0.3 @@ -31160,7 +31298,6 @@ snapshots: w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 - optional: true walker@1.0.8: dependencies: @@ -31332,14 +31469,12 @@ snapshots: whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 - optional: true whatwg-fetch@3.6.20: {} whatwg-mimetype@3.0.0: {} - whatwg-mimetype@4.0.0: - optional: true + whatwg-mimetype@4.0.0: {} whatwg-url-without-unicode@8.0.0-3: dependencies: @@ -31356,7 +31491,6 @@ snapshots: dependencies: tr46: 5.0.0 webidl-conversions: 7.0.0 - optional: true whatwg-url@5.0.0: dependencies: @@ -31514,8 +31648,7 @@ snapshots: xml-name-validator@4.0.0: {} - xml-name-validator@5.0.0: - optional: true + xml-name-validator@5.0.0: {} xml2js@0.6.0: dependencies: