- Here's a quick reminder of our product component class
from the earlier example.
It's in lesson 12, example two in the demo app folder.
If you remember, I had a top level component called app,
which passed in products, that passed in one product
at a time into a component called product component.
My product component received an input,
it received a product object as an input parameter,
and it could generate a sale event to indicate
that somebody had bought that product.
So the picture looked like this.
In my application component, it had three product objects,
product being just data.
For each product,
the application component generated a product component
and it passed that in as a parameter into here.
That was an input passed into the product component.
So the product component has a field called product,
which basically points to a product object like that.
And also a product component, there's a buy button
in my user interface whereby the user can buy that product.
In which case, my product component will
basically generate a sale event
which the application component can handle to display sales.
So that's an output event.
An output parameter, if you like.
My product can output a sale
to say somebody has just bought this product.
We've seen how this works and we've had a play with it.
This is the user interface.
Each product component is a blue box.
It displays the product data that was passed in
as a parameter and the buy button,
it generates a sale event.
That sale event is handled by the top component app.
It receives the sale information and it adds it
to a list that it displays on the top level,
like so.
So this application works and what we wanna do now is
to test this product component.
How can you test a component that has inputs
and has outputs?
How can you test that it's working correctly?
That's what we're going to see now.
How can we test this product component properly
given the fact that it has inputs
and it raises an output events.
How can we test that this is all working properly?
I'm glad you asked.
So what we're gonna do is in the product component spec,
we're gonna set up tests for input parameters
and for output events.
So let's have a look at what we've got here then.
So we have a couple of variables at the top.
First of all, we are gonna be working with product component
and remember how it works with Angular, with Jasmine.
When you are setting up a test environment,
you don't directly access the component,
instead you have this thing called a fixture
which is a wrapper over the product component.
So, let's see how it goes.
And here's the product data.
Now, in the real application,
the product data would've been passed in from my parent.
When you're unit testing the product component,
there is no parent,
you're just testing the product component on its own.
So we are gonna basically create like a dummy version
of this object as if we'd received it from our parent.
Before we go onto that bit of criteria, look at this.
Tell my test environment to create an instance
of product component.
Right, okay.
So it'll create an instance of product component.
A product component, as we've seen,
will have a product field that needs to be initialized.
And in normal circumstances, that would've been passed in
as a parameter for my parent component.
When you are testing, there is no automated creation
of parent components.
You just basically create what you're given.
So this will create a product component.
And also, remember that we have this kind
of fixture that wraps it.
So the fixture object kind of points
to the product component.
This is my product component here.
So the component variable basically points
to my product component.
Inside that product component, it has this product field,
which I'm initializing here.
Create a product object, a Bose sound system,
TV audio 450.
Okay, very nice.
So that's a product object
that I've just created manually.
Normally, that would've been passed in
from the app component.
I've created it manually here in my test and I hook it up
in my component, hook up my product field to point
to the product that I just created.
So in my component, go to the product field
and hook that product field up to the product object.
So here is my Bose sound system.
Do you say Bose or Bose?
Anyway, it's one of those, like so.
Oh, and then detect changes.
Remember, when you have a component,
whenever you change any data in the component,
like we just did, you need to call this function
to update the HTML.
So the HTML will now be updated to display, well presumably,
to display information about that product.
So we can check
that the HTML does display the product correctly.
If I receive an input parameter, a product,
does my HTML display the product detail correctly?
Okay, so let's have a look.
Here's my test.
I'm testing that my product component
should render the product details correctly.
So I'm gonna give you a quick reminder again.
So here is my fixture object.
That's my fixture object.
It points to the component.
That's my product component.
My product component that now has a product field,
that points to a product data,
the Bose sound system, like that.
And we have my product component has rendered HTML.
When I did a detect changes,
the HTML will have been updated.
If you wanna get access to that HTML, you do this.
Remember?
You go to the fixture and you say,
"Can you give me the actual native HTML generated
by the component?"
So what it does, it kind of goes to the component
and then it goes to the HTML and it gives you access
to the actual HTML that was rendered for your component.
That's what we got back there, the HTML rendered
by my product component.
Well, let's just have a quickly reminder what
that should look like.
So in my code, I'm gonna go into the source app folder.
And in there, I've got my product component
and we're looking at the HTML.
So for a product, there should be a div
with a CSS class of product, which should contain child zero
as the description, child one as the category,
and child two as the price.
And so on.
Have a look at the code that I've got here.
Go to the component element,
which basically is the root of the HTML.
And find, now, have a look at this CSS.
This is CSS.
Find all nodes that match this CSS.
So this means, when you say .product,
it means find an element
that has the CSS class called product.
So that, that bit there should find this div.
Because that div has a CSS class of product.
Okay, very good.
And then when you say arrow in CSS,
arrow means child elements, one level deep.
And the star means any.
So what this does is it finds the productive
and then it finds all child elements,
which are one level deep.
So that will give me back a collection
of all the children inside the div,
one level deep inside the div.
It'll give me back these children zero, one, two, and three.
And we can then check
that those children contain the correct marker.
So let's get the first child.
We have to be careful here.
We have to use the safe navigation operator
in case the array wasn't big enough
or it's no.
So we have to be careful.
Go to child zero,
its text content should match this regular expression,
the description, Bose sound system.
So that's child zero.
It should match somewhere in there,
it should say Bose sound system.
When you use regular expressions to match,
all you're checking is that it contains,
does my element contain Bose sound system?
What about child one?
Its text content should contain TV/audio.
So this should contain TV/audio somewhere inside it
if the injection has worked correctly
and my HTML template is correct.
And then find a child two should contain,
amongst other things, it should contain the price
of the Bose sound system, 450 smackeroonies.
Okay right, so what we've done there, if you think about it,
is our component has received an input, a product,
and we are checking that that's worked correctly
and that my HTML is rendering that product successfully.
So that's good.
What about the output property?
So if my product component can raise an event,
does it raise an event properly?
Let's have a look at this code here.
So first of all, I've declared a variable here,
which is going to capture the sale event emitted
by my component.
So it's not initialized yet.
The exclamation mark here is to say,
I can't be bothered to initialize it yet,
it will be initialized somewhere in the code.
Now, have a look at this component.
Remember we had the fixture object
and the fixture object pointed to my product component.
The component variable basically points
to my product component.
I can draw it like that.
And in there, there was a sale event, remember?
An output parameter.
My product component had a sale output parameter.
Quick reminder.
In my product component, it can output a sale event.
That's an event that we can handle.
So this is what you do.
You say, well, take that component and subscribe
or handle the sale event.
I am programmatically handling the sale event
for my component.
So if a sale event occurs,
presumably because the user clicked the buy button,
it'll create a sale object
and that's the sale event will emit that sale object.
I'm subscribing to that sale event.
This is my handler.
This is kind
of like the equivalent code would've actually been
in my application component normally.
But in my test harness, I am handling the same event
that gets submitted.
If my component admits to the sale event, call me back.
And this here will capture the sale event coming
from the product component.
What I do is I take that sale event
and I just store it in this variable for later usage.
So basically, this variable will point
to the sale event that has been emitted
by my product component.
And I can check that the event occurs and that the object
that it emits is the correct event object.
So remember my product component?
It had a HTML in the HTML template.
There was a buy button where the user could buy
that product.
Remember that?
Quick reminder of that.
In my product HTML, each product,
as well as displaying the details, it has a buy button.
So I'm gonna simulate the user clicking this button.
I'm going to take the product component.
I'm going to find the button
and I'm gonna simulate the user clicking that button.
When the user clicks that button, what should happen
in my product component is it should say,
oh, the user clicked the button
and it should emit a sale event.
And that's what I'm gonna test.
Have a look at this code then.
Get the HTML, get the native HTML,
and in that native HTML, find the button.
I am finding the button on the product.
Once I found that button,
I am simulating the user clicking the button.
This is the easiest way to do it.
Once you've got the button,
basically call the click function as
if the user had clicked that button.
It'll call the on click handler in the product component.
So it'll call this function in the product component,
which should emit a sale.
I should get back a sale event after that.
Let's verify that I did.
The sale event should have been captured here.
I've subscribed to the sale event,
captured the sale event object, stick it into this variable.
Let's check that variable.
Let's check the sale.
Was it the Bose sound system?
The description.
Was the sale emitted quantity one?
I'm basically checking the contents
of the sale event that was emitted.
Did it have the correct product description?
And did it have the correct quantity?
There we go.
Okay, so it's quite tricky.
To be perfectly honest,
it's not that common where components emit events,
but if they do emit events,
then this is how you can handle it.
Basically, take your component, subscribe to the event.
In your callback function, capture the event object,
do something to make the event occur,
and then look at the event object afterwards
to see if it's correct.
Okay, so if you ran the tests for this component,
you'd find that they all work beautifully.