refactor: make testing more stable

This commit is contained in:
Rain 2023-07-29 18:07:00 +08:00
parent 767b81c65d
commit 3c7b3f3caf
14 changed files with 172 additions and 75 deletions

View File

@ -1,5 +1,5 @@
import React from 'react';
import { render, screen, userEvent, waitFor } from 'testUtils';
import { render, screen, sleep, userEvent, waitFor } from 'testUtils';
import App1 from '../demos/demo1';
import App2 from '../demos/demo2';
import App3 from '../demos/demo3';
@ -13,6 +13,9 @@ import App9 from '../demos/demo9';
describe('DatePicker', () => {
it('basic', async () => {
const { container, getByText } = render(<App1 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
const input = container.querySelector('input') as HTMLElement;
@ -35,6 +38,9 @@ describe('DatePicker', () => {
it('GMT', async () => {
const { container, getByText } = render(<App2 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
const input = container.querySelector('input') as HTMLElement;
@ -53,6 +59,9 @@ describe('DatePicker', () => {
it('non-UTC', async () => {
const { container } = render(<App3 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
const input = container.querySelector('input') as HTMLElement;
@ -74,6 +83,9 @@ describe('DatePicker', () => {
describe('RangePicker', () => {
it('GMT', async () => {
const { container, getByPlaceholderText } = render(<App4 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
const startInput = getByPlaceholderText('Start date');
const endInput = getByPlaceholderText('End date');
@ -92,6 +104,9 @@ describe('RangePicker', () => {
it('non-GMT', async () => {
const { container, getByPlaceholderText } = render(<App5 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
const startInput = getByPlaceholderText('Start date');
const endInput = getByPlaceholderText('End date');
@ -115,6 +130,9 @@ describe('RangePicker', () => {
it('non-UTC', async () => {
const { container, getByPlaceholderText } = render(<App6 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
const startInput = getByPlaceholderText('Start date');
const endInput = getByPlaceholderText('End date');
@ -133,6 +151,9 @@ describe('RangePicker', () => {
it('showTime=false,gmt=true,utc=true', async () => {
const { container } = render(<App7 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
const input = container.querySelector('input') as HTMLElement;
@ -152,6 +173,9 @@ describe('RangePicker', () => {
it('showTime=false,gmt=false,utc=true', async () => {
const { container } = render(<App8 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
const input = container.querySelector('input') as HTMLElement;
@ -175,6 +199,9 @@ describe('RangePicker', () => {
it('showTime=false,gmt=true,utc=true & not input', async () => {
const currentDateString = new Date().toISOString().split('T')[0];
const { container } = render(<App9 />);
await sleep();
const picker = container.querySelector('.ant-picker') as HTMLElement;
await userEvent.click(picker);

View File

@ -1,11 +1,13 @@
import React from 'react';
import { render, screen } from 'testUtils';
import { render, screen, waitFor } from 'testUtils';
import App1 from '../demos/demo1';
describe('FormItem', () => {
it('should render correctly', () => {
it('should render correctly', async () => {
render(<App1 />);
expect(screen.getByText('title')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText('title')).toBeInTheDocument();
});
});
});

View File

@ -1,5 +1,18 @@
import { FormItem, FormProvider, Input, SchemaComponent } from '@nocobase/client';
import {
APIClientProvider,
CurrentUserProvider,
FormItem,
FormProvider,
Input,
SchemaComponent,
} from '@nocobase/client';
import React from 'react';
import { mockAPIClient } from '../../../../test';
const { apiClient, mockRequest } = mockAPIClient();
mockRequest.onGet('/auth:check').reply(() => {
return [200, { data: {} }];
});
const schema = {
type: 'object',
@ -15,8 +28,12 @@ const schema = {
export default () => {
return (
<FormProvider>
<SchemaComponent components={{ FormItem, Input }} schema={schema} />
</FormProvider>
<APIClientProvider apiClient={apiClient}>
<CurrentUserProvider>
<FormProvider>
<SchemaComponent components={{ FormItem, Input }} schema={schema} />
</FormProvider>
</CurrentUserProvider>
</APIClientProvider>
);
};

View File

@ -8,11 +8,13 @@ describe('FormV2', () => {
it('basic', async () => {
render(<App1 />);
const input = document.querySelector('.ant-input') as HTMLInputElement;
const submit = screen.getByText('Submit');
expect(input).toBeInTheDocument();
expect(screen.getByText('Nickname')).toBeInTheDocument();
let input, submit;
await waitFor(() => {
input = document.querySelector('.ant-input') as HTMLInputElement;
submit = screen.getByText('Submit');
expect(input).toBeInTheDocument();
expect(screen.queryByText('Nickname')).toBeInTheDocument();
});
await userEvent.type(input, '李四');
await userEvent.click(submit);
@ -41,8 +43,7 @@ describe('FormV2', () => {
});
});
// TODO: 等 @Testing-Library 升级到 14.x
it.skip('read pretty', async () => {
it('read pretty', async () => {
render(<App3 />);
await waitFor(() => {

View File

@ -4,6 +4,7 @@ import {
Action,
CollectionField,
CollectionManagerProvider,
CurrentUserProvider,
FormBlockProvider,
FormItem,
FormV2,
@ -26,6 +27,9 @@ mockRequest.onPost('/users:update').reply((params) => {
});
return [200, JSON.parse(params.data)];
});
mockRequest.onGet('/auth:check').reply(() => {
return [200, { data: {} }];
});
function useAction() {
const ctx = useFormBlockContext();
@ -82,11 +86,13 @@ const schema: ISchema = {
export default () => {
return (
<APIClientProvider apiClient={apiClient}>
<CollectionManagerProvider collections={collections}>
<SchemaComponentProvider components={{ FormBlockProvider, FormV2, FormItem, CollectionField, Action, Input }}>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
</CollectionManagerProvider>
<CurrentUserProvider>
<CollectionManagerProvider collections={collections}>
<SchemaComponentProvider components={{ FormBlockProvider, FormV2, FormItem, CollectionField, Action, Input }}>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
</CollectionManagerProvider>
</CurrentUserProvider>
</APIClientProvider>
);
};

View File

@ -5,6 +5,7 @@ import {
BlockSchemaComponentProvider,
CollectionField,
CollectionManagerProvider,
CurrentUserProvider,
FormBlockProvider,
FormItem,
FormV2,
@ -36,6 +37,9 @@ mockRequest.onPost('/users:update').reply((params) => {
});
return [200, JSON.parse(params.data)];
});
mockRequest.onGet('/auth:check').reply(() => {
return [200, { data: {} }];
});
const useAction = () => {
const ctx = useFormBlockContext();
@ -107,15 +111,17 @@ const schema: ISchema = {
export default () => {
return (
<APIClientProvider apiClient={apiClient}>
<CollectionManagerProvider collections={collections}>
<SchemaComponentProvider
components={{ FormBlockProvider, FormItem, CollectionField, Input, Action, FormV2, Password }}
>
<BlockSchemaComponentProvider>
<SchemaComponent schema={schema} />
</BlockSchemaComponentProvider>
</SchemaComponentProvider>
</CollectionManagerProvider>
<CurrentUserProvider>
<CollectionManagerProvider collections={collections}>
<SchemaComponentProvider
components={{ FormBlockProvider, FormItem, CollectionField, Input, Action, FormV2, Password }}
>
<BlockSchemaComponentProvider>
<SchemaComponent schema={schema} />
</BlockSchemaComponentProvider>
</SchemaComponentProvider>
</CollectionManagerProvider>
</CurrentUserProvider>
</APIClientProvider>
);
};

View File

@ -5,9 +5,11 @@ import {
BlockSchemaComponentProvider,
CollectionField,
CollectionManagerProvider,
CurrentUserProvider,
FormBlockProvider,
FormItem,
FormV2,
Grid,
Input,
Password,
SchemaComponent,
@ -26,6 +28,9 @@ mockRequest.onGet('/users:get').reply(200, {
password: '123456',
},
});
mockRequest.onGet('/auth:check').reply(() => {
return [200, { data: {} }];
});
const schema: ISchema = {
type: 'object',
@ -93,15 +98,17 @@ const schema: ISchema = {
export default () => {
return (
<APIClientProvider apiClient={apiClient}>
<CollectionManagerProvider collections={collections}>
<SchemaComponentProvider
components={{ FormBlockProvider, FormItem, CollectionField, Input, Action, FormV2, Password }}
>
<BlockSchemaComponentProvider>
<SchemaComponent schema={schema} />
</BlockSchemaComponentProvider>
</SchemaComponentProvider>
</CollectionManagerProvider>
<CurrentUserProvider>
<CollectionManagerProvider collections={collections}>
<SchemaComponentProvider
components={{ FormBlockProvider, FormItem, CollectionField, Input, Action, FormV2, Password, Grid }}
>
<BlockSchemaComponentProvider>
<SchemaComponent schema={schema} />
</BlockSchemaComponentProvider>
</SchemaComponentProvider>
</CollectionManagerProvider>
</CurrentUserProvider>
</APIClientProvider>
);
};

View File

@ -1,5 +1,5 @@
import React from 'react';
import { render, screen } from 'testUtils';
import { render, screen, waitFor } from 'testUtils';
import App1 from '../demos/demo1';
import App2 from '../demos/demo2';
import App3 from '../demos/demo3';
@ -14,12 +14,13 @@ describe('Grid', () => {
expect(screen.getByText('Block 1')).toBeInTheDocument();
});
it('input', () => {
it('input', async () => {
render(<App2 />);
const inputs = document.querySelectorAll('.ant-input');
expect(inputs.length).toBe(3);
await waitFor(() => {
const inputs = document.querySelectorAll('.ant-input');
expect(inputs.length).toBe(3);
});
});
it('initializer', () => {

View File

@ -1,8 +1,24 @@
import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
import { Form, FormItem, Grid, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
import {
APIClientProvider,
CurrentUserProvider,
Form,
FormItem,
Grid,
Input,
SchemaComponent,
SchemaComponentProvider,
} from '@nocobase/client';
import React from 'react';
import { mockAPIClient } from '../../../../test';
const { apiClient, mockRequest } = mockAPIClient();
mockRequest.onGet('/auth:check').reply(() => {
return [200, { data: {} }];
});
const schema: ISchema = {
type: 'void',
name: 'grid1',
@ -54,8 +70,12 @@ const schema: ISchema = {
export default function App() {
return (
<SchemaComponentProvider components={{ Form, Grid, Input, FormItem }}>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
<APIClientProvider apiClient={apiClient}>
<CurrentUserProvider>
<SchemaComponentProvider components={{ Form, Grid, Input, FormItem }}>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
</CurrentUserProvider>
</APIClientProvider>
);
}

View File

@ -4,6 +4,7 @@ import {
APIClientProvider,
BlockSchemaComponentProvider,
CollectionManagerProvider,
CurrentUserProvider,
SchemaComponent,
SchemaComponentProvider,
} from '@nocobase/client';
@ -19,6 +20,9 @@ mockRequest.onGet('/t_j6omof6tza8:list').reply(async (config) => {
await sleep(200);
return [200, data];
});
mockRequest.onGet('/auth:check').reply(() => {
return [200, { data: {} }];
});
const schema: ISchema = {
type: 'object',
@ -69,15 +73,17 @@ const schema: ISchema = {
export default () => {
return (
<APIClientProvider apiClient={apiClient}>
<SchemaComponentProvider>
<CollectionManagerProvider collections={collections}>
<AntdSchemaComponentProvider>
<BlockSchemaComponentProvider>
<SchemaComponent schema={schema} />
</BlockSchemaComponentProvider>
</AntdSchemaComponentProvider>
</CollectionManagerProvider>
</SchemaComponentProvider>
<CurrentUserProvider>
<SchemaComponentProvider>
<CollectionManagerProvider collections={collections}>
<AntdSchemaComponentProvider>
<BlockSchemaComponentProvider>
<SchemaComponent schema={schema} />
</BlockSchemaComponentProvider>
</AntdSchemaComponentProvider>
</CollectionManagerProvider>
</SchemaComponentProvider>
</CurrentUserProvider>
</APIClientProvider>
);
};

View File

@ -6,15 +6,18 @@ describe('RecordPicker', () => {
it('should show selected options', async () => {
render(<App1 />);
const selector = document.querySelector('.ant-select-selector') as HTMLElement;
expect(selector).toBeInTheDocument();
let selector;
await waitFor(() => {
selector = document.querySelector('.ant-select-selector') as HTMLElement;
expect(selector).toBeInTheDocument();
});
await userEvent.click(selector);
await waitFor(() => {
// 弹窗标题
expect(screen.getByText(/select record/i)).toBeInTheDocument();
expect(screen.queryByText(/select record/i)).toBeInTheDocument();
});
const checkboxes = document.querySelectorAll('.ant-checkbox');
// 第 3 个选项的内容是: “软件开发”
@ -22,8 +25,8 @@ describe('RecordPicker', () => {
await userEvent.click(screen.getByText(/submit/i));
await waitFor(() => {
expect(within(selector).getByText(/软件开发/i)).toBeInTheDocument();
expect(screen.getByText(/软件开发/i, { selector: '.test-record-picker-read-pretty-item' })).toBeInTheDocument();
expect(within(selector).queryByText(/软件开发/i)).toBeInTheDocument();
expect(screen.queryByText(/软件开发/i, { selector: '.test-record-picker-read-pretty-item' })).toBeInTheDocument();
});
});
});

View File

@ -9,6 +9,7 @@ import {
BlockItem,
CollectionField,
CollectionManagerProvider,
CurrentUserProvider,
FormItem,
Input,
RecordPicker,
@ -23,6 +24,9 @@ import data from './mockData';
const { apiClient, mockRequest } = mockAPIClient();
mockRequest.onGet('/auth:check').reply(() => {
return [200, { data: {} }];
});
mockRequest.onGet('/tt_bd_range:list').reply(({ params }) => {
// 已选中的 id
const ids = JSON.parse(params.filter).$and?.[0]?.['id.$ne'] || [];
@ -172,11 +176,13 @@ export default () => {
return (
<APIClientProvider apiClient={apiClient}>
<CollectionManagerProvider collections={mainCollections}>
<SchemaComponentProvider components={components}>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
</CollectionManagerProvider>
<CurrentUserProvider>
<CollectionManagerProvider collections={mainCollections}>
<SchemaComponentProvider components={components}>
<SchemaComponent schema={schema} />
</SchemaComponentProvider>
</CollectionManagerProvider>
</CurrentUserProvider>
</APIClientProvider>
);
};

View File

@ -14,18 +14,18 @@ type VariablesCtx = {
};
export const useVariablesCtx = (): VariablesCtx => {
const { data } = useCurrentUserContext() || {};
const currentUser = useCurrentUserContext();
const { field, service, rowKey } = useTableBlockContext();
const contextData = service?.data?.data?.filter((v) => (field?.data?.selectedRowKeys || [])?.includes(v[rowKey]));
return useMemo(() => {
return {
$user: data?.data || {},
$user: currentUser?.data?.data || {},
$date: {
now: () => dayjs().toISOString(),
},
$context: contextData,
};
}, [data]);
}, [contextData, currentUser?.data?.data]);
};
export const isVariable = (str: unknown) => {

View File

@ -68,11 +68,6 @@ function Label() {
return null;
}
if (!currentUser) {
error('Please check if provide `CurrentUserProvider` in your app.');
throw new Error('Please check if provide `CurrentUserProvider` in your app.');
}
if (!systemSettings) {
error('Please check if provide `SystemSettingsProvider` in your app.');
throw new Error('Please check if provide `SystemSettingsProvider` in your app.');