-
Notifications
You must be signed in to change notification settings - Fork 844
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
Form tests - Phase 1 #196
Form tests - Phase 1 #196
Conversation
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.
Sweet! I have some suggestions, including ways we can reduce the size of these snapshots.
}); | ||
|
||
describe('label', () => { | ||
[(<span>Label</span>)].forEach(value => { |
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 think we need to change this to "Label"
and wrap the value in <span>
on line 71, or else the description text for this test outputs [object Object]
in the snapshot.
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.
Also, if we're not actually iterating over multiple values, I think it makes sense to make the test simpler and terser. This makes the test more readable and eliminates any confusion that we may intend to support multiple different types of label inputs.
describe('label', () => {
test(`is rendered`, () => {
const component = render(
<EuiCheckbox
{...checkboxRequiredProps}
label={<span>Label</span>}
/>
);
expect(component)
.toMatchSnapshot();
});
});
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.
Sounds good!
|
||
describe('props', () => { | ||
test('id is required', () => { | ||
expect(() => render( |
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.
We can remove this render
call since we're not generating a snapshot or simulating user interaction with the component.
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.
Nice, good call!
}); | ||
|
||
test('onChange is required', () => { | ||
expect(() => render( |
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.
Same here.
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.
++
}); | ||
|
||
describe('checked', () => { | ||
[true, false].forEach(value => { |
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.
For boolean props like this, I've usually just been asserting the true
value out of pragmatism, since it seems unlikely we'd break the behavior of this component when the prop is false
. Also, since false is the default then we're already implicitly testing this behavior in all of our other tests. This reduces snapshot code a bit. What do you think? Do you think it's worth keeping both assertions?
describe('checked', () => {
test(`is rendered`, () => {
const component = render(
<EuiCheckbox
{...checkboxRequiredProps}
checked
/>
);
expect(component)
.toMatchSnapshot();
});
});
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.
Let's do it!
onChange: PropTypes.func.isRequired, | ||
type: PropTypes.oneOf(TYPES), | ||
disabled: PropTypes.bool, | ||
*/ |
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.
We can delete this commented-out code.
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.
Ugh I'm trying to not leave these in, but apparently need to do better
<EuiCheckboxGroup | ||
options={[ | ||
{ id: '1', label: 'kibana', disabled: false }, | ||
{ id: '2', label: 'elastic', disabled: true }, |
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.
Looks like disabled
is a prop for the EuiCheckboxGroup
itself. So we can remove it from these options objects and on lines 38-39. We need to add another test for this prop though.
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.
Good call!
describe('EuiCheckboxGroupMockedCheckbox', () => { | ||
test('is rendered', () => { | ||
const component = render( | ||
<EuiCheckboxGroup onChange={() => {}} {...requiredProps} id="foobar" /> |
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.
Do we need to pass in id
?
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'm passing it in as an arbitrary property that will fall into the ...rest
and ensure they are still rendered. I can use a different property, but I feel that's a valuable thing to ensure
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 think you're right that we should be testing that we're able to pass arbitrary props in and they'll pass-through. But that's also what the requiredProps
are for, so I think passing those in already fulfills this intention. What do you think?
@@ -0,0 +1,32 @@ | |||
import React from 'react'; |
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.
We can use sinon stubs to simplify our assertion:
import React from 'react';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { EuiCheckboxGroup } from './checkbox_group';
// This exists because we need to run the following tests
// without mocking the Checkbox component, such as testing
// an interaction that is handled by the Checkbox component.
describe('EuiCheckboxGroupUnmockedCheckbox', () => {
test('onChange receives id of changed checkbox', () => {
const onChangeHandler = sinon.stub();
const component = mount(
<EuiCheckboxGroup
options={[
{ id: '1', label: 'kibana', disabled: false },
]}
idToSelectedMap={{
'1': true,
}}
onChange={onChangeHandler}
/>
);
component.find('input[type="checkbox"]').simulate('change');
sinon.assert.calledWith(onChangeHandler, '1');
});
});
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.
Fab!
@@ -0,0 +1,52 @@ | |||
import React from 'react'; |
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.
Since this file contains our basic set of tests, I think we can treat this file as the "regular" test file and call it checkbox_group.test.js
. And then we can treat the other one as the extension beyond these basic tests. Since it's mostly testing behavior in response to user interaction, how about we call it checkbox_group.behavior.test.js
?
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.
Sounds good!
describe('props', () => { | ||
describe('isInvalid', () => { | ||
[true, false].forEach(value => { | ||
test(`${value} is rendered`, () => { |
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.
isInvalid
will actually call setCustomValidity('Invalid')
on the input under the hood (https://github.com/elastic/eui/blob/master/src/components/form/validatable_control/validatable_control.js#L19), so it won't render anything. I think this test should ideally look something like this:
describe('isInvalid', () => {
test(`sets custom validity error`, () => {
const component = mount(
<EuiFieldNumber
isInvalid
/>
);
expect(component.find('input').hostNodes().getDOMNode().validity.customError).toBe(true);
});
});
Unfortunately, jsdom doesn't support this API yet so it doesn't work. What do you think of leaving a TODO for now?
describe('isInvalid', () => {
test('sets custom validity error', () => {
// TODO: Assert that the input's validity.customError is set to true. Blocked by
// https://github.com/tmpvar/jsdom/issues/544. What we want to do is:
// expect(component.find('input').hostNodes().getDOMNode().validity.customError).toBe(true);
});
});
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.
Nice find for sure.
My thoughts are since this is a unit test, we shouldn't need to worry about what the dependencies are doing. We just need to verify that the property is indeed added, which I think the snapshot test does.
This also means we can freely change the implementation details of EuiValidatableControl
without needing to update these tests.
Thoughts?
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.
Ahh you're right. Sounds good!
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.
Looking good! Just had 3 suggestions and 1 question.
}); | ||
|
||
describe('disabled', () => { | ||
[true, false].forEach(value => { |
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.
Can we get away with just testing true
?
// This exists because we need to run the following tests | ||
// without mocking the Checkbox component, such as testing | ||
// an interaction that is handled by the Checkbox component. | ||
describe('EuiCheckboxGroupUnmockedCheckbox', () => { |
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.
Can we rephrase this as "EuiCheckboxGroup behavior" to make it a bit more readable and reflect the file name?
test('is rendered', () => { | ||
const component = render( | ||
<EuiCheckboxGroup onChange={() => {}} {...requiredProps} /> | ||
<EuiCheckboxGroup onChange={() => {}} {...requiredProps} id="foobar" /> |
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.
What were your thoughts on id
here? I feel like requiredProps
is sufficient for verifying that DOM attributes get passed-through to the element that gets rendered.
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.
Sounds good!
describe('EuiCheckboxGroup', () => { | ||
jest.mock('./checkbox', () => ({ EuiCheckbox: 'eui_checkbox' })); | ||
|
||
describe('EuiCheckboxGroupMockedCheckbox', () => { |
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.
Can we rephrase this as "EuiCheckboxGroup (mocked checkbox)" to make it a bit more readable?
@cjcenizal Ready for another pass! |
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.
🎸 LGTM!
Relates to #177
Adds tests for:
For
CheckboxGroup
, there are two separate test files. This is because for one, we want to mockCheckbox
and the other we do not. It's difficult/complicated to handle mocking and unmocking in a single file.