Skip to content

Commit

Permalink
Initialize model with default values (#1343)
Browse files Browse the repository at this point in the history
  • Loading branch information
kestarumper authored Jun 14, 2024
1 parent a8f4ced commit c047291
Show file tree
Hide file tree
Showing 20 changed files with 195 additions and 132 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
'^uniforms([^/]*)(.*)$': '<rootDir>/packages/uniforms$1/src$2',
},
preset: 'ts-jest',
setupFiles: ['./scripts/setupEnzyme.ts'],
setupFiles: ['./scripts/setupEnzyme.ts', './scripts/setupFilterWarnings.ts'],
setupFilesAfterEnv: ['./scripts/setupMatchers.ts'],
testEnvironment: 'jsdom',
testPathIgnorePatterns: ['/node_modules/', '/_[^/]*$', '\\.d\\.ts$'],
Expand Down
2 changes: 1 addition & 1 deletion packages/uniforms-antd/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ it('exports everything', () => {
});
});

describe('@RTL', () => {
describe('@RTL AntD', () => {
suites.testAutoField(theme.AutoField, {
getDateField: screen => screen.getByRole('textbox'),
getSelectField: screen => screen.getByRole('combobox'),
Expand Down
2 changes: 1 addition & 1 deletion packages/uniforms-bootstrap4/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ it('exports everything', () => {
});
});

describe('@RTL', () => {
describe('@RTL Bootstrap4', () => {
suites.testAutoField(theme.AutoField, {
getDateField: screen => screen.getByLabelText('X'),
getSelectField: screen => screen.getByRole('combobox'),
Expand Down
2 changes: 1 addition & 1 deletion packages/uniforms-bootstrap5/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ it('exports everything', () => {
});
});

describe('@RTL', () => {
describe('@RTL Bootstrap5', () => {
suites.testAutoField(theme.AutoField, {
getDateField: screen => screen.getByLabelText('X'),
getSelectField: screen => screen.getByRole('combobox'),
Expand Down
2 changes: 1 addition & 1 deletion packages/uniforms-mui/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ it('exports everything', () => {
});
});

describe('@RTL', () => {
describe('@RTL MUI', () => {
suites.testAutoField(theme.AutoField, {
getDateField: screen => screen.getByLabelText('X *'),
getSelectField: screen => screen.getByRole('button'),
Expand Down
2 changes: 1 addition & 1 deletion packages/uniforms-semantic/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ it('exports everything', () => {
});
});

describe('@RTL', () => {
describe('@RTL Semantic', () => {
suites.testAutoField(theme.AutoField, {
getDateField: screen => screen.getByLabelText('X'),
getSelectField: screen => screen.getByRole('combobox'),
Expand Down
4 changes: 2 additions & 2 deletions packages/uniforms-unstyled/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ it('exports everything', () => {
});
});

describe('@RTL', () => {
describe('@RTL unstyled', () => {
suites.testAutoField(theme.AutoField, {
getDateField: screen => screen.getByLabelText('X'),
getSelectField: screen => screen.getByRole('combobox'),
Expand All @@ -44,7 +44,7 @@ describe('@RTL', () => {
suites.testListAddField(theme.ListAddField);
suites.testListDelField(theme.ListDelField);
suites.testListField(theme.ListField, {
getListAddField: screen => screen.getByRole('button'),
getListAddField: screen => screen.getByRole('button', { name: '+' }),
testError: false,
});
suites.testListItemField(theme.ListItemField);
Expand Down
14 changes: 8 additions & 6 deletions packages/uniforms/__suites__/ListAddField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function testListAddField(ListAddField: ComponentType<any>) {
test('<ListAddField> - correctly reacts on click', async () => {
const onChange = jest.fn();
renderWithZod({
element: <ListAddField name="x.1" data-testid="x" value="d" />,
element: <ListAddField name="x.$" data-testid="x" value="d" />,
model: { x: ['a', 'b', 'c'] },
onChange,
schema: z.object({ x: z.array(z.string()) }),
Expand All @@ -25,19 +25,21 @@ export function testListAddField(ListAddField: ComponentType<any>) {
test('<ListAddField> - correctly reacts on enter', async () => {
const onChange = jest.fn();
renderWithZod({
element: <ListAddField name="x.1" data-testid="x" value="d" />,
element: <ListAddField name="x.$" data-testid="x" value="d" />,
model: { x: ['a', 'b', 'c'] },
onChange,
schema: z.object({ x: z.array(z.string()) }),
});
await userEvent.type(screen.getByTestId('x'), '{Enter}');
// we can't use `userEvent.type(...)` because it does 'click + type' so we "select the button" using `userEvent.tab()`
await userEvent.tab();
await userEvent.keyboard('{Enter}');
expect(onChange).toHaveBeenLastCalledWith('x', ['a', 'b', 'c', 'd']);
});

test('<ListAddField> - prevents onClick when disabled', async () => {
const onChange = jest.fn();
renderWithZod({
element: <ListAddField name="x.1" data-testid="x" disabled />,
element: <ListAddField name="x.$" data-testid="x" disabled />,
model: { x: ['a', 'b', 'c'] },
schema: z.object({ x: z.array(z.string()) }),
});
Expand All @@ -51,7 +53,7 @@ export function testListAddField(ListAddField: ComponentType<any>) {
test('<ListAddField> - prevents onClick when readOnly', async () => {
const onChange = jest.fn();
renderWithZod({
element: <ListAddField name="x.1" data-testid="x" readOnly />,
element: <ListAddField name="x.$" data-testid="x" readOnly />,
model: { x: ['a', 'b', 'c'] },
onChange,
schema: z.object({ x: z.array(z.string()) }),
Expand All @@ -65,7 +67,7 @@ export function testListAddField(ListAddField: ComponentType<any>) {
test('<ListAddField> - prevents onClick when limit reached', async () => {
const onChange = jest.fn();
renderWithZod({
element: <ListAddField name="x.1" data-testid="x" readOnly />,
element: <ListAddField name="x.$" data-testid="x" readOnly />,
model: { x: ['a', 'b', 'c'] },
onChange,
schema: z.object({ x: z.array(z.string()).min(3) }),
Expand Down
4 changes: 3 additions & 1 deletion packages/uniforms/__suites__/ListDelField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export function testListDelField(ListDelField: ComponentType<any>) {
onChange,
schema: z.object({ x: z.array(z.string()) }),
});
await userEvent.type(screen.getByTestId('x'), '{Enter}');
// we can't use `userEvent.type(...)` because it does 'click + type' so we "select the button" using `userEvent.tab()`
await userEvent.tab();
await userEvent.keyboard('{Enter}');
expect(onChange).toHaveBeenLastCalledWith('x', ['a', 'c']);
});

Expand Down
10 changes: 7 additions & 3 deletions packages/uniforms/__suites__/ListField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,17 @@ export function testListField(
const onChange = jest.fn();
render(
<ListField name="x" label="ListFieldLabel" />,
{ x: { type: Array, optional: true }, 'x.$': String },
{
x: { type: Array, optional: true },
'x.$': { type: Object },
'x.$.name': { type: String, defaultValue: 'someValue' },
},
{ onChange },
);

await userEvent.click(getListAddField(screen));
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenLastCalledWith('x', [undefined]);
expect(await screen.findAllByDisplayValue('someValue')).toHaveLength(1);
expect(onChange).toHaveBeenLastCalledWith('x.0', { name: 'someValue' });
});

if (testError) {
Expand Down
10 changes: 5 additions & 5 deletions packages/uniforms/__suites__/RadioField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ export function testRadioField(
schema: z.object({ x: z.enum(['a', 'b']) }),
onChange,
});
await userEvent.click(screen.getByLabelText('a'));
await userEvent.click(screen.getByLabelText('b'));
await userEvent.click(screen.getByRole('radio', { name: 'a' }));
await userEvent.click(screen.getByRole('radio', { name: 'b' }));

expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledTimes(0);
});

skipTestIf(skipHtmlAttributesTest)(
Expand Down Expand Up @@ -160,9 +160,9 @@ export function testRadioField(
schema: z.object({ x: z.enum(['a', 'b']) }),
onChange,
});
await userEvent.click(screen.getByLabelText('a'));
await userEvent.click(screen.getByRole('radio', { name: 'a' }));

expect(onChange).toHaveBeenLastCalledWith('x', 'a');
expect(onChange).not.toHaveBeenCalled();
});

test('<RadioField> - renders a label', () => {
Expand Down
40 changes: 9 additions & 31 deletions packages/uniforms/__suites__/render-zod.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,23 @@
import { render as renderOnScreen } from '@testing-library/react';
import React, { ReactElement } from 'react';
import { BaseForm, Context, context, randomIds } from 'uniforms';
import React, { ComponentProps, ReactElement } from 'react';
import { ZodBridge } from 'uniforms-bridge-zod';
import { TypeOf, ZodObject, ZodRawShape } from 'zod';
import { AutoForm } from 'uniforms-unstyled';
import { ZodObject, ZodRawShape } from 'zod';

export function renderWithZod<Props, Schema extends ZodObject<ZodRawShape>>({
element,
schema,
...contextValueOverride
...autoFormProps
}: {
element: ReactElement<Props>;
schema: Schema;
} & Omit<Partial<Context<TypeOf<Schema>>>, 'schema'>) {
} & Partial<Omit<ComponentProps<typeof AutoForm>, 'schema'>>) {
return renderOnScreen(element, {
wrapper({ children }) {
const value = {
changed: false,
changedMap: Object.create(null),
error: null,
formRef: null as unknown as BaseForm<TypeOf<Schema>>,
model: Object.create(null),
name: [],
onChange() {},
onSubmit() {},
randomId: randomIds(),
submitted: false,
submitting: false,
validating: false,
...contextValueOverride,
schema: new ZodBridge({
schema,
}),
state: {
disabled: false,
readOnly: false,
showInlineError: false,
...contextValueOverride?.state,
},
};

return <context.Provider children={children} value={value} />;
const bridge = new ZodBridge({ schema });
return (
<AutoForm {...autoFormProps} schema={bridge} children={children} />
);
},
});
}
39 changes: 13 additions & 26 deletions packages/uniforms/__suites__/render.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,30 @@
import { render as renderOnScreen, RenderResult } from '@testing-library/react';
import React, { ReactElement, cloneElement } from 'react';
import SimpleSchema, { SimpleSchemaDefinition } from 'simpl-schema';
import { BaseForm, Context, UnknownObject, context, randomIds } from 'uniforms';
import { Context, UnknownObject } from 'uniforms';
import { SimpleSchema2Bridge } from 'uniforms-bridge-simple-schema-2';
import { AutoForm } from 'uniforms-unstyled';

const randomId = randomIds();
export function render<P, Model extends UnknownObject>(
element: ReactElement<P>,
schema?: SimpleSchemaDefinition,
contextValueExtension?: Partial<Context<Model>>,
contextValueExtension?: Pick<Partial<Context<Model>>, 'onChange'>,
model = {} as Model,
): RenderResult & { rerenderWithProps: (props: P) => void } {
const renderResult = renderOnScreen(element, {
wrapper({ children }) {
if (schema) {
const contextValue = {
changed: false,
changedMap: {},
error: null,
model,
name: [],
onChange() {},
onSubmit() {},
randomId,
submitted: false,
submitting: false,
validating: false,
...contextValueExtension,
schema: new SimpleSchema2Bridge({ schema: new SimpleSchema(schema) }),
state: {
disabled: false,
readOnly: false,
showInlineError: false,
...contextValueExtension?.state,
},
formRef: {} as BaseForm<UnknownObject>,
};
const bridge = new SimpleSchema2Bridge({
schema: new SimpleSchema(schema),
});
return (
<context.Provider value={contextValue}>{children}</context.Provider>
<AutoForm
onChange={contextValueExtension?.onChange}
model={model}
schema={bridge}
>
{children}
</AutoForm>
);
}
return <>{children}</>;
Expand Down
Loading

0 comments on commit c047291

Please sign in to comment.