Sanjay Purswani

@sanjsanj

A guide to TDD a React/Redux TodoList App — Part 4

Part 1 — Link.

Part 2 — Link.

Part 3 — Link.

Part 4 — You’re here now.

The TodoList component

We’ve got a couple more things to do to make our failing e2e test pass; have a .todo-text element and have it contain the text we submit in our form.

Create the folder structure src/components/todoList/ and then write our first unit test in test.js that will allow us to create our component:

src/components/todoList/test.js

This should be self explanatory, it’s very similar to how we started building our addTodo component.

What the component should return in the index.js:

src/components/todoList/index.js

Line 4 We’ll wrap our component in a <ul>.

Then let’s test for a todo in the list:

src/components/todoList/test.js

Line 8 — 13 We create our todos prop, an array containing one todo as an object with id and text.

Line 15 We actually pass that todo in as a prop to our component.

Line 22 We expect that our todoList will contain a .todo-text element with our todo text.

Now we just have to get our todo to render:

src/components/todoList/index.js

Line 2 import PropTypes for our validation.

Line 18 — 25 We tell our component that it will receive a prop called todos, it will be an array of objects, its id will be a number and text will be a string, and everything is required. React prop-types validation.

Line 4 We pass the todos prop in to our actual component.

Line 5 — 8 We map over our array, returning an li whose contents are the todo.text and it has a key of todo.id, the key is required by React to more efficiently know how to re-render the DOM and if you don’t have it you will get a console warning in dev.

Line 11 — 15 We return our ul wrapper with all the li's for each todo in our state.

Displaying todos

At this point our TodoList component knows how to render itself so can be wired in to our main App.

src/App.js

Line 5 import our TodoList component.

Line 18 — 23 Provide proper validation for it.

Line 8 Bring our todos array from state in to our App as a prop so that we can pass it down. We’ve made it available as a prop thanks to line 26.

Line 12 Add the TodoList component to our App and pass todos from state to it.

We now get a test error because we’ve told the component it requires a todos array but haven’t passed it in:

So we make that change:

src/App.test.js

Line 15 Simply pass an empty array to our todos prop, that’ll stop our App.test complaining. We don’t want to unit test child components at the parent level.

Now if we view our App in the browser we’ll find that we can actually display those lovely todos we’ve been storing in state. Huzzah!

And now our e2e test should pass:

Deleting a todo

First we write our e2e test:

e2etests/test.js

Add Line 12 — 21 above to your e2e test.

This test runs through the same procedure as the one before and then clicks the .todo-delete element and then expects the selector’s state to equal ‘failure’. If it existed (was not deleted), it would equal ‘success’.

I like to implement functionality by writing the action, the reducer, creating the component, then adding it to the App.

So let’s test for the right action. First let’s create an appropriate constant:

src/constants/index.js

Line 3 Our new DELETE_TODO constant.

Then, in the action all we need to know to delete a todo will be its unique id.

src/actions/test.js

Then add the code to make it pass:

src/actions/index.js

And now let’s unit test our reducer functionality:

src/reducers/test.js

This should hopefully make a lot of sense by now. We’re giving our reducer a startingState which includes a todo, passing it in the action we just created which should delete that todo from the store, and expecting the returned state to indeed not contain the todo.

We now need to add another case in our reducer to handle this:

src/reducers/index.js

When the DELETE_TODO action.type is encountered we return a new state which includes all of the existing ...state, and we override the todos array to be only a filtered list where todo.ids are not the same as the action.id. Therefore all the todos that are not the one we wanted to delete.

That was easy, wasn’t it?

Now let’s test our component can let us delete a todo:

src/components/todoList/test.js

Line 1 Add jest to the eslint exceptions.

Line 8 Create a mock function for our delete functionality.

Line 10 — 18 Refactor our previous single todos prop to be an object that holds all the props we need.

Line 20 Let our component shallow render and unpack all the props previously specified.

Line 30 — 34 Test that clicking on a .todo-delete element does indeed call our deleteTodo function.

And then the code to make our test pass:

src/components/todoList/index.js

Line 36 Validation our deleteTodo prop.

Line 4 Pass it in to our component.

Line 8 — 14 Add a ‘Delete’ button which will call the deleteTodo function with the todo.id as the argument on the onClick event.

Now we need to hook it up to our UI via the App:

src/App.js

Line 36 — 38 Create the deleteTodo prop that will dispatch the appropriate action.

Line 24 Validate it.

Line 8 Add it to the props for our component.

Line 12 Add it to the TodoList component.

And it’s really just that simple. Unit tests and e2e tests should be passing and the functionality should be working in our App in the browser.

Undelete a todo

If you’re still with me then you’re doing pretty good, and definitely not a newbie! So it’s time for a little challenge. I’m not going to tell you how to do this, why not give it a go yourself?

Think about what you need.

Just above you were able to TDD all the functionality to delete a todo, do the same for undelete. Also think about how, where and when you’re going to store your last deleted todo. With that in place it should click!

It won’t be too difficult to do this, it may take half an hour or a couple of hours but you can do it. Try and TDD it for a solid challenge.

Then check out how I did it:

Disabling buttons

So our App is sort of working, well the stuff we’ve tested is working. But if you try and use it you may discover some bugs that we’ve not coded for. You could branch off and try and fix them and add more functionality if you wish, but one final challenge I’ll pose you before you do that is to disable the ‘Add Todo’ and ‘Undelete’ buttons when it’s not appropriate to click them.

Have a think about how you could implement this, google around a bit and give it a shot. We both know you can do it!

When you’re done have a look at how I did it. I definitely wouldn’t say there was a right or a wrong way, just different approaches:

Style it

As a front ender it pains me to even call this styled but I guess this isn’t a CSS tutorial. If you want to make it pretty then fill your boots. This is just what mine happens to look like:

High production value

Deploying to Heroku

This is optional and just for a laugh, but if you do want to deploy your app online easily for free then Heroku offers a quick fix. This will allow you to play around with production builds and your own CI/CD pipelines if you so wish.

First Get set up with Heroku.

Then follow the instructions for the heroku-buildpack for React.

Visit the App online here.

Writing the readme

I’ve gone with the simple approach of just listing out how to setup, run and test our App.

Previous parts

Part 1 — Link.

Part 2 — Link.

Part 3 — Link.

Part 4 — You’re here now.

Aaand that’s it…

Yup, it really is. Have any feedback? Leave a message below.

I’m outie 9000.

More by Sanjay Purswani

Topics of interest

More Related Stories