diff --git a/.eslintignore b/.eslintignore index 6fbfa158dc..fabaa79cbf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -21,4 +21,3 @@ coverage **/*.d.ts # Scripts scripts/previewEditor/**/* -jest-stare diff --git a/.eslintrc.js b/.eslintrc.js index 5e01410968..a23e6df7c6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,7 +3,7 @@ module.exports = { 'airbnb', 'prettier', 'plugin:compat/recommended', - 'plugin:jest/recommended', + 'plugin:vitest/recommended', 'plugin:react/recommended', 'plugin:import/typescript', 'plugin:markdown/recommended', @@ -12,7 +12,6 @@ module.exports = { browser: true, node: true, jasmine: true, - jest: true, es6: true, }, settings: { @@ -25,7 +24,15 @@ module.exports = { }, }, parser: '@typescript-eslint/parser', - plugins: ['react', '@babel', 'jest', '@typescript-eslint', 'react-hooks', 'unicorn', 'markdown'], + plugins: [ + 'react', + '@babel', + 'vitest', + '@typescript-eslint', + 'react-hooks', + 'unicorn', + 'markdown', + ], // https://github.com/typescript-eslint/typescript-eslint/issues/46#issuecomment-470486034 overrides: [ { @@ -152,8 +159,10 @@ module.exports = { 'scripts/**', '**/*.test.js', '**/__tests__/*', + '__mocks__/**', '*.config.js', '**/*.md', + 'vitest*config.ts', ], }, ], @@ -179,12 +188,12 @@ module.exports = { 'no-restricted-globals': 0, 'max-classes-per-file': 0, - 'jest/no-test-callback': 0, - 'jest/expect-expect': 0, - 'jest/no-done-callback': 0, - 'jest/valid-title': 0, - 'jest/no-conditional-expect': 0, - 'jest/no-standalone-expect': 0, + 'vitest/prefer-to-be': 0, + 'vitest/expect-expect': 0, + 'vitest/no-done-callback': 0, + 'vitest/valid-title': 0, + 'vitest/no-conditional-expect': 0, + 'vitest/no-standalone-expect': 0, 'unicorn/better-regex': 2, 'unicorn/prefer-string-trim-start-end': 2, diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3eb5482b89..767a1e34a7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -191,7 +191,7 @@ jobs: # dom test - name: dom test if: ${{ matrix.module == 'dom' }} - run: npm test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage + run: npm test -- --shard=${{matrix.shard}} --coverage - name: persist coverages if: ${{ matrix.module == 'dom' && matrix.react == '17' }} @@ -347,7 +347,7 @@ jobs: - name: test # lib only run in master branch not in pull request if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }} - run: npm test -- --maxWorkers=2 --shard=${{matrix.shard}} + run: npm test -- --shard=${{matrix.shard}} env: LIB_DIR: ${{ matrix.module }} needs: compile diff --git a/.gitignore b/.gitignore index 7693ccc269..16cbdf1822 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,6 @@ components/version/token-meta.json # Image snapshot diff __diff_output__/ __image_snapshots__/ -/jest-stare /imageSnapshots /imageDiffSnapshots diff --git a/.jest.image.js b/.jest.image.js deleted file mode 100644 index fb42545326..0000000000 --- a/.jest.image.js +++ /dev/null @@ -1,23 +0,0 @@ -const { moduleNameMapper, transformIgnorePatterns } = require('./.jest'); - -// jest config for image snapshots -module.exports = { - setupFiles: ['./tests/setup.js'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'md'], - moduleNameMapper, - transform: { - '\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor', - '\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor', - '\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor', - '\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor', - }, - testRegex: 'image\\.test\\.(j|t)s$', - transformIgnorePatterns, - globals: { - 'ts-jest': { - tsConfigFile: './tsconfig.test.json', - }, - }, - preset: 'jest-puppeteer', - testTimeout: 10000, -}; diff --git a/.jest.js b/.jest.js deleted file mode 100644 index 08ff43ec46..0000000000 --- a/.jest.js +++ /dev/null @@ -1,68 +0,0 @@ -const compileModules = ['react-sticky-box', 'rc-tween-one', '@babel', '@ant-design', 'countup.js']; - -const ignoreList = []; - -// cnpm use `_` as prefix -['', '_'].forEach((prefix) => { - compileModules.forEach((module) => { - ignoreList.push(`${prefix}${module}`); - }); -}); - -const transformIgnorePatterns = [ - // Ignore modules without es dir. - // Update: @babel/runtime should also be transformed - `/node_modules/(?!${ignoreList.join('|')})[^/]+?/(?!(es)/)`, -]; - -function getTestRegex(libDir) { - if (['dist', 'lib', 'es'].includes(libDir)) { - return 'demo\\.test\\.(j|t)sx?$'; - } - return '.*\\.test\\.(j|t)sx?$'; -} - -module.exports = { - verbose: true, - testEnvironment: 'jsdom', - setupFiles: ['./tests/setup.js', 'jest-canvas-mock'], - setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'md'], - modulePathIgnorePatterns: ['/_site/'], - moduleNameMapper: { - '/\\.(css|less)$/': 'identity-obj-proxy', - '^antd$': '/components/index', - '^antd/es/(.*)$': '/components/$1', - }, - testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node', 'image.test.js', 'image.test.ts'], - transform: { - '\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor', - '\\.(m?)js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor', - '\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor', - '\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor', - }, - testRegex: getTestRegex(process.env.LIB_DIR), - collectCoverageFrom: [ - 'components/**/*.{ts,tsx}', - '!components/*/style/index.tsx', - '!components/style/index.tsx', - '!components/*/locale/index.tsx', - '!components/*/__tests__/type.test.tsx', - '!components/**/*/interface.{ts,tsx}', - '!components/*/__tests__/image.test.{ts,tsx}', - '!components/__tests__/node.test.tsx', - '!components/*/demo/*.tsx', - '!components/*/design/**', - ], - transformIgnorePatterns, - globals: { - 'ts-jest': { - tsConfig: './tsconfig.test.json', - }, - }, - testEnvironmentOptions: { - url: 'http://localhost', - }, - // bail: true, - maxWorkers: '50%', -}; diff --git a/.jest.node.js b/.jest.node.js deleted file mode 100644 index 54d6fb852d..0000000000 --- a/.jest.node.js +++ /dev/null @@ -1,19 +0,0 @@ -const { moduleNameMapper, transformIgnorePatterns } = require('./.jest'); - -// jest config for server render environment -module.exports = { - setupFiles: ['./tests/setup.js'], - setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'md'], - moduleNameMapper, - transform: { - '\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor', - '\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor', - '\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor', - '\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor', - }, - testRegex: 'node\\.test\\.(j|t)sx$', - testEnvironment: 'node', - transformIgnorePatterns, - // bail: true, -}; diff --git a/.jest.site.js b/.jest.site.js deleted file mode 100644 index a1f19aee58..0000000000 --- a/.jest.site.js +++ /dev/null @@ -1,21 +0,0 @@ -const { moduleNameMapper, transformIgnorePatterns } = require('./.jest'); - -// jest config for server render environment -module.exports = { - moduleFileExtensions: ['ts', 'tsx', 'js', 'md'], - moduleNameMapper, - transform: { - '\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor', - '\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor', - '\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor', - '\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor', - }, - testRegex: 'check-site\\.(j|t)s$', - testEnvironment: 'node', - transformIgnorePatterns, - globals: { - 'ts-jest': { - tsConfigFile: './tsconfig.test.json', - }, - }, -}; diff --git a/tests/__mocks__/@rc-component/trigger.tsx b/__mocks__/@rc-component/trigger.tsx similarity index 65% rename from tests/__mocks__/@rc-component/trigger.tsx rename to __mocks__/@rc-component/trigger.tsx index b9c9d5c88d..c199b787d4 100644 --- a/tests/__mocks__/@rc-component/trigger.tsx +++ b/__mocks__/@rc-component/trigger.tsx @@ -1,16 +1,17 @@ import type { TriggerProps } from '@rc-component/trigger'; -import MockTrigger from '@rc-component/trigger/lib/mock'; +import MockTrigger from '@rc-component/trigger/es/mock'; import * as React from 'react'; -import { TriggerMockContext } from '../../shared/demoTestContext'; +import { TriggerMockContext } from '../../tests/shared/demoTestContext'; -let OriginTrigger = jest.requireActual('@rc-component/trigger'); -OriginTrigger = OriginTrigger.default ?? OriginTrigger; +const { default: OriginTrigger } = await vi.importActual( + '@rc-component/trigger', +); const ForwardTrigger = React.forwardRef((props, ref) => { const context = React.useContext(TriggerMockContext); const mergedPopupVisible = context?.popupVisible ?? props.popupVisible; - (global as any).triggerProps = props; + globalThis.triggerProps = props; const mergedProps = { ...props, diff --git a/tests/__mocks__/copy-to-clipboard.ts b/__mocks__/copy-to-clipboard.ts similarity index 100% rename from tests/__mocks__/copy-to-clipboard.ts rename to __mocks__/copy-to-clipboard.ts diff --git a/tests/__mocks__/rc-util/lib/Portal.tsx b/__mocks__/rc-util/es/Portal.tsx similarity index 78% rename from tests/__mocks__/rc-util/lib/Portal.tsx rename to __mocks__/rc-util/es/Portal.tsx index 9fe393670f..2858c6319f 100644 --- a/tests/__mocks__/rc-util/lib/Portal.tsx +++ b/__mocks__/rc-util/es/Portal.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import { TriggerMockContext } from '../../../shared/demoTestContext'; +import { TriggerMockContext } from '../../../tests/shared/demoTestContext'; + +const { default: OriginPortal } = await vi.importActual('rc-util/es/Portal'); -let OriginPortal = jest.requireActual('rc-util/lib/Portal'); -OriginPortal = OriginPortal.default ?? OriginPortal; class MockPortal extends React.Component<{ children?: React.ReactNode }> { container: boolean; diff --git a/__mocks__/rc-virtual-list.ts b/__mocks__/rc-virtual-list.ts new file mode 100644 index 0000000000..e99eeb9b9f --- /dev/null +++ b/__mocks__/rc-virtual-list.ts @@ -0,0 +1,3 @@ +import Mock from 'rc-virtual-list/es/mock'; + +export default Mock; diff --git a/components/__tests__/__snapshots__/index.test.ts.snap b/components/__tests__/__snapshots__/index.test.ts.snap index 8572ab796f..a6f6d0399c 100644 --- a/components/__tests__/__snapshots__/index.test.ts.snap +++ b/components/__tests__/__snapshots__/index.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`antd exports modules correctly 1`] = ` +exports[`antd > exports modules correctly 1`] = ` [ "Affix", "Alert", @@ -37,7 +37,9 @@ exports[`antd exports modules correctly 1`] = ` "List", "Mentions", "Menu", + "message", "Modal", + "notification", "Pagination", "Popconfirm", "Popover", @@ -59,6 +61,7 @@ exports[`antd exports modules correctly 1`] = ` "Table", "Tabs", "Tag", + "theme", "TimePicker", "Timeline", "Tooltip", @@ -68,10 +71,7 @@ exports[`antd exports modules correctly 1`] = ` "TreeSelect", "Typography", "Upload", - "Watermark", - "message", - "notification", - "theme", "version", + "Watermark", ] `; diff --git a/components/__tests__/__snapshots__/setup.test.tsx.snap b/components/__tests__/__snapshots__/setup.test.tsx.snap index f2e4db6141..c3c40d4094 100644 --- a/components/__tests__/__snapshots__/setup.test.tsx.snap +++ b/components/__tests__/__snapshots__/setup.test.tsx.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`SetUp.Test diff of React 18 & React 17 1`] = ` +exports[`SetUp.Test > diff of React 18 & React 17 1`] = ` NodeList [
bamboo diff --git a/components/__tests__/index.test.ts b/components/__tests__/index.test.ts index 9ab8d246ba..8364ed2743 100644 --- a/components/__tests__/index.test.ts +++ b/components/__tests__/index.test.ts @@ -1,6 +1,7 @@ +import * as antd from '..'; + const OLD_NODE_ENV = process.env.NODE_ENV; process.env.NODE_ENV = 'development'; -const antd = require('..'); describe('antd', () => { afterAll(() => { diff --git a/components/__tests__/node.test.tsx b/components/__tests__/node.test.tsx index cc63dd37e4..d18c98156f 100644 --- a/components/__tests__/node.test.tsx +++ b/components/__tests__/node.test.tsx @@ -3,21 +3,24 @@ import * as React from 'react'; import { renderToString } from 'react-dom/server'; import type { Options } from '../../tests/shared/demoTest'; -(global as any).testConfig = {}; +globalThis.testConfig = {}; -jest.mock('../../tests/shared/demoTest', () => { +vi.mock('../../tests/shared/demoTest', () => { function fakeDemoTest(name: string, option: Options = {}) { - (global as any).testConfig[name] = option; + globalThis.testConfig[name] = option; } fakeDemoTest.rootPropsTest = () => {}; - return fakeDemoTest; + return { + default: fakeDemoTest, + rootPropsTest: () => {}, + }; }); describe('node', () => { beforeAll(() => { - jest.useFakeTimers().setSystemTime(new Date('2016-11-22')); + vi.useFakeTimers().setSystemTime(new Date('2016-11-22')); }); // Find the component exist demo test file @@ -27,23 +30,27 @@ describe('node', () => { const componentName = componentTestFile.match(/components\/([^/]*)\//)![1]; // Test for ssr - describe(componentName, () => { + // eslint-disable-next-line vitest/valid-describe-callback + describe(componentName, async () => { const demoList = globSync(`./components/${componentName}/demo/*.tsx`); // Use mock to get config - require(`../../${componentTestFile}`); // eslint-disable-line global-require, import/no-dynamic-require - const option = (global as any).testConfig?.[componentName]; - + await import(`../../${componentTestFile}`); demoList.forEach((demoFile) => { + const option = globalThis.testConfig?.[componentName]; const skip: string[] = option?.skip || []; const test = skip.some((skipMarkdown) => demoFile.includes(skipMarkdown)) ? it.skip : it; - test(demoFile, () => { - const Demo = require(`../../${demoFile}`).default; // eslint-disable-line global-require, import/no-dynamic-require - expect(() => { - renderToString(); - }).not.toThrow(); - }); + test( + demoFile, + async () => { + const Demo = (await import(`../../${demoFile}`)).default; + expect(() => { + renderToString(); + }).not.toThrow(); + }, + 15000, + ); }); }); }); diff --git a/components/_util/PurePanel.tsx b/components/_util/PurePanel.tsx index 49e8b2c16e..39e5e2e991 100644 --- a/components/_util/PurePanel.tsx +++ b/components/_util/PurePanel.tsx @@ -7,7 +7,7 @@ export interface BaseProps { style?: React.CSSProperties; } -/* istanbul ignore next */ +/* c8 ignore start */ export default function genPurePanel( Component: any, defaultPrefixCls?: string, @@ -96,3 +96,4 @@ export default function genPurePanel( ); } as typeof Component; } +/* c8 ignore stop */ diff --git a/components/_util/__tests__/getScroll.test.ts b/components/_util/__tests__/getScroll.test.ts index 8cb737ff1a..0eb77443ad 100644 --- a/components/_util/__tests__/getScroll.test.ts +++ b/components/_util/__tests__/getScroll.test.ts @@ -7,7 +7,7 @@ describe('getScroll', () => { }); it('getScroll window', async () => { - const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => { + const scrollToSpy = vi.spyOn(window, 'scrollTo').mockImplementation((x, y) => { window.pageXOffset = x; window.pageYOffset = y; }); @@ -18,7 +18,7 @@ describe('getScroll', () => { }); it('getScroll document', async () => { - const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => { + const scrollToSpy = vi.spyOn(window, 'scrollTo').mockImplementation((x, y) => { document.documentElement.scrollLeft = x; document.documentElement.scrollTop = y; }); @@ -30,7 +30,7 @@ describe('getScroll', () => { it('getScroll div', async () => { const div = document.createElement('div'); - const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => { + const scrollToSpy = vi.spyOn(window, 'scrollTo').mockImplementation((x, y) => { div.scrollLeft = x; div.scrollTop = y; }); @@ -42,7 +42,7 @@ describe('getScroll', () => { it('getScroll documentElement', async () => { const div: any = {}; - const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => { + const scrollToSpy = vi.spyOn(window, 'scrollTo').mockImplementation((x, y) => { div.scrollLeft = null; div.scrollTop = null; div.documentElement = {}; diff --git a/components/_util/__tests__/responsiveObserve.test.tsx b/components/_util/__tests__/responsiveObserve.test.tsx index d88c5ea606..b47595d4cd 100644 --- a/components/_util/__tests__/responsiveObserve.test.tsx +++ b/components/_util/__tests__/responsiveObserve.test.tsx @@ -11,7 +11,7 @@ describe('Test ResponsiveObserve', () => { return null; }; render(); - const subscribeFunc = jest.fn(); + const subscribeFunc = vi.fn(); const token = responsiveObserveRef.subscribe(subscribeFunc); expect( responsiveObserveRef.matchHandlers[responsiveObserveRef.responsiveMap.xs].mql.matches, diff --git a/components/_util/__tests__/scrollTo.test.ts b/components/_util/__tests__/scrollTo.test.ts index 5fe2e09502..a6e7533c1c 100644 --- a/components/_util/__tests__/scrollTo.test.ts +++ b/components/_util/__tests__/scrollTo.test.ts @@ -2,10 +2,10 @@ import { waitFakeTimer } from '../../../tests/utils'; import scrollTo from '../scrollTo'; describe('Test ScrollTo function', () => { - const dateNowMock = jest.spyOn(Date, 'now'); + const dateNowMock = vi.spyOn(Date, 'now'); beforeAll(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); beforeEach(() => { @@ -13,16 +13,16 @@ describe('Test ScrollTo function', () => { }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); afterEach(() => { - jest.clearAllTimers(); + vi.clearAllTimers(); dateNowMock.mockClear(); }); it('test scrollTo', async () => { - const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => { + const scrollToSpy = vi.spyOn(window, 'scrollTo').mockImplementation((_, y) => { window.scrollY = y; window.pageYOffset = y; }); @@ -36,7 +36,7 @@ describe('Test ScrollTo function', () => { }); it('test callback - option', async () => { - const cbMock = jest.fn(); + const cbMock = vi.fn(); scrollTo(1000, { callback: cbMock, }); diff --git a/components/_util/__tests__/util.test.tsx b/components/_util/__tests__/util.test.tsx index 6b80428474..7e806a606f 100644 --- a/components/_util/__tests__/util.test.tsx +++ b/components/_util/__tests__/util.test.tsx @@ -9,19 +9,19 @@ import TransButton from '../transButton'; describe('Test utils function', () => { describe('throttle', () => { beforeAll(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); afterEach(() => { - jest.clearAllTimers(); + vi.clearAllTimers(); }); it('throttle function should work', async () => { - const callback = jest.fn(); + const callback = vi.fn(); const throttled = throttleByAnimationFrame(callback); expect(callback).not.toHaveBeenCalled(); @@ -34,7 +34,7 @@ describe('Test utils function', () => { }); it('throttle function should be canceled', async () => { - const callback = jest.fn(); + const callback = vi.fn(); const throttled = throttleByAnimationFrame(callback); throttled(); @@ -54,7 +54,7 @@ describe('Test utils function', () => { }); it('should trigger onClick when press enter', () => { - const onClick = jest.fn(); + const onClick = vi.fn(); const { container } = render(TransButton); @@ -75,7 +75,7 @@ describe('Test utils function', () => { }); it('isStyleSupport return false in service side', () => { - const spy = jest + const spy = vi .spyOn(window.document, 'documentElement', 'get') .mockImplementation(() => undefined as unknown as HTMLElement); expect(isStyleSupport('color')).toBe(false); diff --git a/components/_util/__tests__/warning.test.ts b/components/_util/__tests__/warning.test.ts index 00a952bd49..377d3eaf69 100644 --- a/components/_util/__tests__/warning.test.ts +++ b/components/_util/__tests__/warning.test.ts @@ -1,8 +1,10 @@ +import type { SpyInstance } from 'vitest'; + describe('Test warning', () => { - let spy: jest.SpyInstance; + let spy: SpyInstance; beforeAll(() => { - spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + spy = vi.spyOn(console, 'error').mockImplementation(() => {}); }); afterAll(() => { @@ -10,7 +12,7 @@ describe('Test warning', () => { }); beforeEach(() => { - jest.resetModules(); + vi.resetModules(); }); afterEach(() => { diff --git a/components/_util/__tests__/wave.test.tsx b/components/_util/__tests__/wave.test.tsx index b6829855b1..4ad0abe81e 100644 --- a/components/_util/__tests__/wave.test.tsx +++ b/components/_util/__tests__/wave.test.tsx @@ -1,13 +1,15 @@ import React from 'react'; import mountTest from '../../../tests/shared/mountTest'; -import { render, fireEvent, getByText, waitFakeTimer, act } from '../../../tests/utils'; +import { act, fireEvent, getByText, render, waitFakeTimer } from '../../../tests/utils'; import Wave from '../wave'; -(global as any).isVisible = true; +let isVisible = vi.hoisted(() => true); -jest.mock('rc-util/lib/Dom/isVisible', () => { - const mockFn = () => (global as any).isVisible; - return mockFn; +vi.mock('rc-util/es/Dom/isVisible', () => { + const mockFn = () => isVisible; + return { + default: mockFn, + }; }); describe('Wave component', () => { @@ -29,44 +31,37 @@ describe('Wave component', () => { } (window as any).ResizeObserver = FakeResizeObserver; - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); expect(obCnt).not.toBe(0); expect(disCnt).not.toBe(0); }); beforeEach(() => { - (global as any).isVisible = true; + isVisible = true; document.body.innerHTML = ''; }); - afterEach(() => { - jest.clearAllTimers(); + afterEach(async () => { + await vi.runAllTimersAsync(); + vi.clearAllTimers(); const styles = document.getElementsByTagName('style'); for (let i = 0; i < styles.length; i += 1) { styles[i].remove(); } }); - function getWaveStyle() { - const styleObj: Record = {}; + function getWaveColor() { const { style } = document.querySelector('.ant-wave')!; - style.cssText.split(';').forEach((kv) => { - if (kv.trim()) { - const cells = kv.split(':'); - styleObj[cells[0].trim()] = cells[1].trim(); - } - }); - - return styleObj; + return style.getPropertyValue('--wave-color'); } function waitRaf() { act(() => { - jest.advanceTimersByTime(100); + vi.advanceTimersByTime(100); }); } @@ -90,7 +85,7 @@ describe('Wave component', () => { }); it('invisible in screen', () => { - (global as any).isVisible = false; + isVisible = false; const { container, unmount } = render( @@ -119,9 +114,7 @@ describe('Wave component', () => { fireEvent.click(container.querySelector('button')!); waitRaf(); - const style = getWaveStyle(); - - expect(style['--wave-color']).toBeFalsy(); + expect(getWaveColor()).toBeFalsy(); unmount(); }); @@ -129,7 +122,7 @@ describe('Wave component', () => { it('wave color is not grey', () => { const { container, unmount } = render( - , @@ -138,8 +131,7 @@ describe('Wave component', () => { fireEvent.click(container.querySelector('button')!); waitRaf(); - const style = getWaveStyle(); - expect(style['--wave-color']).toEqual('red'); + expect(getWaveColor()).toEqual('rgb(255, 0, 0)'); unmount(); }); @@ -147,15 +139,14 @@ describe('Wave component', () => { it('read wave color from border-top-color', () => { const { container, unmount } = render( -
button
+
button
, ); fireEvent.click(getByText(container, 'button')!); waitRaf(); - const style = getWaveStyle(); - expect(style['--wave-color']).toEqual('blue'); + expect(getWaveColor()).toEqual('rgb(0, 0, 255)'); unmount(); }); @@ -163,15 +154,14 @@ describe('Wave component', () => { it('read wave color from background color', () => { const { container, unmount } = render( -
button
+
button
, ); fireEvent.click(getByText(container, 'button')!); waitRaf(); - const style = getWaveStyle(); - expect(style['--wave-color']).toEqual('green'); + expect(getWaveColor()).toEqual('rgb(0, 128, 0)'); unmount(); }); @@ -179,15 +169,14 @@ describe('Wave component', () => { it('read wave color from border firstly', () => { const { container, unmount } = render( -
button
+
button
, ); fireEvent.click(getByText(container, 'button')!); waitRaf(); - const style = getWaveStyle(); - expect(style['--wave-color']).toEqual('yellow'); + expect(getWaveColor()).toEqual('rgb(255, 0, 0)'); unmount(); }); @@ -222,7 +211,7 @@ describe('Wave component', () => { }); it('not show when hidden', () => { - (global as any).isVisible = false; + isVisible = false; const { container } = render( @@ -271,7 +260,7 @@ describe('Wave component', () => { it('wave color should inferred if border is transparent and background is not', () => { const { container, unmount } = render( - , @@ -279,8 +268,7 @@ describe('Wave component', () => { fireEvent.click(container.querySelector('button')!); waitRaf(); - const style = getWaveStyle(); - expect(style['--wave-color']).toEqual('red'); + expect(getWaveColor()).toEqual('rgb(255, 0, 0)'); unmount(); }); @@ -297,8 +285,7 @@ describe('Wave component', () => { fireEvent.click(container.querySelector('button')!); waitRaf(); - const style = getWaveStyle(); - expect(style['--wave-color']).toEqual('red'); + expect(getWaveColor()).toEqual('red'); unmount(); }); diff --git a/components/affix/__tests__/Affix.test.tsx b/components/affix/__tests__/Affix.test.tsx index cbe9b44815..7d2552e6fb 100644 --- a/components/affix/__tests__/Affix.test.tsx +++ b/components/affix/__tests__/Affix.test.tsx @@ -22,7 +22,7 @@ const AffixMounter: React.FC = ({ getInstance, ...restProps }) => { const container = useRef(null); useEffect(() => { if (container.current) { - container.current.addEventListener = jest + container.current.addEventListener = vi .fn() .mockImplementation((event: keyof HTMLElementEventMap, cb: (ev: Event) => void) => { events[event] = cb; @@ -42,12 +42,12 @@ describe('Affix Render', () => { rtlTest(Affix); accessibilityTest(Affix); - const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect'); + const domMock = vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect'); const classRect: Record = { container: { top: 0, bottom: 100 } as DOMRect }; beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); beforeAll(() => { @@ -57,8 +57,8 @@ describe('Affix Render', () => { }); afterEach(() => { - jest.useRealTimers(); - jest.clearAllTimers(); + vi.useRealTimers(); + vi.clearAllTimers(); }); afterAll(() => { @@ -89,7 +89,11 @@ describe('Affix Render', () => { }); it('Anchor correct render when target is null', async () => { - render( null}>test); + render( + null}> + test + , + ); await waitFakeTimer(); }); @@ -109,7 +113,7 @@ describe('Affix Render', () => { }); it('updatePosition when offsetTop changed', async () => { - const onChange = jest.fn(); + const onChange = vi.fn(); const { container, rerender } = render(); await waitFakeTimer(); @@ -228,7 +232,7 @@ describe('Affix Render', () => { '.fixed', // outer ].forEach((selector) => { it(`trigger listener when size change: ${selector}`, async () => { - const updateCalled = jest.fn(); + const updateCalled = vi.fn(); const { container } = render( , { diff --git a/components/affix/__tests__/__snapshots__/Affix.test.tsx.snap b/components/affix/__tests__/__snapshots__/Affix.test.tsx.snap index bd230c7ac4..1d68a8068f 100644 --- a/components/affix/__tests__/__snapshots__/Affix.test.tsx.snap +++ b/components/affix/__tests__/__snapshots__/Affix.test.tsx.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Affix Render rtl render component should be rendered correctly in RTL direction 1`] = ` +exports[`Affix Render > rtl render > component should be rendered correctly in RTL direction 1`] = `
custom action 1`] = `
`; -exports[`Alert rtl render component should be rendered correctly in RTL direction 1`] = ` +exports[`Alert > rtl render > component should be rendered correctly in RTL direction 1`] = `
{ accessibilityTest(Alert); beforeAll(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it('should show close button and could be closed', async () => { - const onClose = jest.fn(); + const onClose = vi.fn(); render( { await userEvent.click(screen.getByRole('button', { name: /close/i })); - act(() => { - jest.runAllTimers(); - }); - expect(onClose).toHaveBeenCalledTimes(1); }); @@ -76,7 +72,7 @@ describe('Alert', () => { }); it('should show error as ErrorBoundary when children have error', () => { - const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const warnSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); expect(warnSpy).toHaveBeenCalledTimes(0); // @ts-expect-error // eslint-disable-next-line react/jsx-no-undef @@ -105,11 +101,9 @@ describe('Alert', () => { await userEvent.hover(screen.getByRole('alert')); - act(() => { - jest.runAllTimers(); + await waitFor(() => { + expect(screen.getByRole('tooltip')).toBeInTheDocument(); }); - - expect(screen.getByRole('tooltip')).toBeInTheDocument(); }); it('could be used with Popconfirm', async () => { @@ -123,10 +117,6 @@ describe('Alert', () => { ); await userEvent.click(screen.getByRole('alert')); - act(() => { - jest.runAllTimers(); - }); - expect(screen.getByRole('tooltip')).toBeInTheDocument(); }); diff --git a/components/anchor/__tests__/Anchor.test.tsx b/components/anchor/__tests__/Anchor.test.tsx index 51cf897264..bd253574f6 100644 --- a/components/anchor/__tests__/Anchor.test.tsx +++ b/components/anchor/__tests__/Anchor.test.tsx @@ -2,6 +2,7 @@ import { resetWarned } from 'rc-util/lib/warning'; import React, { useState } from 'react'; import scrollIntoView from 'scroll-into-view-if-needed'; +import type { SpyInstance } from 'vitest'; import Anchor from '..'; import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils'; import Button from '../../button'; @@ -9,8 +10,9 @@ import type { AnchorDirection } from '../Anchor'; const { Link } = Anchor; -function createDiv() { +function createRootDiv() { const root = document.createElement('div'); + root.className = 'root'; document.body.appendChild(root); return root; } @@ -18,18 +20,16 @@ function createDiv() { let idCounter = 0; const getHashUrl = () => `Anchor-API-${idCounter++}`; -jest.mock('scroll-into-view-if-needed', () => jest.fn()); +vi.mock('scroll-into-view-if-needed', () => ({ + default: vi.fn(), +})); describe('Anchor Render', () => { - const getBoundingClientRectMock = jest.spyOn( - HTMLHeadingElement.prototype, - 'getBoundingClientRect', - ); - const getClientRectsMock = jest.spyOn(HTMLHeadingElement.prototype, 'getClientRects'); - const scrollIntoViewMock = jest.createMockFromModule('scroll-into-view-if-needed'); + const getBoundingClientRectMock = vi.spyOn(HTMLHeadingElement.prototype, 'getBoundingClientRect'); + const getClientRectsMock = vi.spyOn(HTMLHeadingElement.prototype, 'getClientRects'); beforeAll(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); getBoundingClientRectMock.mockReturnValue({ width: 100, height: 100, @@ -39,18 +39,18 @@ describe('Anchor Render', () => { }); beforeEach(() => { - jest.useFakeTimers(); - scrollIntoViewMock.mockReset(); + vi.useFakeTimers(); + vi.mocked(scrollIntoView).mockReset(); }); afterEach(() => { - jest.clearAllTimers(); - jest.useRealTimers(); + vi.clearAllTimers(); + vi.useRealTimers(); }); afterAll(() => { - jest.clearAllTimers(); - jest.useRealTimers(); + vi.clearAllTimers(); + vi.useRealTimers(); getBoundingClientRectMock.mockRestore(); getClientRectsMock.mockRestore(); }); @@ -186,12 +186,12 @@ describe('Anchor Render', () => { const link = container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!; fireEvent.click(link); await waitFakeTimer(); - expect(link.classList).toContain('ant-anchor-link-title-active'); + expect(link.classList.contains('ant-anchor-link-title-active')).toBeTruthy(); }); it('scrolls the page when clicking a link', async () => { - const root = createDiv(); - const scrollToSpy = jest.spyOn(window, 'scrollTo'); + const root = createRootDiv(); + const scrollToSpy = vi.spyOn(window, 'scrollTo'); render(
Q1
, { container: root }); const { container } = render( , @@ -200,13 +200,14 @@ describe('Anchor Render', () => { fireEvent.click(link); await waitFakeTimer(); expect(scrollToSpy).toHaveBeenCalled(); + scrollToSpy.mockRestore(); }); it('handleScroll should not be triggered when scrolling caused by clicking a link', async () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); - const root = createDiv(); - const onChange = jest.fn(); + const root = createRootDiv(); + const onChange = vi.fn(); render(
Hello
@@ -265,8 +266,8 @@ describe('Anchor Render', () => { it('targetOffset prop', async () => { const hash = getHashUrl(); - const scrollToSpy = jest.spyOn(window, 'scrollTo'); - const root = createDiv(); + const scrollToSpy = vi.spyOn(window, 'scrollTo'); + const root = createRootDiv(); render(

Hello

, { container: root }); const { container, rerender } = render( , @@ -290,14 +291,16 @@ describe('Anchor Render', () => { fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!); await waitFakeTimer(); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); + + scrollToSpy.mockRestore(); }); // https://github.com/ant-design/ant-design/issues/31941 it('targetOffset prop when contain spaces', async () => { const hash = `${getHashUrl()} s p a c e s`; - const scrollToSpy = jest.spyOn(window, 'scrollTo'); - const root = createDiv(); + const scrollToSpy = vi.spyOn(window, 'scrollTo'); + const root = createRootDiv(); render(

Hello

, { container: root }); const { container, rerender } = render( , @@ -319,6 +322,8 @@ describe('Anchor Render', () => { fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!); await waitFakeTimer(); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); + + scrollToSpy.mockRestore(); }); it('onClick event', () => { @@ -347,7 +352,7 @@ describe('Anchor Render', () => { it('onChange event', () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); - const onChange = jest.fn(); + const onChange = vi.fn(); const { container } = render( { const hash1 = getHashUrl(); const hash2 = getHashUrl(); - const beforeFn = jest.fn(); - const afterFn = jest.fn(); + const beforeFn = vi.fn(); + const afterFn = vi.fn(); const Demo: React.FC = () => { const [trigger, setTrigger] = useState(false); @@ -435,8 +440,8 @@ describe('Anchor Render', () => { getBoundingClientRectMock.mockReturnValue({ width: 0, height: 0, top: 1000 } as DOMRect); const hash = getHashUrl(); - const scrollToSpy = jest.spyOn(window, 'scrollTo'); - const root = createDiv(); + const scrollToSpy = vi.spyOn(window, 'scrollTo'); + const root = createRootDiv(); render(

Hello

, { container: root }); const { container, rerender } = render( @@ -470,13 +475,14 @@ describe('Anchor Render', () => { height: 100, top: 1000, } as DOMRect); + + scrollToSpy.mockRestore(); }); it('test edge case when container is not windows', async () => { const hash = getHashUrl(); - const scrollToSpy = jest.spyOn(window, 'scrollTo'); - const root = createDiv(); + const root = createRootDiv(); render(

Hello

, { container: root }); const { container, rerender } = render( @@ -492,19 +498,23 @@ describe('Anchor Render', () => {
, ); + // Since it's mock rect pos here. We need always reset the `scrollTop` for test + document.body.scrollTop = 0; fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!); await waitFakeTimer(); - expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); + expect(document.body.scrollTop).toEqual(1000); + document.body.scrollTop = 0; setProps({ offsetTop: 100 }); fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!); await waitFakeTimer(); - expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); + expect(document.body.scrollTop).toEqual(900); + document.body.scrollTop = 0; setProps({ targetOffset: 200 }); fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!); await waitFakeTimer(); - expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); + expect(document.body.scrollTop).toEqual(800); }); describe('getCurrentAnchor', () => { @@ -529,7 +539,7 @@ describe('Anchor Render', () => { it('should trigger onChange when have getCurrentAnchor', () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); - const onChange = jest.fn(); + const onChange = vi.fn(); const { container } = render( { it('getCurrentAnchor have default link as argument', () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); - const getCurrentAnchor = jest.fn(); + const getCurrentAnchor = vi.fn(); const { container } = render( { describe('scroll x', () => { it('targetOffset horizontal', async () => { const hash = getHashUrl(); - const scrollToSpy = jest.spyOn(window, 'scrollTo'); - const root = createDiv(); + const scrollToSpy = vi.spyOn(window, 'scrollTo'); + const root = createRootDiv(); render(

Hello

, { container: root }); const { container, rerender } = render( { fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!); await waitFakeTimer(); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); + + scrollToSpy.mockRestore(); }); }); @@ -756,12 +768,12 @@ describe('Anchor Render', () => { const link = container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!; fireEvent.click(link); await waitFakeTimer(); - expect(link.classList).toContain('ant-anchor-link-title-active'); + expect(link.classList.contains('ant-anchor-link-title-active')).toBeTruthy(); }); it('scrolls the page when clicking a link', async () => { - const root = createDiv(); - const scrollToSpy = jest.spyOn(window, 'scrollTo'); + const root = createRootDiv(); + const scrollToSpy = vi.spyOn(window, 'scrollTo'); render(
Q1
, { container: root }); const { container } = render( @@ -772,13 +784,15 @@ describe('Anchor Render', () => { fireEvent.click(link); await waitFakeTimer(); expect(scrollToSpy).toHaveBeenCalled(); + + scrollToSpy.mockRestore(); }); it('handleScroll should not be triggered when scrolling caused by clicking a link', async () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); - const root = createDiv(); - const onChange = jest.fn(); + const root = createRootDiv(); + const onChange = vi.fn(); render(
Hello
@@ -851,10 +865,10 @@ describe('Anchor Render', () => { }); describe('warning', () => { - let errSpy: jest.SpyInstance; + let errSpy: SpyInstance; beforeEach(() => { resetWarned(); - errSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + errSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { @@ -971,14 +985,14 @@ describe('Anchor Render', () => { const toggleButton = wrapper.container.querySelector('button')!; fireEvent.click(toggleButton); - act(() => jest.runAllTimers()); + act(() => vi.runAllTimers()); expect(!!ink.style.left).toBe(true); expect(!!ink.style.width).toBe(true); expect(ink.style.top).toBe(''); expect(ink.style.height).toBe(''); fireEvent.click(toggleButton); - act(() => jest.runAllTimers()); + act(() => vi.runAllTimers()); expect(!!ink.style.top).toBe(true); expect(!!ink.style.height).toBe(true); expect(ink.style.left).toBe(''); diff --git a/components/anchor/__tests__/__snapshots__/Anchor.test.tsx.snap b/components/anchor/__tests__/__snapshots__/Anchor.test.tsx.snap index aa6da33ae7..0a030f440b 100644 --- a/components/anchor/__tests__/__snapshots__/Anchor.test.tsx.snap +++ b/components/anchor/__tests__/__snapshots__/Anchor.test.tsx.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Anchor Render render items and ignore jsx children 1`] = ` +exports[`Anchor Render > render items and ignore jsx children 1`] = `
`; -exports[`Anchor Render renders items correctly 1`] = ` +exports[`Anchor Render > renders items correctly 1`] = `
`; -exports[`Anchor Render renders items correctly#horizontal 1`] = ` +exports[`Anchor Render > renders items correctly#horizontal 1`] = `
rtl render > component should be rendered correctly in RTL direction 1`] = `
`; -exports[`App single 1`] = ` +exports[`App > single 1`] = `
diff --git a/components/app/__tests__/index.test.tsx b/components/app/__tests__/index.test.tsx index c738a60a20..cf58ca84b3 100644 --- a/components/app/__tests__/index.test.tsx +++ b/components/app/__tests__/index.test.tsx @@ -11,12 +11,12 @@ describe('App', () => { rtlTest(App); beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterEach(() => { - jest.clearAllTimers(); - jest.useRealTimers(); + vi.clearAllTimers(); + vi.useRealTimers(); }); it('single', () => { @@ -134,10 +134,12 @@ describe('App', () => { it('support style', () => { const { container } = render( - +
test
, ); - expect(container.querySelector('.ant-app')).toHaveStyle('color: blue;'); + expect(container.querySelector('.ant-app')).toHaveStyle( + 'color: rgb(255, 0, 0);', + ); }); }); diff --git a/components/auto-complete/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/auto-complete/__tests__/__snapshots__/demo-extend.test.ts.snap index 18443f6349..f4a2dc307a 100644 --- a/components/auto-complete/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/auto-complete/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`renders components/auto-complete/demo/basic.tsx extend context correctly 1`] = ` Array [ @@ -235,7 +235,7 @@ exports[`renders components/auto-complete/demo/certain-category.tsx extend conte >
rtl render > component should be rendered correctly in RTL direction 1`] = `