1 00:00:06,540 --> 00:00:09,960 - Here's a quick reminder of our product component class 2 00:00:09,960 --> 00:00:11,580 from the earlier example. 3 00:00:11,580 --> 00:00:16,440 It's in lesson 12, example two in the demo app folder. 4 00:00:16,440 --> 00:00:19,950 If you remember, I had a top level component called app, 5 00:00:19,950 --> 00:00:23,070 which passed in products, that passed in one product 6 00:00:23,070 --> 00:00:26,970 at a time into a component called product component. 7 00:00:26,970 --> 00:00:29,970 My product component received an input, 8 00:00:29,970 --> 00:00:33,780 it received a product object as an input parameter, 9 00:00:33,780 --> 00:00:36,720 and it could generate a sale event to indicate 10 00:00:36,720 --> 00:00:39,480 that somebody had bought that product. 11 00:00:39,480 --> 00:00:42,270 So the picture looked like this. 12 00:00:42,270 --> 00:00:47,270 In my application component, it had three product objects, 13 00:00:47,910 --> 00:00:49,980 product being just data. 14 00:00:49,980 --> 00:00:51,780 For each product, 15 00:00:51,780 --> 00:00:56,460 the application component generated a product component 16 00:00:56,460 --> 00:01:01,460 and it passed that in as a parameter into here. 17 00:01:01,500 --> 00:01:04,980 That was an input passed into the product component. 18 00:01:04,980 --> 00:01:08,493 So the product component has a field called product, 19 00:01:09,540 --> 00:01:13,260 which basically points to a product object like that. 20 00:01:13,260 --> 00:01:17,670 And also a product component, there's a buy button 21 00:01:17,670 --> 00:01:21,150 in my user interface whereby the user can buy that product. 22 00:01:21,150 --> 00:01:23,580 In which case, my product component will 23 00:01:23,580 --> 00:01:26,119 basically generate a sale event 24 00:01:26,119 --> 00:01:30,690 which the application component can handle to display sales. 25 00:01:30,690 --> 00:01:32,760 So that's an output event. 26 00:01:32,760 --> 00:01:34,530 An output parameter, if you like. 27 00:01:34,530 --> 00:01:37,260 My product can output a sale 28 00:01:37,260 --> 00:01:40,080 to say somebody has just bought this product. 29 00:01:40,080 --> 00:01:43,500 We've seen how this works and we've had a play with it. 30 00:01:43,500 --> 00:01:45,000 This is the user interface. 31 00:01:45,000 --> 00:01:47,610 Each product component is a blue box. 32 00:01:47,610 --> 00:01:50,160 It displays the product data that was passed in 33 00:01:50,160 --> 00:01:53,100 as a parameter and the buy button, 34 00:01:53,100 --> 00:01:55,020 it generates a sale event. 35 00:01:55,020 --> 00:01:58,800 That sale event is handled by the top component app. 36 00:01:58,800 --> 00:02:01,920 It receives the sale information and it adds it 37 00:02:01,920 --> 00:02:06,060 to a list that it displays on the top level, 38 00:02:06,060 --> 00:02:07,080 like so. 39 00:02:07,080 --> 00:02:10,680 So this application works and what we wanna do now is 40 00:02:10,680 --> 00:02:12,960 to test this product component. 41 00:02:12,960 --> 00:02:16,743 How can you test a component that has inputs 42 00:02:16,743 --> 00:02:19,020 and has outputs? 43 00:02:19,020 --> 00:02:22,050 How can you test that it's working correctly? 44 00:02:22,050 --> 00:02:23,430 That's what we're going to see now. 45 00:02:23,430 --> 00:02:26,130 How can we test this product component properly 46 00:02:26,130 --> 00:02:28,770 given the fact that it has inputs 47 00:02:28,770 --> 00:02:31,290 and it raises an output events. 48 00:02:31,290 --> 00:02:35,100 How can we test that this is all working properly? 49 00:02:35,100 --> 00:02:36,450 I'm glad you asked. 50 00:02:36,450 --> 00:02:40,200 So what we're gonna do is in the product component spec, 51 00:02:40,200 --> 00:02:43,830 we're gonna set up tests for input parameters 52 00:02:43,830 --> 00:02:46,500 and for output events. 53 00:02:46,500 --> 00:02:49,020 So let's have a look at what we've got here then. 54 00:02:49,020 --> 00:02:51,600 So we have a couple of variables at the top. 55 00:02:51,600 --> 00:02:55,320 First of all, we are gonna be working with product component 56 00:02:55,320 --> 00:02:58,290 and remember how it works with Angular, with Jasmine. 57 00:02:58,290 --> 00:03:00,600 When you are setting up a test environment, 58 00:03:00,600 --> 00:03:03,120 you don't directly access the component, 59 00:03:03,120 --> 00:03:05,520 instead you have this thing called a fixture 60 00:03:05,520 --> 00:03:08,253 which is a wrapper over the product component. 61 00:03:09,180 --> 00:03:11,130 So, let's see how it goes. 62 00:03:11,130 --> 00:03:12,810 And here's the product data. 63 00:03:12,810 --> 00:03:14,610 Now, in the real application, 64 00:03:14,610 --> 00:03:17,880 the product data would've been passed in from my parent. 65 00:03:17,880 --> 00:03:20,430 When you're unit testing the product component, 66 00:03:20,430 --> 00:03:22,080 there is no parent, 67 00:03:22,080 --> 00:03:24,690 you're just testing the product component on its own. 68 00:03:24,690 --> 00:03:27,540 So we are gonna basically create like a dummy version 69 00:03:27,540 --> 00:03:31,710 of this object as if we'd received it from our parent. 70 00:03:31,710 --> 00:03:34,590 Before we go onto that bit of criteria, look at this. 71 00:03:34,590 --> 00:03:39,300 Tell my test environment to create an instance 72 00:03:39,300 --> 00:03:41,310 of product component. 73 00:03:41,310 --> 00:03:42,420 Right, okay. 74 00:03:42,420 --> 00:03:47,160 So it'll create an instance of product component. 75 00:03:47,160 --> 00:03:49,590 A product component, as we've seen, 76 00:03:49,590 --> 00:03:54,177 will have a product field that needs to be initialized. 77 00:03:54,177 --> 00:03:57,330 And in normal circumstances, that would've been passed in 78 00:03:57,330 --> 00:04:00,150 as a parameter for my parent component. 79 00:04:00,150 --> 00:04:04,410 When you are testing, there is no automated creation 80 00:04:04,410 --> 00:04:05,910 of parent components. 81 00:04:05,910 --> 00:04:08,340 You just basically create what you're given. 82 00:04:08,340 --> 00:04:11,100 So this will create a product component. 83 00:04:11,100 --> 00:04:13,950 And also, remember that we have this kind 84 00:04:13,950 --> 00:04:16,500 of fixture that wraps it. 85 00:04:16,500 --> 00:04:19,170 So the fixture object kind of points 86 00:04:19,170 --> 00:04:21,510 to the product component. 87 00:04:21,510 --> 00:04:23,463 This is my product component here. 88 00:04:25,590 --> 00:04:28,230 So the component variable basically points 89 00:04:28,230 --> 00:04:30,303 to my product component. 90 00:04:31,140 --> 00:04:34,263 Inside that product component, it has this product field, 91 00:04:35,130 --> 00:04:36,480 which I'm initializing here. 92 00:04:36,480 --> 00:04:39,810 Create a product object, a Bose sound system, 93 00:04:39,810 --> 00:04:42,300 TV audio 450. 94 00:04:42,300 --> 00:04:43,890 Okay, very nice. 95 00:04:43,890 --> 00:04:45,540 So that's a product object 96 00:04:45,540 --> 00:04:47,670 that I've just created manually. 97 00:04:47,670 --> 00:04:48,870 Normally, that would've been passed in 98 00:04:48,870 --> 00:04:50,190 from the app component. 99 00:04:50,190 --> 00:04:53,910 I've created it manually here in my test and I hook it up 100 00:04:53,910 --> 00:04:57,960 in my component, hook up my product field to point 101 00:04:57,960 --> 00:05:00,030 to the product that I just created. 102 00:05:00,030 --> 00:05:02,940 So in my component, go to the product field 103 00:05:02,940 --> 00:05:07,200 and hook that product field up to the product object. 104 00:05:07,200 --> 00:05:09,783 So here is my Bose sound system. 105 00:05:10,890 --> 00:05:12,360 Do you say Bose or Bose? 106 00:05:12,360 --> 00:05:15,330 Anyway, it's one of those, like so. 107 00:05:15,330 --> 00:05:16,890 Oh, and then detect changes. 108 00:05:16,890 --> 00:05:20,200 Remember, when you have a component, 109 00:05:20,200 --> 00:05:23,400 whenever you change any data in the component, 110 00:05:23,400 --> 00:05:26,580 like we just did, you need to call this function 111 00:05:26,580 --> 00:05:28,650 to update the HTML. 112 00:05:28,650 --> 00:05:33,650 So the HTML will now be updated to display, well presumably, 113 00:05:34,380 --> 00:05:38,040 to display information about that product. 114 00:05:38,040 --> 00:05:39,960 So we can check 115 00:05:39,960 --> 00:05:44,430 that the HTML does display the product correctly. 116 00:05:44,430 --> 00:05:47,640 If I receive an input parameter, a product, 117 00:05:47,640 --> 00:05:51,483 does my HTML display the product detail correctly? 118 00:05:52,620 --> 00:05:54,087 Okay, so let's have a look. 119 00:05:54,087 --> 00:05:56,670 Here's my test. 120 00:05:56,670 --> 00:05:59,250 I'm testing that my product component 121 00:05:59,250 --> 00:06:02,430 should render the product details correctly. 122 00:06:02,430 --> 00:06:04,350 So I'm gonna give you a quick reminder again. 123 00:06:04,350 --> 00:06:07,170 So here is my fixture object. 124 00:06:07,170 --> 00:06:08,640 That's my fixture object. 125 00:06:08,640 --> 00:06:10,233 It points to the component. 126 00:06:11,130 --> 00:06:13,110 That's my product component. 127 00:06:13,110 --> 00:06:16,383 My product component that now has a product field, 128 00:06:18,570 --> 00:06:23,250 that points to a product data, 129 00:06:23,250 --> 00:06:27,030 the Bose sound system, like that. 130 00:06:27,030 --> 00:06:30,633 And we have my product component has rendered HTML. 131 00:06:31,500 --> 00:06:33,360 When I did a detect changes, 132 00:06:33,360 --> 00:06:38,360 the HTML will have been updated. 133 00:06:38,460 --> 00:06:42,060 If you wanna get access to that HTML, you do this. 134 00:06:42,060 --> 00:06:42,893 Remember? 135 00:06:42,893 --> 00:06:44,617 You go to the fixture and you say, 136 00:06:44,617 --> 00:06:48,240 "Can you give me the actual native HTML generated 137 00:06:48,240 --> 00:06:49,290 by the component?" 138 00:06:49,290 --> 00:06:51,760 So what it does, it kind of goes to the component 139 00:06:52,680 --> 00:06:55,290 and then it goes to the HTML and it gives you access 140 00:06:55,290 --> 00:06:59,130 to the actual HTML that was rendered for your component. 141 00:06:59,130 --> 00:07:02,100 That's what we got back there, the HTML rendered 142 00:07:02,100 --> 00:07:03,603 by my product component. 143 00:07:04,528 --> 00:07:06,690 Well, let's just have a quickly reminder what 144 00:07:06,690 --> 00:07:07,803 that should look like. 145 00:07:09,420 --> 00:07:13,080 So in my code, I'm gonna go into the source app folder. 146 00:07:13,080 --> 00:07:15,480 And in there, I've got my product component 147 00:07:15,480 --> 00:07:17,790 and we're looking at the HTML. 148 00:07:17,790 --> 00:07:21,660 So for a product, there should be a div 149 00:07:21,660 --> 00:07:26,660 with a CSS class of product, which should contain child zero 150 00:07:27,720 --> 00:07:31,440 as the description, child one as the category, 151 00:07:31,440 --> 00:07:33,630 and child two as the price. 152 00:07:33,630 --> 00:07:35,010 And so on. 153 00:07:35,010 --> 00:07:37,470 Have a look at the code that I've got here. 154 00:07:37,470 --> 00:07:39,060 Go to the component element, 155 00:07:39,060 --> 00:07:42,120 which basically is the root of the HTML. 156 00:07:42,120 --> 00:07:44,940 And find, now, have a look at this CSS. 157 00:07:44,940 --> 00:07:46,260 This is CSS. 158 00:07:46,260 --> 00:07:51,150 Find all nodes that match this CSS. 159 00:07:51,150 --> 00:07:53,460 So this means, when you say .product, 160 00:07:53,460 --> 00:07:56,460 it means find an element 161 00:07:56,460 --> 00:07:59,730 that has the CSS class called product. 162 00:07:59,730 --> 00:08:04,730 So that, that bit there should find this div. 163 00:08:07,680 --> 00:08:11,730 Because that div has a CSS class of product. 164 00:08:11,730 --> 00:08:13,200 Okay, very good. 165 00:08:13,200 --> 00:08:16,740 And then when you say arrow in CSS, 166 00:08:16,740 --> 00:08:20,130 arrow means child elements, one level deep. 167 00:08:20,130 --> 00:08:22,380 And the star means any. 168 00:08:22,380 --> 00:08:25,590 So what this does is it finds the productive 169 00:08:25,590 --> 00:08:27,450 and then it finds all child elements, 170 00:08:27,450 --> 00:08:29,250 which are one level deep. 171 00:08:29,250 --> 00:08:31,620 So that will give me back a collection 172 00:08:31,620 --> 00:08:33,930 of all the children inside the div, 173 00:08:33,930 --> 00:08:36,120 one level deep inside the div. 174 00:08:36,120 --> 00:08:41,120 It'll give me back these children zero, one, two, and three. 175 00:08:41,580 --> 00:08:42,780 And we can then check 176 00:08:42,780 --> 00:08:45,810 that those children contain the correct marker. 177 00:08:45,810 --> 00:08:47,910 So let's get the first child. 178 00:08:47,910 --> 00:08:49,050 We have to be careful here. 179 00:08:49,050 --> 00:08:51,660 We have to use the safe navigation operator 180 00:08:51,660 --> 00:08:54,150 in case the array wasn't big enough 181 00:08:54,150 --> 00:08:54,983 or it's no. 182 00:08:54,983 --> 00:08:56,820 So we have to be careful. 183 00:08:56,820 --> 00:08:58,320 Go to child zero, 184 00:08:58,320 --> 00:09:01,800 its text content should match this regular expression, 185 00:09:01,800 --> 00:09:04,743 the description, Bose sound system. 186 00:09:05,730 --> 00:09:07,293 So that's child zero. 187 00:09:08,130 --> 00:09:10,530 It should match somewhere in there, 188 00:09:10,530 --> 00:09:13,650 it should say Bose sound system. 189 00:09:13,650 --> 00:09:16,200 When you use regular expressions to match, 190 00:09:16,200 --> 00:09:19,170 all you're checking is that it contains, 191 00:09:19,170 --> 00:09:24,170 does my element contain Bose sound system? 192 00:09:24,540 --> 00:09:26,100 What about child one? 193 00:09:26,100 --> 00:09:29,940 Its text content should contain TV/audio. 194 00:09:29,940 --> 00:09:34,940 So this should contain TV/audio somewhere inside it 195 00:09:35,190 --> 00:09:37,530 if the injection has worked correctly 196 00:09:37,530 --> 00:09:39,990 and my HTML template is correct. 197 00:09:39,990 --> 00:09:43,530 And then find a child two should contain, 198 00:09:43,530 --> 00:09:45,930 amongst other things, it should contain the price 199 00:09:45,930 --> 00:09:49,743 of the Bose sound system, 450 smackeroonies. 200 00:09:50,880 --> 00:09:54,120 Okay right, so what we've done there, if you think about it, 201 00:09:54,120 --> 00:09:59,120 is our component has received an input, a product, 202 00:09:59,130 --> 00:10:01,290 and we are checking that that's worked correctly 203 00:10:01,290 --> 00:10:05,700 and that my HTML is rendering that product successfully. 204 00:10:05,700 --> 00:10:06,933 So that's good. 205 00:10:07,830 --> 00:10:09,840 What about the output property? 206 00:10:09,840 --> 00:10:14,377 So if my product component can raise an event, 207 00:10:14,377 --> 00:10:17,190 does it raise an event properly? 208 00:10:17,190 --> 00:10:18,990 Let's have a look at this code here. 209 00:10:20,340 --> 00:10:23,850 So first of all, I've declared a variable here, 210 00:10:23,850 --> 00:10:27,390 which is going to capture the sale event emitted 211 00:10:27,390 --> 00:10:29,010 by my component. 212 00:10:29,010 --> 00:10:30,693 So it's not initialized yet. 213 00:10:32,040 --> 00:10:34,530 The exclamation mark here is to say, 214 00:10:34,530 --> 00:10:37,530 I can't be bothered to initialize it yet, 215 00:10:37,530 --> 00:10:41,550 it will be initialized somewhere in the code. 216 00:10:41,550 --> 00:10:44,520 Now, have a look at this component. 217 00:10:44,520 --> 00:10:47,200 Remember we had the fixture object 218 00:10:48,060 --> 00:10:51,873 and the fixture object pointed to my product component. 219 00:10:52,950 --> 00:10:55,738 The component variable basically points 220 00:10:55,738 --> 00:10:57,630 to my product component. 221 00:10:57,630 --> 00:10:58,893 I can draw it like that. 222 00:11:00,060 --> 00:11:04,050 And in there, there was a sale event, remember? 223 00:11:04,050 --> 00:11:05,100 An output parameter. 224 00:11:05,100 --> 00:11:08,580 My product component had a sale output parameter. 225 00:11:08,580 --> 00:11:09,780 Quick reminder. 226 00:11:09,780 --> 00:11:14,125 In my product component, it can output a sale event. 227 00:11:14,125 --> 00:11:17,250 That's an event that we can handle. 228 00:11:17,250 --> 00:11:18,540 So this is what you do. 229 00:11:18,540 --> 00:11:21,900 You say, well, take that component and subscribe 230 00:11:21,900 --> 00:11:23,280 or handle the sale event. 231 00:11:23,280 --> 00:11:25,860 I am programmatically handling the sale event 232 00:11:25,860 --> 00:11:27,390 for my component. 233 00:11:27,390 --> 00:11:30,570 So if a sale event occurs, 234 00:11:30,570 --> 00:11:34,260 presumably because the user clicked the buy button, 235 00:11:34,260 --> 00:11:35,770 it'll create a sale object 236 00:11:37,123 --> 00:11:41,520 and that's the sale event will emit that sale object. 237 00:11:41,520 --> 00:11:43,860 I'm subscribing to that sale event. 238 00:11:43,860 --> 00:11:45,510 This is my handler. 239 00:11:45,510 --> 00:11:46,343 This is kind 240 00:11:46,343 --> 00:11:48,060 of like the equivalent code would've actually been 241 00:11:48,060 --> 00:11:50,340 in my application component normally. 242 00:11:50,340 --> 00:11:53,820 But in my test harness, I am handling the same event 243 00:11:53,820 --> 00:11:54,900 that gets submitted. 244 00:11:54,900 --> 00:11:58,740 If my component admits to the sale event, call me back. 245 00:11:58,740 --> 00:12:02,280 And this here will capture the sale event coming 246 00:12:02,280 --> 00:12:04,200 from the product component. 247 00:12:04,200 --> 00:12:06,060 What I do is I take that sale event 248 00:12:06,060 --> 00:12:11,060 and I just store it in this variable for later usage. 249 00:12:11,160 --> 00:12:13,535 So basically, this variable will point 250 00:12:13,535 --> 00:12:17,370 to the sale event that has been emitted 251 00:12:17,370 --> 00:12:18,570 by my product component. 252 00:12:18,570 --> 00:12:22,050 And I can check that the event occurs and that the object 253 00:12:22,050 --> 00:12:24,663 that it emits is the correct event object. 254 00:12:26,010 --> 00:12:29,820 So remember my product component? 255 00:12:29,820 --> 00:12:33,210 It had a HTML in the HTML template. 256 00:12:33,210 --> 00:12:35,520 There was a buy button where the user could buy 257 00:12:35,520 --> 00:12:36,353 that product. 258 00:12:36,353 --> 00:12:37,860 Remember that? 259 00:12:37,860 --> 00:12:39,240 Quick reminder of that. 260 00:12:39,240 --> 00:12:42,090 In my product HTML, each product, 261 00:12:42,090 --> 00:12:45,900 as well as displaying the details, it has a buy button. 262 00:12:45,900 --> 00:12:49,170 So I'm gonna simulate the user clicking this button. 263 00:12:49,170 --> 00:12:51,300 I'm going to take the product component. 264 00:12:51,300 --> 00:12:52,797 I'm going to find the button 265 00:12:52,797 --> 00:12:56,283 and I'm gonna simulate the user clicking that button. 266 00:12:58,470 --> 00:13:01,350 When the user clicks that button, what should happen 267 00:13:01,350 --> 00:13:03,720 in my product component is it should say, 268 00:13:03,720 --> 00:13:05,940 oh, the user clicked the button 269 00:13:05,940 --> 00:13:07,890 and it should emit a sale event. 270 00:13:07,890 --> 00:13:09,510 And that's what I'm gonna test. 271 00:13:09,510 --> 00:13:11,647 Have a look at this code then. 272 00:13:11,647 --> 00:13:15,063 Get the HTML, get the native HTML, 273 00:13:16,173 --> 00:13:18,810 and in that native HTML, find the button. 274 00:13:18,810 --> 00:13:21,510 I am finding the button on the product. 275 00:13:21,510 --> 00:13:22,770 Once I found that button, 276 00:13:22,770 --> 00:13:25,020 I am simulating the user clicking the button. 277 00:13:25,020 --> 00:13:26,790 This is the easiest way to do it. 278 00:13:26,790 --> 00:13:28,170 Once you've got the button, 279 00:13:28,170 --> 00:13:29,760 basically call the click function as 280 00:13:29,760 --> 00:13:31,833 if the user had clicked that button. 281 00:13:32,700 --> 00:13:36,990 It'll call the on click handler in the product component. 282 00:13:36,990 --> 00:13:40,590 So it'll call this function in the product component, 283 00:13:40,590 --> 00:13:43,170 which should emit a sale. 284 00:13:43,170 --> 00:13:46,110 I should get back a sale event after that. 285 00:13:46,110 --> 00:13:48,060 Let's verify that I did. 286 00:13:48,060 --> 00:13:51,483 The sale event should have been captured here. 287 00:13:52,407 --> 00:13:54,900 I've subscribed to the sale event, 288 00:13:54,900 --> 00:13:59,100 captured the sale event object, stick it into this variable. 289 00:13:59,100 --> 00:14:00,570 Let's check that variable. 290 00:14:00,570 --> 00:14:01,860 Let's check the sale. 291 00:14:01,860 --> 00:14:04,320 Was it the Bose sound system? 292 00:14:04,320 --> 00:14:05,730 The description. 293 00:14:05,730 --> 00:14:08,280 Was the sale emitted quantity one? 294 00:14:08,280 --> 00:14:10,290 I'm basically checking the contents 295 00:14:10,290 --> 00:14:12,480 of the sale event that was emitted. 296 00:14:12,480 --> 00:14:15,570 Did it have the correct product description? 297 00:14:15,570 --> 00:14:18,810 And did it have the correct quantity? 298 00:14:18,810 --> 00:14:19,643 There we go. 299 00:14:19,643 --> 00:14:21,780 Okay, so it's quite tricky. 300 00:14:21,780 --> 00:14:23,040 To be perfectly honest, 301 00:14:23,040 --> 00:14:27,900 it's not that common where components emit events, 302 00:14:27,900 --> 00:14:29,130 but if they do emit events, 303 00:14:29,130 --> 00:14:32,250 then this is how you can handle it. 304 00:14:32,250 --> 00:14:36,660 Basically, take your component, subscribe to the event. 305 00:14:36,660 --> 00:14:40,671 In your callback function, capture the event object, 306 00:14:40,671 --> 00:14:43,860 do something to make the event occur, 307 00:14:43,860 --> 00:14:45,960 and then look at the event object afterwards 308 00:14:45,960 --> 00:14:48,180 to see if it's correct. 309 00:14:48,180 --> 00:14:51,544 Okay, so if you ran the tests for this component, 310 00:14:51,544 --> 00:14:53,793 you'd find that they all work beautifully.