|
1 | 1 | import {render, fireEvent, waitFor} from '@testing-library/svelte'; |
2 | | -import {writable, get as svelteGet} from 'svelte/store'; |
3 | 2 |
|
4 | 3 | import Input from './fixtures/input.svelte'; |
| 4 | +import StoreContextInspector from './fixtures/store-context-inspector.svelte'; |
5 | 5 |
|
6 | 6 | import Form from 'lib/components/Form.svelte'; |
| 7 | +import {createForm} from 'lib/index'; |
7 | 8 |
|
8 | 9 | const defaultProps = { |
9 | 10 | initialValues: {foo: ''}, |
10 | 11 | onSubmit() {}, |
11 | 12 | }; |
12 | 13 |
|
13 | | -const get = (store) => { |
14 | | - /** |
15 | | - * not sure why we need to invoke `get` twice, but we do |
16 | | - */ |
17 | | - return svelteGet(svelteGet(store)); |
| 14 | +const contextType = { |
| 15 | + userDefined: 'user', |
| 16 | + fallback: 'fallback', |
18 | 17 | }; |
19 | 18 |
|
20 | 19 | describe('Form', () => { |
21 | | - test('-> exposes `form` prop via slot properties', async () => { |
22 | | - const inputId = 'foo'; |
23 | | - const props = { |
24 | | - ...defaultProps, |
25 | | - initialValues: {[inputId]: ''}, |
| 20 | + describe.each` |
| 21 | + contextName | type |
| 22 | + ${'fallback'} | ${contextType.fallback} |
| 23 | + ${'user-defined'} | ${contextType.userDefined} |
| 24 | + `('-> with $contextName context', ({type}) => { |
| 25 | + const getForm = (props = defaultProps) => { |
| 26 | + const form = |
| 27 | + type === contextType.userDefined ? createForm(props) : undefined; |
| 28 | + const resultProps = type === contextType.userDefined ? {} : props; |
| 29 | + |
| 30 | + return [resultProps, form]; |
26 | 31 | }; |
27 | | - let form = writable({}); |
28 | | - const {getByLabelText} = render( |
29 | | - <Form {...props} let_form={form}> |
30 | | - <Input field={inputId} /> |
31 | | - </Form>, |
32 | | - ); |
33 | | - const input = getByLabelText(inputId); |
34 | 32 |
|
35 | | - expect(get(form)).toHaveProperty('foo', ''); |
36 | | - |
37 | | - const value = 'bar'; |
38 | | - |
39 | | - await fireEvent.input(input, {target: {value}}); |
40 | | - |
41 | | - expect(get(form)).toHaveProperty('foo', value); |
42 | | - }); |
43 | | - |
44 | | - test('-> exposes `state` prop via slot properties', async () => { |
45 | | - const inputId = 'foo'; |
46 | | - const props = { |
47 | | - ...defaultProps, |
48 | | - initialValues: {[inputId]: ''}, |
49 | | - }; |
50 | | - let state = writable({}); |
51 | | - const {getByLabelText} = render( |
52 | | - <Form {...props} let_state={state}> |
53 | | - <Input field={inputId} /> |
54 | | - </Form>, |
55 | | - ); |
56 | | - const input = getByLabelText(inputId); |
57 | | - |
58 | | - expect(get(state)).toHaveProperty('isModified', false); |
59 | | - |
60 | | - const value = 'bar'; |
61 | | - |
62 | | - await fireEvent.input(input, {target: {value}}); |
63 | | - |
64 | | - expect(get(state)).toHaveProperty('isModified', true); |
65 | | - }); |
66 | | - |
67 | | - test.each` |
68 | | - methodName |
69 | | - ${'handleChange'} |
70 | | - ${'handleSubmit'} |
71 | | - ${'updateField'} |
72 | | - ${'updateInitialValues'} |
73 | | - ${'updateTouched'} |
74 | | - ${'updateValidateField'} |
75 | | - ${'validateField'} |
76 | | - `('-> exposes $methodName via slot properties', ({methodName}) => { |
77 | | - const inputId = 'foo'; |
78 | | - const methodSetter = jest.fn(); |
79 | | - const props = { |
80 | | - ...defaultProps, |
81 | | - initialValues: {[inputId]: ''}, |
82 | | - [`let_${methodName}`]: methodSetter, |
83 | | - }; |
84 | | - render( |
85 | | - <Form {...props}> |
86 | | - <Input field={inputId} /> |
87 | | - </Form>, |
88 | | - ); |
89 | | - |
90 | | - expect(methodSetter.mock.calls[0][0]).toBeInstanceOf(Function); |
91 | | - expect(methodSetter.mock.calls[0][0]).toHaveProperty('name', methodName); |
| 33 | + test('-> exposes `form` store to children via context', async () => { |
| 34 | + const inputId = 'foo'; |
| 35 | + const baseProps = {...defaultProps, initialValues: {[inputId]: ''}}; |
| 36 | + const [props, form] = getForm(baseProps); |
| 37 | + const {getByLabelText, getByTestId} = render( |
| 38 | + <Form {...props} context={form}> |
| 39 | + <Input field={inputId} /> |
| 40 | + |
| 41 | + <StoreContextInspector data-testid="values" storeProp="form" /> |
| 42 | + </Form>, |
| 43 | + ); |
| 44 | + const input = getByLabelText(inputId); |
| 45 | + const valuesEl = getByTestId('values'); |
| 46 | + let values = JSON.parse(valuesEl.textContent); |
| 47 | + |
| 48 | + expect(values[inputId]).toBe(baseProps.initialValues[inputId]); |
| 49 | + expect(input.value).toBe(values[inputId]); |
| 50 | + |
| 51 | + const value = 'bar'; |
| 52 | + |
| 53 | + await fireEvent.input(input, {target: {value}}); |
| 54 | + |
| 55 | + values = JSON.parse(valuesEl.textContent); |
| 56 | + |
| 57 | + expect(values[inputId]).toBe(value); |
| 58 | + expect(input.value).toBe(value); |
| 59 | + }); |
| 60 | + |
| 61 | + test('-> exposes `state` store to children via context', async () => { |
| 62 | + const inputId = 'foo'; |
| 63 | + const baseProps = {...defaultProps, initialValues: {[inputId]: ''}}; |
| 64 | + const [props, form] = getForm(baseProps); |
| 65 | + const {getByLabelText, getByTestId} = render( |
| 66 | + <Form {...props} context={form}> |
| 67 | + <Input field={inputId} /> |
| 68 | + |
| 69 | + <StoreContextInspector data-testid="state" storeProp="state" /> |
| 70 | + </Form>, |
| 71 | + ); |
| 72 | + const input = getByLabelText(inputId); |
| 73 | + const stateEl = getByTestId('state'); |
| 74 | + let state = JSON.parse(stateEl.textContent); |
| 75 | + |
| 76 | + expect(state).toHaveProperty('isModified', false); |
| 77 | + |
| 78 | + const value = 'bar'; |
| 79 | + |
| 80 | + await fireEvent.input(input, {target: {value}}); |
| 81 | + |
| 82 | + state = JSON.parse(stateEl.textContent); |
| 83 | + expect(state).toHaveProperty('isModified', true); |
| 84 | + }); |
| 85 | + |
| 86 | + test('-> exposes `errors` store to children via context', async () => { |
| 87 | + const inputId = 'foo'; |
| 88 | + const validationError = `${inputId} is required`; |
| 89 | + const baseProps = { |
| 90 | + ...defaultProps, |
| 91 | + initialValues: {[inputId]: ''}, |
| 92 | + validate: (values) => { |
| 93 | + if (!values[inputId]) { |
| 94 | + return {[inputId]: validationError}; |
| 95 | + } |
| 96 | + }, |
| 97 | + }; |
| 98 | + const [props, form] = getForm(baseProps); |
| 99 | + const {getByTestId, getByLabelText, getByRole} = render( |
| 100 | + <Form {...props} context={form}> |
| 101 | + <Input field={inputId} /> |
| 102 | + <StoreContextInspector data-testid="error" storeProp="errors" /> |
| 103 | + |
| 104 | + <button type="submit">submit</button> |
| 105 | + </Form>, |
| 106 | + ); |
| 107 | + const input = getByLabelText(inputId); |
| 108 | + const button = getByRole('button'); |
| 109 | + const errorEl = getByTestId('error'); |
| 110 | + let errors = JSON.parse(errorEl.textContent); |
| 111 | + |
| 112 | + expect(errors).toHaveProperty(inputId, ''); |
| 113 | + |
| 114 | + await fireEvent.click(button); |
| 115 | + |
| 116 | + await waitFor(() => { |
| 117 | + errors = JSON.parse(errorEl.textContent); |
| 118 | + expect(errors).toHaveProperty(inputId, validationError); |
| 119 | + }); |
| 120 | + |
| 121 | + const value = 'bar'; |
| 122 | + |
| 123 | + await fireEvent.input(input, {target: {value}}); |
| 124 | + await fireEvent.click(button); |
| 125 | + |
| 126 | + await waitFor(() => { |
| 127 | + errors = JSON.parse(errorEl.textContent); |
| 128 | + expect(errors).toHaveProperty(inputId, ''); |
| 129 | + }); |
| 130 | + }); |
| 131 | + |
| 132 | + test.each` |
| 133 | + methodName |
| 134 | + ${'handleChange'} |
| 135 | + ${'handleSubmit'} |
| 136 | + ${'updateField'} |
| 137 | + ${'updateInitialValues'} |
| 138 | + ${'updateTouched'} |
| 139 | + ${'updateValidateField'} |
| 140 | + ${'validateField'} |
| 141 | + `('-> exposes $methodName via slot properties', ({methodName}) => { |
| 142 | + const inputId = 'foo'; |
| 143 | + const methodSetter = jest.fn(); |
| 144 | + const baseProps = {...defaultProps, initialValues: {[inputId]: ''}}; |
| 145 | + const methodProps = {[`let_${methodName}`]: methodSetter}; |
| 146 | + const [props, form] = getForm(baseProps); |
| 147 | + render( |
| 148 | + <Form {...props} {...methodProps} context={form}> |
| 149 | + <Input field={inputId} /> |
| 150 | + </Form>, |
| 151 | + ); |
| 152 | + |
| 153 | + expect(methodSetter.mock.calls[0][0]).toBeInstanceOf(Function); |
| 154 | + expect(methodSetter.mock.calls[0][0]).toHaveProperty('name', methodName); |
| 155 | + }); |
92 | 156 | }); |
93 | 157 | }); |
0 commit comments