Part 2 — You’re here now.
Now let’s write a more useful feature test that will help us write corresponding unit tests and code to actually build the functionality we want:
Let’s break it down in plain english.
Line 9
Create a todoText
string constant.
Line 10
Navigate to the page.
Line 11
Find the .todo-input
element and enter the todoText
.
Line 12
Click the .todo-submit
element.
Line 13
Find the .todo-text
element and assign its text value to the actual
const.
Line 15
Assert that actual
is equal to todoText
.
Run the test and watch it fail. You should have 1 passing and 1 failing e2e test, if both are failing you have an issue with watch-mode and should run the e2e tests manually. I haven’t had time to properly debug this watch issue yet but go right ahead if you’re suitably motivated.
Before we write the necessary code we’re going to write appropriate unit tests, and before that let’s fix the eslint errors we have in our e2e test. Add the below comment to the top of the e2etests/test.js
file:
/* global describe, it, browser */
If we get another eslint error in that file complaining about chai
being a devDependency there is a known bug that has caused some debate over eslint rule merging. It’s not relevant to what we’re doing here but if you want to know more start reading here.
The fix for the moment is to add another rule to our .eslintrc.js
rules: {..."import/no-extraneous-dependencies": [2, { devDependencies: true }],},
This is what my .eslintrc.js
looks like now:
Let’s create the following folder structure src/components/addTodo/
and within that make a placeholder index.js
and leave it empty. We don’t want to write any code we don’t need, and we only need just enough code to make our tests pass. So let’s write a unit test in src/components/addTodo/test.js
:
Line 5
Import an AddTodo
component from the index.js
in the same folder as this test.
Line 9 — 10
Shallow render the component to the component
const and expect that it exists.
This unit test should fail because our .../addTodo/index.js
returns nothing and therefore can not render. So let’s create our component:
You should just leave your unit tests task running, if it isn’t then run _npm run test_
.
Now our test should pass. Let’s write the next test, the minimum we have to do to inch closer to our end goal:
The test should fail, let’s do the minimum to make it pass:
At this point we can incorporate our AddTodo
component in to our main App
, this should help us pass the first error in our e2e test and show us what more we need to do:
Line 2
import the AddTodo
component.
Line 7
Add it to our App
.
Line 6
Add an appropriate heading.
Our e2e test should now show us that it was able to find the .todo-input
and enter some text in it, but it was not able to find the .todo-submit
, so that’s exactly what we’re going to unit test and code next:
So let’s add the .todo-submit
element that we need, knowing we’ll want it to be a button. Now I know what you’re thinking, ‘An input, a submit button, what next — a form?’, yes you’re right, a form!
Our e2e test can enter text in to the input and click on the submit button, however we need to hook up the functionality for it, but let’s write an appropriate unit test first:
Line 1
Add an exception to eslint for jest
.
Line 14
We assign the jest.fn()
method as our mock. We use this because it comes bundled with our app and gives us nice helper methods to make it easy to test that the function was called whilst not worrying about it’s actual implementation.
Line 4
We import the mount
module from enzyme
.
Line 15
We actually mount
our component so we have access to the event
to preventDefault()
on the form submission. We’re also instantiating it with a submitTodo
prop which we’re giving our submitMock
function.
<AddTodo submitTodo={submitMock} />
translates to; Create an AddTodo component, it has a property called submitTodo
, when we instantiate the component pass in submitMock
as that property. Later in the component itself we’ll write some validation for the prop so we ensure it’s a function.
Line 17 — 19
We expect that our submitTodo
starts off with zero calls, and once we submit our form it has one call, confirming the event was triggered.
We’re about to make substantial changes to our AddTodo
component, but first let’s install the prop-types node package:
npm install --save-dev prop-types
Let’s walk through the changes:
Line 4 — 7
We’ve changed the syntax of our component a little. Because we want to do more than just return some markup we’ve gone from the starting construction of:
const Something = () => (<p>Return something</p>);
To:
const Something = () => {const text = "Return something";
return (<p>{text}</p>);};
Then:
Line 5
We’re going to need a mutable variable to store our input text, so we do a let input;
.
Line 19 — 21
When we submit our form we want to send the input’s text so we tell React to store a ref
(reference) to this DOM element, that means that React will be able to track and use it in the component. And then assign the element to the mutable input
var.
There are better ways to do the above but I think it’s a nice opportunity to explore the functionality of a _ref_
, read more here.
Line 10 — 14
We tell the form what to do onSubmit
. Firstly preventDefault
actions, in this case reloading the page since that’s the default behaviour of form submission. Then call a submitTodo
function with the input.value
, in this case the text that is entered in to the input. Then set the input.value
back to an empty string so it clears the input text box.
Line 4
We pass the submitTodo
function in to our component as a property, aka prop
. Read about components and props.
Line 2
We have to import the prop-types
node package to give us prop validation and defaults when we don’t provide values for them.
Line 32 — 34
We have to validate the prop
in our component so we tell React it’s going to be a function and it’s required when we instantiate this component. Read about validating property types.
The test we just wrote passes but our changes to the AddTodo
component have broken two other tests. We’re now telling the component that it requires a submitTodo
prop when instantiated, it’s a function, it’s required, and we haven’t provided a default.
// src/components/addTodo/index.js// Line 32 - 34
AddTodo.propTypes = {submitTodo: PropTypes.func.isRequired,};
Now in our tests we want each of the instances of the component to also have this prop, but this is going to be the first of a number of refactors so let’s optimise our setup a little, remembering DRY (Don’t repeat yourself).
Line 8 — 17
We create a mutable component
variable and assign it our shallow
mounted component beforeEach
test, and remove the assignment from each individual it(...)
block.
Line 33
We still have to keep this assignment because we actually want to mount
it when rendering for this test.
Line 1
Add beforeEach
to the eslint exceptions.
Now we have our App test to fix. It’s telling us exactly where the error has occurred, App.js Line 7
:
Line 7
For now we’re just passing an empty ES6 arrow function () => {}
in to our AddTodo
‘s submitTodo
prop. It will allow us to pass our component validation and have passing unit tests once again, although it won’t help our e2e test progress or actually add any functionality yet… but that’s what we’ll do next.
What our App looks like now.
We’ve learned how to:
Now we need to go beyond just creating UI and start setting up our event dispatchers via actions
and event handlers via reducers
.
Part 2 — You’re here now.