- In a real application, users click buttons,
enter the values into text boxes,
and we have event handlers that process those events.
And in your tests, you have to make sure
that those event handlers work properly.
If the user clicks this button,
does my application respond properly?
So you can simulate events in a react application.
Enzyme allows you to simulate from a test harness,
the user interacting with the application.
The user isn't really interacting with the application
because your test is automated, but you can emulate it.
So this works if you're doing shallow rendering
or full DOM rendering.
If you call the shallow function,
or if you call the mount function,
Enzyme will allow you to emulate events
upon your components.
But if you're just calling the render function,
then it doesn't work.
So if you call the shallow function, or the mount function,
Enzyme helps you simulate events on your content.
So why bother?
Well, like I said, because in a real application,
the user will interact with your elements
but in your test harness,
the user isn't actually sitting there, it's automated.
So you can programmatically emulate user events
to see how your program responds.
Does my component update itself properly
when the user does this event?
So what I've done is I've slightly enhanced
the sample application.
If you go into Enzyme Events,
there's a demo application there and it's similar to before,
but it's been enhanced.
If you wanna run it yourself,
go to the Enzyme Events demo app for lesson 10,
npm install, to install the libraries.
And then npm start or yarn start to start the application.
And what you'll find is that
the application now has two text boxes.
The user can enter a minimum text value,
a minimum product value and a maximum product value,
and the application will only display products
in that range.
So that makes it a little bit more interactive.
Let's see how that looks.
I've built the application and it's running.
Let's see what it looks like.
So here it is.
Notice the text boxes at the top.
They allow me to enter a minimum price.
The default minimum is zero
and the default maximum is a very large number.
So by default, it displays all products.
But if I typed in here a minimum of 300,
then it'll only display products 300 or above.
I can also specify a maximum value.
I can say, let's display,
now you'll have to bear with me a bit.
If I display maximum of 400,
it'll only display products that cost up to 400
but not products that cost more.
Interesting, but it always displays
the total number of products in stock
because my company does sell three products,
but only two of them are in that price range.
And also, obviously, you can specify the minimum
and the maximum.
Give me products that cost between 300 and 400,
or let's say between 200 and 400.
Okay, so we get just the ski boots.
Did you notice as well
that the total value is always correct?
Based on what it's displayed here,
it'll calculate the total based on the filtered products.
So at the moment, the total value there is 1,000.
If I clear some of these filters,
then it'll be 1,000 plus another 1,000,
that's a total value 2,000.
And if I revert back completely,
maybe actually you get the idea.
That's how it all kind of works.
Right, so we're gonna have to simulate events
from the user.
But before we look at how to simulate events,
I thought it'd be useful to just explain
how the application works itself before we try to test it.
So let's take a look at the code.
Here's my code editor.
It is really similar to the previous examples.
So it's just the product list that's different.
In the product list, we have some interesting bits in here
that I'm gonna need to explain.
I'm gonna spend a few minutes just going through this.
So first of all, this thing here,
react has this concept called state.
When a component's being rendered,
if you want to remember values across multiple refreshes,
then what you do is you store that data in react state.
It's like a permanent data store
that the component always has access to.
As it's being refreshed, it remembers the old value.
I'll explain that in a bit more detail in a moment.
Okay, we'll come back to that.
I've got two functions here.
You know that you can have nested functions in JavaScript.
My outer function can have nested functions.
This function will be invoked when the user types a value
into the minimum text box.
And this value, it basically changes the minimum.
This function will be invoked
when the user types in the maximum text box.
So again, I'm gonna come back to those in a moment.
Let's have a look at the actual webpage itself.
If there aren't any products, then it displays that message.
And that's not very interesting.
Let's come down here, this is more interesting.
First of all, I filter the products array.
Arrays in JavaScript have a filter function.
It basically, you give it a lambda,
and filter will invoke that lambda for each element.
For each product, it'll pass it into here.
And what you do is you return either true or false.
If you return true, it means keep that element.
If you return false, it means don't, it means discard.
So this will keep the products
that have a price in the minimum-maximum range.
Those products will be retained in this collection,
that's just going to be the list of products
in the filtered price range.
So what it does,
it displays how many products there are altogether.
And then down here, if I just skip down there,
it'll only display the filtered products.
So if I'd entered a minimum and a maximum,
only the products in that filtered range will be displayed.
Above it, we've got a couple of text boxes.
There's a text box here, it just says minimum price.
Okay, so that's the label there.
And then there's a text box here.
Here's the text box
where the user can enter the minimum price.
Okay, so when the user changes the value in that text box,
it'll call the function.
So the on change event,
it'll call the on change min function
whenever I change the value in here.
So it calls the on change min function.
And obviously the same thing here, for my second text box,
when the user types a value into the max text box,
then it'll call the on change max.
So that's interesting in itself.
This is how you handle events in react,
you say on, and then the name of the windows,
so the name of the HTML event,
and then you give it the name
of your event handler function.
Don't forget the curly brackets.
Whenever you've got JavaScript
in the middle of all this XML malarkey,
remember the curly brackets,
to say this is a JavaScript function name to invoke.
So the user will type a value
into the minimum or the maximum text box, like so.
As soon as you do that, it'll call one of these functions.
So let's go back up and have a look at those functions,
on change min and on change max.
So before I can describe what those functions mean,
I need to go back and explain the idea of state.
If you declare a local variable inside a function,
this is my render, the component function.
If I declare a local variable in here
as soon as the function is finished
and the component's been rendered,
the local variable would disappear.
If you wanna store value for longer than that,
we say, react, usestate.
It basically stores the memory inside react
to make it permanent.
The way it works is when you call this function,
you say, "I wanna store a value."
Oh, the initial value of it is zero.
The function gives you back two variables.
It actually returns back two things.
It returns you back a variable that basically represents
the value zero.
And it gives you back a function pointer.
It says, "If you wanna change the value
can you tell me, please?
Because if you change the value,
react will automatically re-render your component."
When you change the value that react is holding in state,
when you call one of these helper functions
to change the value, react will say,
"Oh, you've changed the value of your data?"
It'll automatically re-render,
it'll call your function again, to re-display itself.
So what's gonna happen then?
It'll say to react,
"Okay, I want to store the value zero,
let's have a variable called min that grabs that value."
So min here is initially the value zero.
This is a return value,
this is returned from react, usestate.
It says, "Here's a function that you can call, call me back,
if you want to change the value of the minimum."
And that's what happens here.
If the user typed in the value in the min text box,
like that, the change event occurs,
it comes into this function.
This event basically represents what just happened.
It's the change event.
Events in HTML have a target property,
and the target property says,
"Which element generated the event?"
Well, it was the text box, wasn't it?
So e.target is the text box that generated the change event.
Get the value from the text box.
What is the value in the text box 1234?
It could have been empty.
If it's empty, then we just used that value there, zero.
This is like the way of saying,
give me the value of the text box at all zero as a default.
We have to convert it into a number, by the way.
Whenever you get the value from a text box
it comes back as a string.
Well, that's not much use
if you're doing mathematical range check-in.
So convert that string into a number.
That's the number that the user just entered
into the minimum text box or zero as a default.
That's the value that the user just entered.
And what you do here is you say set min.
At this point I'm calling back the function
that react gave me.
React gave me that function, it said,
"Call that function,
if you want to change the value of the minimum price."
So when I call the set min function, react says,
"Oh, you have changed the value of some data."
It will automatically re-display the component
based on the new value of the minimum.
It automatically re-renders everything
whenever you change state.
So whenever I change a value in here,
and I tell react that the state has changed,
react will automatically re-render my component
per key stroke.
And obviously, it's the same idea when the user changes
the maximum value.
In here, this event will describe the event.
It'll say, "What just happened?"
There'll be a text box.
That's the value of the maximum text box
converted into a number.
Oh, did you know this?
I've got a large default.
If the maximum text box is empty
then by default, it'll use this,
it'll use a million as the default upper limit.
That's the value for the maximum.
Whenever you change, whenever you call one of these state
to change your functions,
react will automatically re-render,
it'll call your function again,
to repaint your user interface
with the new filtered products based on the new value
for the minimum and the maximum.
So that was quite useful actually,
that shows what happens, it shows how state can help us
manage data across multiple refreshes
and how to handle events.
But of course, this is just in the real world.
What about when we're testing this?
How can I simulate the user interacting with the text box?
How can I simulate the change event?
Well, that's the whole point of this discussion.
You call in your text code, you've rendered the component,
you get back a wrapper.
You call simulate and you give it two parameters,
this is simulating an event and an event argument object.
So here's the code, we're gonna look at.
Well, first of all, it gets the text box,
that was the text box with an ID of min.
Upon that text box, it simulates the change event.
And notice the second parameter, the second parameter here
is the event object.
So remember when you have an event object,
this object here is basically the event object
that I'm passing in.
So event objects, this is going to be the event object.
Event objects in HTML have a target property.
What was the element that generated the event?
So in our simulated world, well, that would be a text box.
And text boxes have a value.
So what I'm doing here is I'm creating this object
and I'm setting the target property to point to this object.
This object here is like a very, very cute
and careful simulated text box
whose value I've just simulated.
I've simulated the user change in the minimum to be 101,
as if the user had typed this in.
I've just simulated that event 101 as text.
Obviously it gets converted into a number by my code.
So the target property identifies the text box
that there is effectively the bit that matters
for the text box.
And that text box I've simulated
the user change in the value as if they typed it in
but they haven't, I've programmatically assigned it.
It's as if the user just set the value to 101.
What should happen in that case?
Well, what should happen in that case is,
we started off with three products.
If the user types in 101,
we should just have two products left.
Let's check that it actually works well.
It does work, we can write tests to ensure that
there are just two products displayed now,
and what should the total value be?
That's what our test will establish.
Right, so simulating the user setting the minimum price.
Right, so first of all, you can either call
the shallow function or you can call the mount function.
So I call the mount function,
and I mount the product list component
and I give it an array of three products.
It gives you back a wrapper.
I just wanna simulate, I just wanna check at the moment
there should be three products.
It should render three products.
Remember, in my code, initially, if there aren't any minimum
or maximum entered,
then the number of products that are rendered
should be three, unless the user types anything in,
there should be three products rendered.
Let's verify that there are three products
at the moment initially.
Okay, now let's simulate the event.
In my rendered content, find the minimum text box,
simulate the change event, such that the text box value,
the minimum, is now 101.
As soon as you do that,
code should happen in your component.
In your component,
the user has just typed the value effectively
into the text box.
That should call this function
to change the minimum to be 101.
That should cause the component to re-render
with a minimum of 101.
So down here, it'll say,
"Oh, let's filter the products based on the minimum of 101."
And then, what products should be left
if the minimum price is 101?
So if the user entered 101,
we should see two products there, carving skis, 1,000,
ski boots are 250.
Let's verify that did all actually happen in our test.
Right, so there should now just be two products,
the two products that cost 101 or above.
What about if the user enters a value
in the max text box?
Same idea, this is my second test.
First of all, render in all products,
there should be three products displayed.
Let's simulate the user entering into the max text box,
changing the max to be 999.
So what value should we get back if the user enters
a value of 999 in here?
999.
Again, we should just see those two.
The skis have gone because they were too expensive.
We just have those two products left.
So we can verify that there were just two products left
in our page.
There is more detail.
If you want to, you can go deeper into the content to see
in exact detail, what did I get back?
So in my test here, I can say,
simulate the user entering the value of 101,
there should be two products.
Simulate the user entering a minimum value of 251,
there should just be one product.
If you look at the numbers for the prices, you'll see.
Simulate the user change in the minimum to be 1,001.
In which case, there shouldn't be any products,
because they all cost less than that.
And same idea for the max.
When you doing unit testing,
you often check different numbers
to make sure that the ranges work.
Initially, all products are rendered.
Simulate the maximum text box change in to be 999.
That should just give us two products.
Simulate the maximum value being 249.
That should just give us one.
Simulate then the user setting,
the maximum value to be 99, and then we should have zero.
If you look at the values,
you'll see that that filtering is correct.
So we just need to run the tests
and see if it works, and it works.
All right, so that's a really useful exercise, I think.
First of all, state management, handling events,
and then simulating the user, invoking those events,
to make sure that the content that was rendered was correct.