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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
3 changes: 1 addition & 2 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,6 @@ export default tseslint.config([
jest: pluginJest,
},
rules: {
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/unbound-method': 'off',
'jest/unbound-method': 'error',
},
Expand Down Expand Up @@ -366,7 +365,7 @@ export default tseslint.config([
},
{
name: 'packages/clerk-js - vitest',
files: ['packages/clerk-js/src/**/*.test.{ts,tsx}'],
files: ['packages/clerk-js/src/**/*.spec.{ts,tsx}'],
rules: {
'jest/unbound-method': 'off',
'@typescript-eslint/unbound-method': 'off',
Expand Down
62 changes: 62 additions & 0 deletions packages/clerk-js/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const { name } = require('./package.json');

/** @type {import('ts-jest').JestConfigWithTsJest} */
const config = {
displayName: name.replace('@clerk', ''),
injectGlobals: true,
globals: {
__PKG_NAME__: '@clerk/clerk-js',
__PKG_VERSION__: 'test',
__BUILD_VARIANT_CHIPS__: false,
__BUILD_DISABLE_RHC__: false,
},

testEnvironment: '<rootDir>/jest.jsdom-with-timezone.ts',
roots: ['<rootDir>/src'],
setupFiles: ['./jest.setup.ts'],
setupFilesAfterEnv: ['./jest.setup-after-env.ts'],
testRegex: [
'/__tests__/(.+/)*.*.test.[jt]sx?$',
'/ui/.*/__tests__/.*.test.[jt]sx?$',
'/(core|utils)/.*.test.[jt]sx?$',
],
testPathIgnorePatterns: ['/node_modules/'],
collectCoverage: false,
coverageProvider: 'v8',
coverageDirectory: 'coverage',
coveragePathIgnorePatterns: ['/node_modules/'],
// collectCoverageFrom: [
// '**/*.{js,jsx,ts,tsx}',
// '!**/*.d.ts',
// '!**/index.ts',
// '!**/index.browser.ts',
// '!**/index.headless.ts',
// '!**/index.headless.browser.ts',
// '!**/coverage/**',
// '!**/dist/**',
// '!**/node_modules/**',
// ],
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\](?!(@formkit/auto-animate/react)).+\\.(js|jsx|mjs|cjs|ts|tsx)$'],
moduleDirectories: ['node_modules', '<rootDir>/src'],
moduleNameMapper: {
'@/(.*)': '<rootDir>/src/$1',
},
transform: {
'^.+\\.m?tsx?$': [
'@swc/jest',
{
jsc: {
transform: {
react: {
runtime: 'automatic',
importSource: '@emotion/react',
},
},
},
},
],
'^.+\\.svg$': '<rootDir>/svgTransform.js',
},
};

module.exports = config;
21 changes: 21 additions & 0 deletions packages/clerk-js/jest.jsdom-with-timezone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import JSDOMEnvironment from 'jest-environment-jsdom';

/**
* Timezone-aware jsdom Jest environment. Supports `@timezone` JSDoc
* pragma within test suites to set timezone.
*
* You'd make another copy of this extending the Node environment,
* if needed for Node server environment-based tests.
*/
module.exports = class TimezoneAwareJSDOMEnvironment extends JSDOMEnvironment {
// @ts-ignore
constructor(config, context) {
// Allow test suites to change timezone, even if TZ is passed in a script.
// Falls back to existing TZ environment variable or UTC if no timezone is specified.
// IMPORTANT: This must happen before super(config) is called, otherwise
// it doesn't work.
process.env.TZ = context.docblockPragmas.timezone || process.env.TZ || 'UTC';

super(config, context);
}
};
1 change: 1 addition & 0 deletions packages/clerk-js/jest.setup-after-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@testing-library/jest-dom';
76 changes: 76 additions & 0 deletions packages/clerk-js/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import crypto from 'node:crypto';
import { TextDecoder, TextEncoder } from 'node:util';

import { jest } from '@jest/globals';

class FakeResponse {}

if (typeof window !== 'undefined') {
Object.defineProperties(globalThis, {
TextDecoder: { value: TextDecoder },
TextEncoder: { value: TextEncoder },
Response: { value: FakeResponse },
crypto: { value: crypto.webcrypto },
});

window.ResizeObserver =
window.ResizeObserver ||
jest.fn().mockImplementation(() => ({
disconnect: jest.fn(),
observe: jest.fn(),
unobserve: jest.fn(),
}));

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});

//@ts-expect-error - JSDOM doesn't provide IntersectionObserver, so we mock it for testing
global.IntersectionObserver = class IntersectionObserver {
constructor() {}

disconnect() {
return null;
}

observe() {
return null;
}

takeRecords() {
return null;
}

unobserve() {
return null;
}
};

// Mock HTMLCanvasElement.prototype.getContext to prevent errors
HTMLCanvasElement.prototype.getContext = jest.fn().mockImplementation(((contextType: string) => {
if (contextType === '2d') {
return {
fillRect: jest.fn(),
getImageData: jest.fn(() => ({ data: new Uint8ClampedArray([255, 255, 255, 255]) }) as unknown as ImageData),
} as unknown as CanvasRenderingContext2D;
}
if (contextType === 'webgl' || contextType === 'webgl2') {
return {} as unknown as WebGLRenderingContext;
}
return null;
}) as any) as jest.MockedFunction<HTMLCanvasElement['getContext']>;

// Mock document.elementFromPoint for input-otp library
Object.defineProperty(document, 'elementFromPoint', {
value: jest.fn().mockReturnValue(null),
writable: true,
});
}
8 changes: 7 additions & 1 deletion packages/clerk-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,15 @@
"lint": "eslint src",
"lint:attw": "attw --pack . --profile node16 --ignore-rules named-exports",
"lint:publint": "publint || true",
"test": "vitest --watch=false",
"test": "jest && vitest --watch=false",
"test:cache:clear": "jest --clearCache --useStderr",
"test:ci": "jest --maxWorkers=70%",
"test:coverage": "jest --collectCoverage && open coverage/lcov-report/index.html",
"test:jest": "jest",
"test:sandbox:integration": "playwright test",
"test:sandbox:integration:ui": "playwright test --ui",
"test:sandbox:integration:update-snapshots": "playwright test --update-snapshots",
"test:vitest": "vitest",
"watch": "rspack build --config rspack.config.js --env production --watch"
},
"browserslist": "last 2 years",
Expand Down Expand Up @@ -92,6 +97,7 @@
"@rspack/core": "^1.4.11",
"@rspack/plugin-react-refresh": "^1.5.0",
"@svgr/webpack": "^6.5.1",
"@swc/jest": "0.2.39",
"@types/cloudflare-turnstile": "^0.2.2",
"@types/node": "^22.18.1",
"@types/webpack-env": "^1.18.8",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const mockEnvironmentFetch = vi.fn();
vi.mock('../resources/Client');
vi.mock('../resources/Environment');

// Because Jest, don't ask me why...
vi.mock('../auth/devBrowser', () => ({
createDevBrowser: (): DevBrowser => ({
clear: vi.fn(),
Expand Down
Loading
Loading