-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Formatic 3000 #124
base: master
Are you sure you want to change the base?
WIP: Formatic 3000 #124
Changes from 38 commits
230f394
8eec2ce
cebd6fa
8f390f4
2c827b3
c5d0bc8
6739955
f23a600
3fe0ad1
f193215
af6e3e0
0fa226e
067a500
8878def
0f4c5d5
0c571f3
018b2b6
221b69c
2476fc3
ace4df5
565da6f
be8de3c
7e5f9bd
abe9f0c
737daa6
dfc5f54
9cc81ac
9218c37
c84bc32
6aeaf24
7d7a3ad
9b6362d
0185cdd
089c4f3
06c06b4
4bc8326
65f7c08
87660c8
a4a0242
4d3981b
302cf96
0b80b23
a08c04a
5e9b175
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/*global jest, describe, test, expect, afterEach*/ | ||
import React from 'react'; | ||
import { render, fireEvent, cleanup } from 'react-testing-library'; | ||
import { useField } from '@/src/future'; | ||
import { ExampleForm, defaultValue } from '@/demo/future/AutoFields'; | ||
|
||
afterEach(cleanup); | ||
|
||
describe('AutoFields', () => { | ||
test('renders fields from defaultValues', () => { | ||
const onChangeSpy = jest.fn(); | ||
const { getByLabelText } = render( | ||
<ExampleForm defaultValue={defaultValue} onChange={onChangeSpy} /> | ||
); | ||
fireEvent.change(getByLabelText('First Name'), { | ||
target: { value: 'Joe' }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: '', | ||
age: 0, | ||
}); | ||
fireEvent.change(getByLabelText('Last Name'), { | ||
target: { value: 'Foo' }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
age: 0, | ||
}); | ||
|
||
fireEvent.change(getByLabelText('Age'), { | ||
target: { value: 32 }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
age: 32, | ||
}); | ||
}); | ||
|
||
test('renders fields from values', () => { | ||
const onChangeSpy = jest.fn(); | ||
const { getByLabelText } = render( | ||
<ExampleForm onChange={onChangeSpy} value={defaultValue} /> | ||
); | ||
fireEvent.change(getByLabelText('First Name'), { | ||
target: { value: 'Joe' }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: '', | ||
age: 0, | ||
}); | ||
fireEvent.change(getByLabelText('Last Name'), { | ||
target: { value: 'Foo' }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
age: 0, | ||
}); | ||
|
||
fireEvent.change(getByLabelText('Age'), { | ||
target: { value: 32 }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
age: 32, | ||
}); | ||
}); | ||
|
||
test('component prop overrides default field components', () => { | ||
function CustomTextField({ fieldKey, label }) { | ||
const { value, onChangeTargetValue } = useField(fieldKey); | ||
return ( | ||
<div> | ||
<div> | ||
<label htmlFor={fieldKey}>Custom {label}</label> | ||
</div> | ||
<div> | ||
<input | ||
id={fieldKey} | ||
onChange={onChangeTargetValue} | ||
type="text" | ||
value={value} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
const onChangeSpy = jest.fn(); | ||
const components = { string: CustomTextField }; | ||
const { getByLabelText } = render( | ||
<ExampleForm | ||
components={components} | ||
onChange={onChangeSpy} | ||
value={defaultValue} | ||
/> | ||
); | ||
|
||
fireEvent.change(getByLabelText('Custom First Name'), { | ||
target: { value: 'Joe' }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: '', | ||
age: 0, | ||
}); | ||
|
||
fireEvent.change(getByLabelText('Custom Last Name'), { | ||
target: { value: 'Foo' }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
age: 0, | ||
}); | ||
|
||
fireEvent.change(getByLabelText('Age'), { | ||
target: { value: 32 }, | ||
}); | ||
expect(onChangeSpy).toHaveBeenLastCalledWith({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
age: 32, | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/*global jest, describe, test, expect, afterEach*/ | ||
import React from 'react'; | ||
import { render, fireEvent, cleanup } from 'react-testing-library'; | ||
|
||
import { FormContainer, AutoFields, TextField, useField } from '@/src/future'; | ||
|
||
afterEach(cleanup); | ||
|
||
describe('avoid rerendering', () => { | ||
const defaultValue = { firstName: '', lastName: '' }; | ||
|
||
test('should avoid unnecessary rerendering with uncontrolled container', async () => { | ||
const renderSpy = jest.fn(); | ||
function FirstName() { | ||
renderSpy('firstName'); | ||
const { value } = useField('firstName'); | ||
return <div>First Name: {value}</div>; | ||
} | ||
function LastName() { | ||
renderSpy('lastName'); | ||
const { value } = useField('lastName'); | ||
return <div>First Name: {value}</div>; | ||
} | ||
const { getByLabelText } = render( | ||
<FormContainer defaultValue={defaultValue}> | ||
<FirstName /> | ||
<LastName /> | ||
<TextField fieldKey="firstName" id="firstName" label="First Name" /> | ||
<TextField fieldKey="lastName" id="lastName" label="Last Name" /> | ||
</FormContainer> | ||
); | ||
fireEvent.change(getByLabelText('First Name'), { | ||
target: { value: 'Joe' }, | ||
}); | ||
expect(renderSpy.mock.calls).toEqual([ | ||
['firstName'], | ||
['lastName'], | ||
['firstName'], | ||
]); | ||
}); | ||
|
||
test('avoids unnecessary renders of AutoFields', () => { | ||
function CustomTextField({ fieldKey, label }) { | ||
const { value, onChangeTargetValue } = useField(fieldKey); | ||
const randomId = Math.random(); | ||
return ( | ||
<> | ||
<label htmlFor={randomId}>{label}</label> | ||
<input | ||
id={randomId} | ||
onChange={onChangeTargetValue} | ||
type="text" | ||
value={value} | ||
/> | ||
</> | ||
); | ||
} | ||
|
||
const components = { | ||
string: CustomTextField, | ||
}; | ||
|
||
const { getByLabelText } = render( | ||
<FormContainer defaultValue={defaultValue}> | ||
<AutoFields components={components} /> | ||
</FormContainer> | ||
); | ||
|
||
const getInputId = id => getByLabelText(id).id; | ||
|
||
const intitialLastNameId = getInputId('Last Name'); | ||
const initialFirstNameId = getInputId('First Name'); | ||
|
||
fireEvent.change(getByLabelText('First Name'), { | ||
target: { value: 'Joe' }, | ||
}); | ||
|
||
expect(getInputId('First Name')).not.toBe(initialFirstNameId); | ||
expect(getInputId('Last Name')).toBe(intitialLastNameId); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/*global jest, describe, test, expect, afterEach*/ | ||
import React from 'react'; | ||
import { render, fireEvent, cleanup } from 'react-testing-library'; | ||
|
||
import { ExampleForm, defaultValue } from '@/demo/future/BuiltInField'; | ||
|
||
afterEach(cleanup); | ||
|
||
describe('built-in field', () => { | ||
test('should fire onChange correctly', () => { | ||
const onChangeSpy = jest.fn(); | ||
const { getByLabelText } = render( | ||
<ExampleForm defaultValue={defaultValue} onChange={onChangeSpy} /> | ||
); | ||
fireEvent.change(getByLabelText('First Name'), { | ||
target: { value: 'Joe' }, | ||
}); | ||
expect(onChangeSpy.mock.calls[0][0]).toEqual({ | ||
firstName: 'Joe', | ||
lastName: '', | ||
}); | ||
fireEvent.change(getByLabelText('Last Name'), { | ||
target: { value: 'Foo' }, | ||
}); | ||
expect(onChangeSpy.mock.calls[1][0]).toEqual({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/*global jest, describe, test, expect, afterEach*/ | ||
import React from 'react'; | ||
import { render, fireEvent, cleanup } from 'react-testing-library'; | ||
|
||
import { FormContainer, TextField } from '@/src/future'; | ||
|
||
afterEach(cleanup); | ||
|
||
describe('built-in field with auto input ids', () => { | ||
test('should have auto-generated input ids', () => { | ||
const onChangeSpy = jest.fn(); | ||
const defaultValue = { | ||
firstName: '', | ||
lastName: '', | ||
}; | ||
|
||
const renderForm = () => ( | ||
<FormContainer defaultValue={defaultValue} onChange={onChangeSpy}> | ||
<div data-testid="firstNameWrapper"> | ||
<TextField fieldKey="firstName" label="First Name" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this approach a lot - you can specify your fields with JSX and control where it goes, add wrapper stuff, etc. I also really like the Just my first impressions, but this is all feeling very nice! |
||
</div> | ||
<div data-testid="lastNameWrapper"> | ||
<TextField fieldKey="lastName" label="Last Name" /> | ||
</div> | ||
</FormContainer> | ||
); | ||
const { getByTestId, rerender } = render(renderForm()); | ||
|
||
const firstNameInput = getByTestId('firstNameWrapper').querySelector( | ||
'input' | ||
); | ||
const lastNameInput = getByTestId('lastNameWrapper').querySelector('input'); | ||
|
||
const firstNameId = firstNameInput.getAttribute('id'); | ||
const lastNameId = lastNameInput.getAttribute('id'); | ||
|
||
expect(firstNameId).toBeTruthy(); | ||
expect(lastNameId).toBeTruthy(); | ||
expect(firstNameId).not.toEqual(lastNameId); | ||
|
||
fireEvent.change(firstNameInput, { | ||
target: { value: 'Joe' }, | ||
}); | ||
expect(onChangeSpy.mock.calls[0][0]).toEqual({ | ||
firstName: 'Joe', | ||
lastName: '', | ||
}); | ||
fireEvent.change(lastNameInput, { | ||
target: { value: 'Foo' }, | ||
}); | ||
expect(onChangeSpy.mock.calls[1][0]).toEqual({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
}); | ||
|
||
rerender(renderForm()); | ||
|
||
// Ids should not change when rerendering. | ||
expect(firstNameId).toEqual(firstNameInput.getAttribute('id')); | ||
expect(lastNameId).toEqual(lastNameInput.getAttribute('id')); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/*global jest, describe, test, expect, afterEach*/ | ||
import React from 'react'; | ||
import { render, fireEvent, cleanup } from 'react-testing-library'; | ||
|
||
import { ExampleForm, defaultValue } from '@/demo/future/BuiltInInput'; | ||
|
||
afterEach(cleanup); | ||
|
||
describe('built-in input', () => { | ||
test('should fire onChange correctly', () => { | ||
const onChangeSpy = jest.fn(); | ||
const { getByLabelText } = render( | ||
<ExampleForm defaultValue={defaultValue} onChange={onChangeSpy} /> | ||
); | ||
fireEvent.change(getByLabelText('First Name'), { | ||
target: { value: 'Joe' }, | ||
}); | ||
expect(onChangeSpy.mock.calls[0][0]).toEqual({ | ||
firstName: 'Joe', | ||
lastName: '', | ||
}); | ||
fireEvent.change(getByLabelText('Last Name'), { | ||
target: { value: 'Foo' }, | ||
}); | ||
expect(onChangeSpy.mock.calls[1][0]).toEqual({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/*global jest, describe, test, expect, afterEach*/ | ||
import React from 'react'; | ||
import { render, fireEvent, cleanup } from 'react-testing-library'; | ||
|
||
import { | ||
ExampleForm, | ||
value, | ||
} from '@/demo/future/ControlledCustomFormWithHooks'; | ||
|
||
afterEach(cleanup); | ||
|
||
describe('controlled custom form with hooks', () => { | ||
test('should fire onChange correctly', () => { | ||
const onChangeSpy = jest.fn(); | ||
const { getByLabelText, rerender } = render( | ||
<ExampleForm onChange={onChangeSpy} value={value} /> | ||
); | ||
fireEvent.change(getByLabelText('First Name'), { | ||
target: { value: 'Joe' }, | ||
}); | ||
const newValue = onChangeSpy.mock.calls[0][0]; | ||
expect(newValue).toEqual({ | ||
firstName: 'Joe', | ||
lastName: '', | ||
}); | ||
rerender(<ExampleForm onChange={onChangeSpy} value={newValue} />); | ||
fireEvent.change(getByLabelText('Last Name'), { | ||
target: { value: 'Foo' }, | ||
}); | ||
expect(onChangeSpy.mock.calls[1][0]).toEqual({ | ||
firstName: 'Joe', | ||
lastName: 'Foo', | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the
setupTestFrameworkScriptFile
option injest.config
would call setupcleanup
for all tests. Then we wouldn't need to explicitly call it in every test file.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that seems reasonable. The only downside of that is you can't opt out of that behavior then? For example, if you wanted to do some iterative tests on the same DOM, you could use
afterAll
, but with the global setting, your DOM would be wiped out each time.