- Over the last few sessions,
we've been having a look at the demo application,
which had two levels of components,
a high level app component,
that passed data into a lower level product component.
So, here's the lower level product component.
It's in lesson 12, example two.
It's the demo app we've been looking at
for the last couple of sessions.
So, a product component receives an input
from its parent and it renders one product.
It can pass an output event back to the parent
to say a sale has just occurred.
So, we have an application component that sits here.
And the application component has some product data
that it basically passes in as an input.
And it can receive an event like that as an output,
which you can then update sales on the webpage.
Okay?
So, we've been focusing our efforts
on the product component in the last session.
What we're gonna do now is have a look
at how to test the application component itself.
So, here's the application component.
It holds an array of products
and it gets notified of sales, so we can display them.
That's the application component.
How does that get rendered?
Quick reminder, here we go.
So in the app folder,
here's my application component code.
It has a bunch of products,
which are initialized here.
And the HTML, remember we've seen this before.
In the HTML we iterate through those products.
For each product we pass each product
into a product component.
So, app product is the selector
for product component to display one product.
We pass in an input parameter
to say here's the value for your product input.
And, if you generate a sale event
then I will handle it in my function,
me being the application component
and you being the product.
So, when it says app product here,
just to remind you before we start looking at
testing in this section.
App product, remember, is the selector
for product component.
So, there it is.
So in my application HTML,
when I say app product, it's going to create
an instance of my product component.
My product component expects a product input parameter.
"Give me a product object." It says.
And we say, "Have this one."
That's the input.
My product component can generate a sale event.
If my component generates a sale event
then I handle it in my application component.
I say, "Oh, by the way, if you handle,
"if you emit a sale event, I will call this function
"in my application component to handle that event."
In my application component,
I will handle sale events like so.
Okay so, we've seen how all that works.
What we haven't yet seen
is how to test the top level component.
How can we test the application component?
Okay so, here it is.
Here's my app component spec TS.
So, generally when you're,
when you have a top level component that you wanna test
that has lower level components,
as a general rule of thumb,
you should consider a stubbing the lower level component.
And that's what I've done here.
I've defined a stub implementation for product, okay?
Because, I'm testing my application component
and I don't really care about product component.
So, I've created a product stub component.
Basically, in my application HTML,
now, anywhere it says, "Give me a product component."
Instead of creating a real product component,
it'll create a stub product component.
It has an input parameter for product.
It can generate the sale event,
but it doesn't have any template.
So effectively, it's like an empty box.
My application component will render, for each product,
it'll render one of these and it's just an empty stub.
So, what we need to do then is next step
when you're testing components,
you need to set up a test module.
Remember in Angular, a module is a box
of components that, you know, it needs to know about.
So, the test bed, configure test module adapter.
I'm gonna give it my main application component
and my stub product component, okay?
So, it knows about those two components now.
Remember the compiled components function?
When you call compiled components
it basically converts any HTML
that might be in your templates.
It converts into pure JavaScript,
so that these components are now ready to be instantiated.
Okay then, so let's have a look at a test.
I'm gonna check that when my application component
is rendered, it creates the appropriate number
of child product components.
So, let's see what's going on.
Go to my test bed and create an application component.
So, remember what happens.
That will create a fixture object.
And that fixture object is a wrapper
for our our application component.
So, here's our application component.
And as we know, because we've seen this code
a few times now,
when we create the application component,
it has three products inside it, okay?
So, if I draw this, like so,
here's my application component.
Three products, you know, just data.
Those are the products in my array.
So, the fixture available variable is this object.
Detect changes, we've seen that a few times now.
When you say detect changes,
it basically updates the HTML, okay?
So, it does data binding.
So, whatever HTML I had for my application component,
it will be updated with that data.
Now, what would happen at that point?
What happens at that point?
Think about this.
In my HTML, from my application component,
this should now have created three instances of this.
It should have basically,
instantiated three app products.
That app product now corresponds
to my stub product component.
So, it'll receive a product
and it'll potentially emit a sale.
But, it's gonna instantiate a stub product
for each one of those.
And there should be three of them.
My array of products has three products,
so there should be three of these
created inside my generated HTML.
Right.
Now then, here's something you haven't seen before.
When you go to fixture,
you can examine the static HTML that was generated.
We've seen a function called fixture.nativeElement.
When you say fixture native element,
it gives you the raw HTML, okay?
So if you call the native element function,
it'll give you the raw HTML generated by the component.
But there's a more useful function, called debug element.
And what that does is it doesn't give you the raw HTML.
It gives you the actual component tree itself,
including the product component, not just the HTML,
but the components that generated them.
So debug element, the debug element function
gives you access to the actual component tree.
There should be three products in that product tree.
What's the debug element function property?
So, the debug element property,
it gives us access to this actual component tree.
And that component tree has high level
query capabilities, based on Angular.
So this basically, is Angular aware.
It isn't just HTML.
It allows you to look at actual components
and so on, in your actual component hierarchy.
For example, you can call upon this debug element.
You can say, "Query all."
And then, you can give it a selector.
By, by the way, is another class provided by Angular.
When you say By.css, you can give it a CSS selector.
It means, if you say this, it means element name.
Or there's a dot to represent the CSS class.
Or you could use a hash to represent an ID.
But this means, find elements called app product
in my actual component tree, you know,
before they've actually been expanded.
So, fixture.debugElement gives me access
to the complete dom structure generated
by my component in there.
I can find all occurrences of elements
that have this element name, app product.
So what that's going to find,
it's going to find these elements,
these elements here, okay?
There would be three of those,
because there were three products.
So it actually gives me access to the actual,
you know, component hierarchy, app product.
That's the element.
I'm looking for an element called app product.
And by the way, there should have been three of them.
All right so, using this technique,
you can, using debug element,
I think is probably the main thing here.
It gives you access to the, you know,
to the complete dom structure,
or to the complete component structure of your component,
so that you can check basically,
how many instances of my child component
did it attempt to create.
And there should have been three of them.
So, have a play with this.
Have a play with the debug element function
and try to get familiar with this approach.
This is basically how you locate elements
using the tag name represented by your component.