refactor: front-end testing with vitest (#1900)

* refactor: front-end testing with vitest

* fix: fix build failed
This commit is contained in:
被雨水过滤的空气-Rairn 2023-05-25 10:40:08 +08:00 committed by GitHub
parent c603108542
commit 49117afb72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 837 additions and 87 deletions

View File

@ -53,7 +53,7 @@ jobs:
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
- name: Test with Sqlite - name: Test with Sqlite
run: yarn nocobase install -f && yarn test run: yarn nocobase install -f && yarn test && yarn test:client
env: env:
DB_DIALECT: sqlite DB_DIALECT: sqlite
DB_STORAGE: /tmp/db.sqlite DB_STORAGE: /tmp/db.sqlite
@ -94,7 +94,7 @@ jobs:
- run: yarn install - run: yarn install
# - run: yarn build # - run: yarn build
- name: Test with postgres - name: Test with postgres
run: yarn nocobase install -f && yarn test run: yarn nocobase install -f && yarn test && yarn test:client
env: env:
DB_DIALECT: postgres DB_DIALECT: postgres
DB_HOST: postgres DB_HOST: postgres
@ -131,7 +131,7 @@ jobs:
- run: yarn install - run: yarn install
# - run: yarn build # - run: yarn build
- name: Test with MySQL - name: Test with MySQL
run: yarn nocobase install -f && yarn test run: yarn nocobase install -f && yarn test && yarn test:client
env: env:
DB_DIALECT: mysql DB_DIALECT: mysql
DB_HOST: mysql DB_HOST: mysql

View File

@ -69,7 +69,7 @@ jobs:
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
- name: Test with postgres - name: Test with postgres
run: yarn nocobase install -f && yarn test run: yarn nocobase install -f && yarn test && yarn test:client
env: env:
DB_DIALECT: postgres DB_DIALECT: postgres
DB_HOST: postgres DB_HOST: postgres

View File

@ -8,7 +8,7 @@ module.exports = {
verbose: true, verbose: true,
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
preset: 'ts-jest', preset: 'ts-jest',
testMatch: ['**/__tests__/**/*.test.[jt]s?(x)'], testMatch: ['**/__tests__/**/*.test.[jt]s'],
setupFiles: ['./jest.setup.ts'], setupFiles: ['./jest.setup.ts'],
setupFilesAfterEnv: [require.resolve('jest-dom/extend-expect'), './jest.setupAfterEnv.ts'], setupFilesAfterEnv: [require.resolve('jest-dom/extend-expect'), './jest.setupAfterEnv.ts'],
moduleNameMapper: { moduleNameMapper: {
@ -24,7 +24,7 @@ module.exports = {
diagnostics: false, diagnostics: false,
}, },
}, },
modulePathIgnorePatterns: ['/esm/', '/es/', '/dist/', '/lib/'], modulePathIgnorePatterns: ['/esm/', '/es/', '/dist/', '/lib/', '/client/', '/sdk/', '\\.test\\.tsx$'],
// add .mjs .cjs for formula.js // add .mjs .cjs for formula.js
moduleFileExtensions: [...defaults.moduleFileExtensions, 'mjs', 'cjs'], moduleFileExtensions: [...defaults.moduleFileExtensions, 'mjs', 'cjs'],
coveragePathIgnorePatterns: [ coveragePathIgnorePatterns: [

View File

@ -18,6 +18,7 @@
"start": "nocobase start", "start": "nocobase start",
"build": "nocobase build", "build": "nocobase build",
"test": "nocobase test", "test": "nocobase test",
"test:client": "vitest",
"doc": "nocobase doc", "doc": "nocobase doc",
"postinstall": "nocobase postinstall", "postinstall": "nocobase postinstall",
"lint": "eslint .", "lint": "eslint .",
@ -44,11 +45,16 @@
"@commitlint/cli": "^16.1.0", "@commitlint/cli": "^16.1.0",
"@commitlint/config-conventional": "^16.0.0", "@commitlint/config-conventional": "^16.0.0",
"@commitlint/prompt-cli": "^16.1.0", "@commitlint/prompt-cli": "^16.1.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@vitejs/plugin-react": "^4.0.0",
"auto-changelog": "^2.4.0", "auto-changelog": "^2.4.0",
"ghooks": "^2.0.4", "ghooks": "^2.0.4",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"pretty-format": "^24.0.0", "pretty-format": "^24.0.0",
"pretty-quick": "^3.1.0" "pretty-quick": "^3.1.0",
"vite": "^4.3.8",
"vitest": "^0.31.1"
}, },
"volta": { "volta": {
"node": "18.14.2", "node": "18.14.2",

View File

@ -1,4 +1,80 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compose > case 1 1`] = `
<div>
<div>
<h1>
A
</h1>
</div>
</div>
`;
exports[`compose > case 2 1`] = `
<div>
<div>
<h1>
A
</h1>
<div>
<h1>
B
</h1>
</div>
</div>
</div>
`;
exports[`compose > case 3 1`] = `
<div>
<div>
<h1>
A
</h1>
</div>
</div>
`;
exports[`compose > case 4 1`] = `
<div>
<div>
<h1>
A
</h1>
<div>
<h1>
B
</h1>
<div>
<h1>
C
</h1>
</div>
</div>
</div>
</div>
`;
exports[`compose > case 5 1`] = `
<div>
<div>
<h1>
A
</h1>
<div>
<h1>
B
1
</h1>
<div>
<h1>
C
</h1>
</div>
</div>
</div>
</div>
`;
exports[`compose case 1 1`] = ` exports[`compose case 1 1`] = `
<div> <div>

View File

@ -1,4 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`route-switch > case 1 1`] = `
<div>
<h1>
Home
</h1>
</div>
`;
exports[`route-switch case 1 1`] = ` exports[`route-switch case 1 1`] = `
<div> <div>

View File

@ -48,7 +48,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
showTime: true, showTime: true,
gmt: true, gmt: true,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
result.onChange(moment.utc('2022-02-22 22:22:22')); result.onChange(moment.utc('2022-02-22 22:22:22'));
@ -59,7 +59,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
showTime: true, showTime: true,
gmt: false, gmt: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
const m = moment('2022-02-22 22:22:22'); const m = moment('2022-02-22 22:22:22');
@ -71,7 +71,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
showTime: false, showTime: false,
gmt: true, gmt: true,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
result.onChange(moment.utc('2022-02-22')); result.onChange(moment.utc('2022-02-22'));
@ -82,7 +82,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
showTime: false, showTime: false,
gmt: false, gmt: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
const m = moment('2022-02-22'); const m = moment('2022-02-22');
@ -94,7 +94,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
picker: 'year', picker: 'year',
gmt: true, gmt: true,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
result.onChange(moment.utc('2022-01-01T00:00:00.000Z')); result.onChange(moment.utc('2022-01-01T00:00:00.000Z'));
@ -105,7 +105,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
picker: 'year', picker: 'year',
gmt: false, gmt: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
const m = moment('2022-02-01 00:00:00'); const m = moment('2022-02-01 00:00:00');
@ -117,7 +117,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
picker: 'month', picker: 'month',
gmt: true, gmt: true,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
result.onChange(moment.utc('2022-02-22T00:00:00.000Z')); result.onChange(moment.utc('2022-02-22T00:00:00.000Z'));
@ -128,7 +128,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
picker: 'month', picker: 'month',
gmt: false, gmt: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
const m = moment('2022-02-01 00:00:00'); const m = moment('2022-02-01 00:00:00');
@ -140,7 +140,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
picker: 'quarter', picker: 'quarter',
gmt: true, gmt: true,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
result.onChange(moment.utc('2022-02-22T00:00:00.000Z')); result.onChange(moment.utc('2022-02-22T00:00:00.000Z'));
@ -151,7 +151,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
picker: 'quarter', picker: 'quarter',
gmt: false, gmt: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
const m = moment('2022-02-01 00:00:00'); const m = moment('2022-02-01 00:00:00');
@ -163,7 +163,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
picker: 'week', picker: 'week',
gmt: true, gmt: true,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
const m = moment.utc('2022-02-21T00:00:00.000Z'); const m = moment.utc('2022-02-21T00:00:00.000Z');
@ -175,7 +175,7 @@ describe('mapDatePicker', () => {
const props = { const props = {
picker: 'week', picker: 'week',
gmt: false, gmt: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
const m = moment('2022-02-21 00:00:00'); const m = moment('2022-02-21 00:00:00');
@ -188,7 +188,7 @@ describe('mapDatePicker', () => {
showTime: true, showTime: true,
gmt: true, gmt: true,
utc: false, utc: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
result.onChange(moment('2022-02-22 22:22:22')); result.onChange(moment('2022-02-22 22:22:22'));
@ -200,7 +200,7 @@ describe('mapDatePicker', () => {
showTime: false, showTime: false,
gmt: true, gmt: true,
utc: false, utc: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const result = mapDatePicker()(props); const result = mapDatePicker()(props);
result.onChange(moment('2022-01-01 23:00:00')); result.onChange(moment('2022-01-01 23:00:00'));

View File

@ -7,7 +7,7 @@ describe('mapRangePicker', () => {
showTime: false, showTime: false,
gmt: true, gmt: true,
utc: true, utc: true,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const { onChange } = mapRangePicker()(props); const { onChange } = mapRangePicker()(props);
const value = [moment.utc('2023-01-01T00:00:00.000Z'), moment.utc('2023-01-02T00:00:00.000Z')]; const value = [moment.utc('2023-01-01T00:00:00.000Z'), moment.utc('2023-01-02T00:00:00.000Z')];
@ -20,7 +20,7 @@ describe('mapRangePicker', () => {
showTime: true, showTime: true,
gmt: true, gmt: true,
utc: true, utc: true,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const { onChange } = mapRangePicker()(props); const { onChange } = mapRangePicker()(props);
const value = [moment.utc('2023-01-01T00:00:00.000Z'), moment.utc('2023-01-02T00:00:00.000Z')]; const value = [moment.utc('2023-01-01T00:00:00.000Z'), moment.utc('2023-01-02T00:00:00.000Z')];
@ -33,7 +33,7 @@ describe('mapRangePicker', () => {
showTime: false, showTime: false,
gmt: true, gmt: true,
utc: false, utc: false,
onChange: jest.fn(), onChange: vi.fn(),
}; };
const { onChange } = mapRangePicker()(props); const { onChange } = mapRangePicker()(props);
const value = [moment.utc('2023-01-01T00:00:00.000Z'), moment.utc('2023-01-02T00:00:00.000Z')]; const value = [moment.utc('2023-01-01T00:00:00.000Z'), moment.utc('2023-01-02T00:00:00.000Z')];

View File

@ -482,7 +482,7 @@ describe('parentsIn', () => {
const dn = createDesignable({ const dn = createDesignable({
current: schema.properties.menu.properties.item1, current: schema.properties.menu.properties.item1,
}); });
const callback = jest.fn(); const callback = vi.fn();
dn.on('error', callback); dn.on('error', callback);
dn.insertAfterBegin(schema.properties.menu); dn.insertAfterBegin(schema.properties.menu);
expect(schema.properties.menu).toBeDefined(); expect(schema.properties.menu).toBeDefined();

View File

@ -0,0 +1,8 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"types": [
"vitest/globals"
]
},
}

View File

@ -17,11 +17,6 @@
"rc-tabs": "^11.7.1", "rc-tabs": "^11.7.1",
"rc-tree": "^5.2.0" "rc-tree": "^5.2.0"
}, },
"devDependencies": {
"@testing-library/jest-dom": "^5.11.3",
"@testing-library/react": "^10.4.8",
"react-test-renderer": "^16.13.1"
},
"peerDependencies": { "peerDependencies": {
"@umijs/preset-dumi": "1.x", "@umijs/preset-dumi": "1.x",
"react": "^16.13.1 || ^17.0.0" "react": "^16.13.1 || ^17.0.0"

View File

@ -0,0 +1,8 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"types": [
"vitest/globals"
]
},
}

24
scripts/setupVitest.ts Normal file
View File

@ -0,0 +1,24 @@
import '@testing-library/jest-dom';
import { vi } from 'vitest';
import '../packages/core/client/src/i18n';
// 解决 ypeError: window.matchMedia is not a function
// 参见: https://github.com/vitest-dev/vitest/issues/821#issuecomment-1046954558
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(), // deprecated
removeListener: vi.fn(), // deprecated
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
// 解决 Error: Not implemented: window.computedStyle(elt, pseudoElt)
// 参见https://github.com/nickcolley/jest-axe/issues/147#issuecomment-758804533
const { getComputedStyle } = window;
window.getComputedStyle = (elt) => getComputedStyle(elt);

7
testUtils.ts Normal file
View File

@ -0,0 +1,7 @@
const globalTimeout = global.setTimeout;
export const sleep = async (timeout = 0) => {
await new Promise((resolve) => {
globalTimeout(resolve, timeout);
});
};

View File

@ -49,7 +49,8 @@
} }
}, },
"include": [ "include": [
"packages/**/*" "packages/**/*",
"scripts/setupVitest.ts"
], ],
"exclude": [ "exclude": [
"packages/**/node_modules", "packages/**/node_modules",

27
vitest.config.ts Normal file
View File

@ -0,0 +1,27 @@
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
setupFiles: 'scripts/setupVitest.ts',
environment: 'jsdom',
css: true,
threads: true,
alias: [
{ find: '@nocobase/evaluators/client', replacement: 'packages/core/evaluators/src/client' },
{ find: '@nocobase/utils/client', replacement: 'packages/core/utils/src/client' },
{ find: /^@nocobase\/app-(.*)/, replacement: 'packages/$1/src' },
{ find: /^@nocobase\/plugin-sample-(.*)/, replacement: 'packages/samples/$1/src' },
{ find: /^@nocobase\/plugin-pro-(.*)/, replacement: 'packages/pro-plugins/$1/src' },
{ find: /^@nocobase\/plugin-(.*)/, replacement: 'packages/plugins/$1/src' },
{ find: /^@nocobase\/preset-(.*)/, replacement: 'packages/presets/$1/src' },
{ find: /^@nocobase\/(.*)/, replacement: 'packages/core/$1/src' },
],
include: ['packages/**/{dumi-theme-nocobase,sdk,client}/**/__tests__/**/*.{test,spec}.{ts,tsx}'],
exclude: ['**/node_modules/**', '**/dist/**', '**/lib/**', '**/es/**', '**/{vitest,commitlint}.config.*'],
testTimeout: 300000,
bail: 1,
},
});

698
yarn.lock

File diff suppressed because it is too large Load Diff