-
Notifications
You must be signed in to change notification settings - Fork 75
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
ProjectStatus unit tests #4127
ProjectStatus unit tests #4127
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.
Not sure if testing page layout elements is necessary, or a good practice. Happy to remove if not needed/wanted.
No, we do not need to re-test what React and JSX is doing for us for standard HTML elements. We trust that our dependencies are tested and we should not spend time re-testing core functionality of them. We do we want to test that our custom React components are rendered.
So far I haven't been able to write tests that simulate a user clicking the WorkflowToggle checkbox, getting the WorkflowDefaultDialog modal to open, and testing its functionality.
Testing the interaction between components are integration tests. Just focus on writing unit tests for this component. The dialog component already has its own unit tests written, too.
Let me know if you need further clarification on what should be covered.
PR UpdateThanks for your input @srallen :) RFC: I was writing a test for rendering the WorkflowDefaultDialog component, and came upon an issue - the component is currently rendered once for every workflow, rather than just once, for the default workflow. (you can test this by inspecting the modal element on a project with multiple workflows). This seems like an issue that should be resolved, and I've refactored the code in ProjectStatus to only render one WorkflowDefaultDialog component when the modal is triggered (on a separate branch, not currently included here). However, updating ProjectStatus to only render one WorkflowDefaultDialog seems like it's outside of the scope of this PR. But, the issue was discovered in the process of writing the test for the presence of the component, which is within the scope of the PR... I pushed up changes that include a passing test for the WorkflowDefaultDialog component (it tests whether it renders one for each workflow), with a comment to refactor ProjectStatus so that we only render one component. Happy to either update ProjectStatus and the test within this PR, or to open up a separate PR to address the issue and fix the test there, just wanted to confer before continuing :) |
Let's get the fix in with this. Just keep the tests and the bug fix as separate commits. |
app/pages/admin/project-status.jsx
Outdated
this.setState({ | ||
dialogIsOpen: true | ||
}); | ||
} | ||
|
||
if ((defaultWorkflowId !== workflow.id) || (defaultWorkflowId === workflow.id && !workflow.active)) { | ||
if ((this.state.defaultWorkflowId !== workflow.id) || (this.state.defaultWorkflowId === workflow.id && !workflow.active)) { |
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.
As I was updating this conditional, I was wondering if it's necessary - I tried changing it to just be an else
attached to the conditional above it, and the UI functionality seems to be the same (tests all still passing as well).
Would appreciate input on whether it's necessary here to explicitly check for the opposite of the conditions described above.
PR Update
|
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 not sure if I need to write tests for other methods in this component, (e.g., project and workflow GET requests, componentDidMount, and the event handler methods).
- Event handlers should be unit tested with stubs, mocks, or spies.
componentDidMount
depends on the component. Shallow rendering doesn't fire this lifecycle method. In this case cdm is calling a method that is requesting data from the API- Testing the interaction with the API are integration tests, so for this PR, you do not need to worry about that.
For the bug, the reason multiple dialogs were being rendered is it is in the map. I didn't catch that before in the last PR. Recall it was originally placed there to bind the individual workflow ids to the click hander, but that's not being done anymore. Instead of redundantly setting the state with the default workflow id, pull the rendering of the dialog outside of the map of the workflows.
PR Update
|
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.
About the failing test, check out enzyme's docs on simulate
. The second gotcha gives a clue:
Even though the name would imply this simulates an actual event, .simulate() will in fact target the component's prop based on the event you give it. For example, .simulate('click') will actually get the onClick prop and call it.
The test is currently finding the last shallow rendered WorkflowToggle
and attempting to simulate the click event, which as enzyme describes, finds the onClick
prop and calls it. WorkflowToggle
doesn't have an onClick
prop. The prop handleToggle
is passed down to the child which does have an onClick
prop. So, this event should be unit tested on WorkflowToggle
. Testing the prop passing down to WorkflowToggle
and that the class method handleToggle
is called would be an integration test.
Also, just an FYI, the event variable isn't defined, so even if you were going to write an integration test, an undefined variable is being passed into the simulated event.
|
||
wrapper = shallow(<ProjectStatus />); | ||
|
||
wrapper.setState({ |
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.
This doesn't need to happen in the before. You're setting state in your tests already for the specific condition that's being tested.
|
||
describe('custom components', () => { | ||
it('LoadingIndicator renders if project is not in state', () => { | ||
wrapper.setState({ project: null }); |
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.
This doesn't need to happen once you remove the setState out of the before block. You do want to test what is rendered for the initial state. There should also be a unit test for when the workflows state is null (the initial state for workflows)
assert.equal(loadingIndicator.length, 1); | ||
}); | ||
|
||
it('LoadingIndicator does not render if project is in state', () => { |
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.
Perhaps put the unit tests that rely upon there being a project in the component state in its own describe block.
onChangeWorkflowLevelSpy = sinon.stub(ProjectStatus.prototype, 'onChangeWorkflowLevel'); | ||
handleDialogCancelSpy = sinon.stub(ProjectStatus.prototype, 'handleDialogCancel'); | ||
handleDialogSuccessSpy = sinon.stub(ProjectStatus.prototype, 'handleDialogSuccess'); | ||
handleToggleSpy = sinon.stub(ProjectStatus.prototype, 'handleToggle'); |
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 be consistent with variable names. If they're stubs, then name the variables stubs.
Thanks, yeah, I was thinking perhaps the issue was actually that I was still getting integration and unit tests mixed up - thanks for clearing that up. Tests now refactored into |
ed40f5b
to
ba00182
Compare
const versionListComponent = wrapper.find('VersionList'); | ||
assert.equal(versionListComponent.length, 1); | ||
}); | ||
|
||
it('renders a no workflows found message when no workflow is in component state', () => { | ||
const noWorkflowsMessage = wrapper.find('div[children="No workflows found"]'); |
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 was able to target this element using a solution suggested here, but, as one person comments, this seems tightly coupled to the implementation - would it be better to move this text to a Translate component, and then test whether what is rendered matches what is in the Translate component's content
prop? (something more like this)
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 don't need translations here. I'm of the opinion that finding specific DOM elements for a unit test will always be coupled with the implementation. It's unavoidable, however, I would avoid using a prop that is internal to the React API as well as using a selector that looks like a CSS selector when it's not. Here's how I'd write this test and the DOM traversal:
const noWorkflowsMessage = wrapper.find('.project-status__section').at(1).children().find('div');
assert.equal(noWorkflowsMessage.text(), 'No workflows found');
It also better matches the description of the test, which is that it renders a specific message.
- Adds basic page layout tests. - Adds tests for workflow settings. - Tests for modal dialog for toggle default workflow not working.
81f7192
to
6d7254b
Compare
Thanks, I'll keep that in mind going forward :) Just rebased this against master. |
Describe your changes
mount
instead ofshallow
render, but haven't had success with mount either.stub
instead of aspy
, and control/force the code down a specific path (ie, to click the toggle for a default workflow, then force 'ok'/'cancel')Review Checklist
rm -rf node_modules/ && npm install
and app works as expected?Optional
ChangeListener
orPromiseRenderer
components with code that updates component state?