Slide 1

Slide 1 text

@AlisaDuncan Tidy up your tests TIDY UP YOUR TESTS use component harnesses to write tests that bring you joy ALISA DUNCAN

Slide 2

Slide 2 text

ALISA DUNCAN Software Architect | Co-organizer | Core Team | Angular GDE Loves learning about, discussing, and writing automated tests Enjoys reading a book and drinking a glass of wine on the porch @ALISADUNCAN AngularKC ngGirls

Slide 3

Slide 3 text

TESTS THAT DON'T BRING US JOY Do your component tests feel "cluttered"?

Slide 4

Slide 4 text

TESTS THAT DON'T BRING US JOY Do your component tests feel "cluttered"? Is it difficult to read and understand your component unit tests at a glance?

Slide 5

Slide 5 text

TESTS THAT DON'T BRING US JOY Do your component tests feel "cluttered"? Is it difficult to read and understand your component unit tests at a glance? Do you want tests to focus on testing behavior without having to deal with querying the DOM?

Slide 6

Slide 6 text

TIDY YOUR TESTS AND FOCUS ON MEANINGFUL TESTS WITH COMPONENT HARNESSES

Slide 7

Slide 7 text

WHAT ARE COMPONENT TEST HARNESSES? Test harnesses are a set of APIs in Angular CDK that support testing interactions with components

Slide 8

Slide 8 text

WHAT ARE COMPONENT TEST HARNESSES? Test harnesses are a set of APIs in Angular CDK that support testing interactions with components All Angular Material components have test harnesses built-in in v12

Slide 9

Slide 9 text

WHY IS THIS USEFUL? Tests that are easier to read

Slide 10

Slide 10 text

WHY IS THIS USEFUL? Tests that are easier to read Tests that use an API to interact with a component

Slide 11

Slide 11 text

WHY IS THIS USEFUL? Tests that are easier to read Tests that use an API to interact with a component Allows you and your team to write tests that focus on behavior

Slide 12

Slide 12 text

LET'S WRITE A TEST

Slide 13

Slide 13 text

A TIDY LIST TODO APP Consider the tests you'd write for an app like this. We'll look at a task view.

Slide 14

Slide 14 text

THE CODE WE'LL TEST
{{tidyTask.description}}
favorite_border delete
1 2 3 4 5 6 7 8 9 10 11 12 13 14

Slide 15

Slide 15 text

THE CODE WE'LL TEST
{{tidyTask.description}}
favorite_border delete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [ngClass]="tidyTask.completed ? 'tidy-task-completed' : ''">
1 7
8 favorite_border 9 10 delete 11 12
13
14

Slide 16

Slide 16 text

EXAMPLE TEST it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 17

Slide 17 text

EXAMPLE TEST it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); it('should apply completed class to match task completion', () => { 1 2 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18

Slide 18

Slide 18 text

EXAMPLE TEST it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); it('should apply completed class to match task completion', () => { 1 2 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 5 6 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18

Slide 19

Slide 19 text

EXAMPLE TEST it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); it('should apply completed class to match task completion', () => { 1 2 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 5 6 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18

Slide 20

Slide 20 text

EXAMPLE TEST it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); it('should apply completed class to match task completion', () => { 1 2 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 5 6 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 10 11 12 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18

Slide 21

Slide 21 text

EXAMPLE TEST it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); it('should apply completed class to match task completion', () => { 1 2 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 5 6 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 10 11 12 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 expect(cbEl.nativeElement.checked).toBe(true); expect(component.tidyTask.completed).toBeTrue(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 17 }); 18

Slide 22

Slide 22 text

EXAMPLE TEST it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); it('should apply completed class to match task completion', () => { 1 2 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 5 6 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 10 11 12 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 expect(cbEl.nativeElement.checked).toBe(true); expect(component.tidyTask.completed).toBeTrue(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 17 }); 18 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 16 17 }); 18

Slide 23

Slide 23 text

EXAMPLE TEST it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); it('should apply completed class to match task completion', () => { 1 2 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 5 6 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); it('should apply completed class to match task completion', () => { 1 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 10 11 12 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 expect(component.tidyTask.completed).toBeTrue(); 17 }); 18 expect(cbEl.nativeElement.checked).toBe(true); expect(component.tidyTask.completed).toBeTrue(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 15 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); 16 17 }); 18 expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); it('should apply completed class to match task completion', () => { 1 const matCb = fixture.debugElement.query(By.css('mat-checkbox')); 2 expect(matCb).toBeTruthy(); 3 4 const cbEl = matCb.query(By.css('input')); 5 expect(cbEl).toBeTruthy(); 6 expect(cbEl.nativeElement.checked).toBe(false); 7 expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); 8 9 const cbClickEl = 10 fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); 11 cbClickEl.nativeElement.click(); 12 fixture.detectChanges(); 13 14 expect(cbEl.nativeElement.checked).toBe(true); 15 16 17 }); 18 it('should apply completed class to match task completion', () => { const matCb = fixture.debugElement.query(By.css('mat-checkbox')); expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('tidy-task-completed'); const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inner-container')); cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-task-completed'); expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 24

Slide 24 text

EXAMPLE TEST it('should apply completed class to match task completion const matCb = fixture.debugElement.query(By.css('mat-ch expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('ti const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inne cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-t expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Slide 25

Slide 25 text

EXAMPLE TEST it('should apply completed class to match task completion const matCb = fixture.debugElement.query(By.css('mat-ch expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('ti const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inne cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-t expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 it('should apply completed class to match task completion', async () => { const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); await cb.toggle(); expect(await cb.isChecked()).toBeTrue(); expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12

Slide 26

Slide 26 text

EXAMPLE TEST it('should apply completed class to match task completion const matCb = fixture.debugElement.query(By.css('mat-ch expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('ti const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inne cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-t expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 it('should apply completed class to match task completion', async () => { const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); await cb.toggle(); expect(await cb.isChecked()).toBeTrue(); expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 const cb = await loader.getHarness(MatCheckboxHarness); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12

Slide 27

Slide 27 text

EXAMPLE TEST it('should apply completed class to match task completion const matCb = fixture.debugElement.query(By.css('mat-ch expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('ti const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inne cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-t expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 it('should apply completed class to match task completion', async () => { const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); await cb.toggle(); expect(await cb.isChecked()).toBeTrue(); expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 const cb = await loader.getHarness(MatCheckboxHarness); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); it('should apply completed class to match task completion', async () => { 1 2 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12

Slide 28

Slide 28 text

EXAMPLE TEST it('should apply completed class to match task completion const matCb = fixture.debugElement.query(By.css('mat-ch expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('ti const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inne cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-t expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 it('should apply completed class to match task completion', async () => { const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); await cb.toggle(); expect(await cb.isChecked()).toBeTrue(); expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 const cb = await loader.getHarness(MatCheckboxHarness); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); it('should apply completed class to match task completion', async () => { 1 2 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 5 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12

Slide 29

Slide 29 text

EXAMPLE TEST it('should apply completed class to match task completion const matCb = fixture.debugElement.query(By.css('mat-ch expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('ti const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inne cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-t expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 it('should apply completed class to match task completion', async () => { const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); await cb.toggle(); expect(await cb.isChecked()).toBeTrue(); expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 const cb = await loader.getHarness(MatCheckboxHarness); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); it('should apply completed class to match task completion', async () => { 1 2 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 5 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 await cb.toggle(); it('should apply completed class to match task completion', async () => { 1 const cb = await loader.getHarness(MatCheckboxHarness); 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12

Slide 30

Slide 30 text

EXAMPLE TEST it('should apply completed class to match task completion const matCb = fixture.debugElement.query(By.css('mat-ch expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('ti const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inne cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-t expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 it('should apply completed class to match task completion', async () => { const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); await cb.toggle(); expect(await cb.isChecked()).toBeTrue(); expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 const cb = await loader.getHarness(MatCheckboxHarness); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); it('should apply completed class to match task completion', async () => { 1 2 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 5 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 await cb.toggle(); it('should apply completed class to match task completion', async () => { 1 const cb = await loader.getHarness(MatCheckboxHarness); 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 expect(await cb.isChecked()).toBeTrue(); it('should apply completed class to match task completion', async () => { 1 const cb = await loader.getHarness(MatCheckboxHarness); 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12

Slide 31

Slide 31 text

EXAMPLE TEST it('should apply completed class to match task completion const matCb = fixture.debugElement.query(By.css('mat-ch expect(matCb).toBeTruthy(); const cbEl = matCb.query(By.css('input')); expect(cbEl).toBeTruthy(); expect(cbEl.nativeElement.checked).toBe(false); expect(matCb.nativeElement.classList).not.toContain('ti const cbClickEl = fixture.debugElement.query(By.css('.mat-checkbox-inne cbClickEl.nativeElement.click(); fixture.detectChanges(); expect(cbEl.nativeElement.checked).toBe(true); expect(matCb.nativeElement.classList).toContain('tidy-t expect(component.tidyTask.completed).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 it('should apply completed class to match task completion', async () => { const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); await cb.toggle(); expect(await cb.isChecked()).toBeTrue(); expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12 const cb = await loader.getHarness(MatCheckboxHarness); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); it('should apply completed class to match task completion', async () => { 1 2 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 const cb = await loader.getHarness(MatCheckboxHarness); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); it('should apply completed class to match task completion', async () => { 1 2 expect(await cb.isChecked()).toBeFalse(); 3 4 5 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 await cb.toggle(); it('should apply completed class to match task completion', async () => { 1 const cb = await loader.getHarness(MatCheckboxHarness); 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 8 9 expect(await cb.isChecked()).toBeTrue(); 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 expect(await cb.isChecked()).toBeTrue(); it('should apply completed class to match task completion', async () => { 1 const cb = await loader.getHarness(MatCheckboxHarness); 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 10 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); 11 }); 12 expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); it('should apply completed class to match task completion', async () => { 1 const cb = await loader.getHarness(MatCheckboxHarness); 2 expect(await cb.isChecked()).toBeFalse(); 3 4 const cbHost = await cb.host(); 5 expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); 6 7 await cb.toggle(); 8 9 expect(await cb.isChecked()).toBeTrue(); 10 11 }); 12

Slide 32

Slide 32 text

EXAMPLE TEST it('should apply completed class to match task completion', async () => { const cb = await loader.getHarness(MatCheckboxHarness); expect(await cb.isChecked()).toBeFalse(); const cbHost = await cb.host(); expect(await cbHost.hasClass('tidy-task-completed')).not.toBeTrue(); await cb.toggle(); expect(await cb.isChecked()).toBeTrue(); expect(await cbHost.hasClass('tidy-task-completed')).toBeTrue(); }); 1 2 3 4 5 6 7 8 9 10 11 12

Slide 33

Slide 33 text

THE CDK TESTING API

Slide 34

Slide 34 text

GET A HARNESSLOADER FOR YOUR ENVIRONMENT import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; describe('Tidy Test', () => { let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({...}); fixture = TestBed.createComponent(TidyTestComponent); loader = TestbedHarnessEnvironment.loader(fixture); }); }); 1 2 3 4 5 6 7 8 9 10 11 12

Slide 35

Slide 35 text

GET A HARNESSLOADER FOR YOUR ENVIRONMENT import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; describe('Tidy Test', () => { let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({...}); fixture = TestBed.createComponent(TidyTestComponent); loader = TestbedHarnessEnvironment.loader(fixture); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 1 2 3 describe('Tidy Test', () => { 4 let loader: HarnessLoader; 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 fixture = TestBed.createComponent(TidyTestComponent); 9 loader = TestbedHarnessEnvironment.loader(fixture); 10 }); 11 }); 12

Slide 36

Slide 36 text

GET A HARNESSLOADER FOR YOUR ENVIRONMENT import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; describe('Tidy Test', () => { let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({...}); fixture = TestBed.createComponent(TidyTestComponent); loader = TestbedHarnessEnvironment.loader(fixture); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 1 2 3 describe('Tidy Test', () => { 4 let loader: HarnessLoader; 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 fixture = TestBed.createComponent(TidyTestComponent); 9 loader = TestbedHarnessEnvironment.loader(fixture); 10 }); 11 }); 12 let loader: HarnessLoader; import { HarnessLoader } from '@angular/cdk/testing'; 1 import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 2 3 describe('Tidy Test', () => { 4 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 fixture = TestBed.createComponent(TidyTestComponent); 9 loader = TestbedHarnessEnvironment.loader(fixture); 10 }); 11 }); 12

Slide 37

Slide 37 text

GET A HARNESSLOADER FOR YOUR ENVIRONMENT import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; describe('Tidy Test', () => { let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({...}); fixture = TestBed.createComponent(TidyTestComponent); loader = TestbedHarnessEnvironment.loader(fixture); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 1 2 3 describe('Tidy Test', () => { 4 let loader: HarnessLoader; 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 fixture = TestBed.createComponent(TidyTestComponent); 9 loader = TestbedHarnessEnvironment.loader(fixture); 10 }); 11 }); 12 let loader: HarnessLoader; import { HarnessLoader } from '@angular/cdk/testing'; 1 import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 2 3 describe('Tidy Test', () => { 4 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 fixture = TestBed.createComponent(TidyTestComponent); 9 loader = TestbedHarnessEnvironment.loader(fixture); 10 }); 11 }); 12 fixture = TestBed.createComponent(TidyTestComponent); loader = TestbedHarnessEnvironment.loader(fixture); import { HarnessLoader } from '@angular/cdk/testing'; 1 import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 2 3 describe('Tidy Test', () => { 4 let loader: HarnessLoader; 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 9 10 }); 11 }); 12

Slide 38

Slide 38 text

GET A HARNESSLOADER FOR YOUR ENVIRONMENT import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; describe('Tidy Test', () => { let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({...}); fixture = TestBed.createComponent(TidyTestComponent); loader = TestbedHarnessEnvironment.loader(fixture); }); }); 1 2 3 4 5 6 7 8 9 10 11 12 import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 1 2 3 describe('Tidy Test', () => { 4 let loader: HarnessLoader; 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 fixture = TestBed.createComponent(TidyTestComponent); 9 loader = TestbedHarnessEnvironment.loader(fixture); 10 }); 11 }); 12 let loader: HarnessLoader; import { HarnessLoader } from '@angular/cdk/testing'; 1 import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 2 3 describe('Tidy Test', () => { 4 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 fixture = TestBed.createComponent(TidyTestComponent); 9 loader = TestbedHarnessEnvironment.loader(fixture); 10 }); 11 }); 12 fixture = TestBed.createComponent(TidyTestComponent); loader = TestbedHarnessEnvironment.loader(fixture); import { HarnessLoader } from '@angular/cdk/testing'; 1 import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; 2 3 describe('Tidy Test', () => { 4 let loader: HarnessLoader; 5 6 beforeEach(() => { 7 TestBed.configureTestingModule({...}); 8 9 10 }); 11 }); 12 import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; describe('Tidy Test', () => { let loader: HarnessLoader; beforeEach(() => { TestBed.configureTestingModule({...}); fixture = TestBed.createComponent(TidyTestComponent); loader = TestbedHarnessEnvironment.loader(fixture); }); }); 1 2 3 4 5 6 7 8 9 10 11 12

Slide 39

Slide 39 text

GET HARNESSES const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 1 2 3 4 5 6

Slide 40

Slide 40 text

GET HARNESSES const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 1 2 3 4 5 6 const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); 3 4 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); 5 const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 6

Slide 41

Slide 41 text

GET HARNESSES const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 1 2 3 4 5 6 const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); 3 4 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); 5 const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 6 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 3 4 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); 5 const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 6

Slide 42

Slide 42 text

GET HARNESSES const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 1 2 3 4 5 6 const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); 3 4 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); 5 const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 6 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 3 4 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); 5 const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 6 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); 3 4 5 6

Slide 43

Slide 43 text

GET HARNESSES const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 1 2 3 4 5 6 const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); 3 4 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); 5 const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 6 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 3 4 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); 5 const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 6 const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); 1 2 const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); 3 4 5 6 const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness); const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector'); const childBtn: MatButtonHarness = await childLoader.getHarness(MatButtonHarness); 1 2 3 4 5 6

Slide 44

Slide 44 text

FILTER FOR SPECIFIC HARNESSES const btns: MatButtonHarness[] = await loader.getAllHarnesses( MatButtonHarness.with({text: 'delete'}) ); 1 2 3

Slide 45

Slide 45 text

ACCESS THE HOST ELEMENT const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness); const btnHost: TestElement = await btn.host(); await btnHost.hover(); 1 2 3

Slide 46

Slide 46 text

OPTIMIZE MULTIPLE ACTIONS const checkbox: MatCheckboxHarness = await loader.getHarness(MatCheckboxHarness); const [checked, label] = await parallel(() => [ checkbox.isChecked(), checkbox.getLabelText() ]); 1 2 3 4 5

Slide 47

Slide 47 text

THESE ARE INTEGRATION TESTS You are manipulating child components

Slide 48

Slide 48 text

THESE ARE INTEGRATION TESTS You are manipulating child components Write your tests with intention

Slide 49

Slide 49 text

IMPLEMENTING A TEST HARNESS FOR A CUSTOM COMPONENT

Slide 50

Slide 50 text

A TIDY LIST TODO APP Take a look at the sentiment rating. We'll write a test harness for the component.

Slide 51

Slide 51 text

THE COMPONENT WE'LL WRITE A HARNESS FOR @Component({ selector: 'app-sentiment-rating', template: ` i ? 'accent' : ''"> = rate">favorite_border i">favorite ` }) export class SentimentRatingComponent{ public sentimentRating = [1, 2, 3, 4, 5]; @Input() public rate = 0; @Output() public changed = new EventEmitter(); public onRating(rating: number): void { this.rate = rating; this.changed.emit(rating); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

Slide 52

Slide 52 text

THE COMPONENT WE'LL WRITE A HARNESS FOR @Component({ selector: 'app-sentiment-rating', template: ` i ? 'accent' : ''"> = rate">favorite_border i">favorite ` }) export class SentimentRatingComponent{ public sentimentRating = [1, 2, 3, 4, 5]; @Input() public rate = 0; @Output() public changed = new EventEmitter(); public onRating(rating: number): void { this.rate = rating; this.changed.emit(rating); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 selector: 'app-sentiment-rating', @Component({ 1 2 template: ` 3 i ? 'accent' : ''"> 7 8 = rate">favorite_border 9 i">favorite 10 11 ` 12 }) 13 export class SentimentRatingComponent{ 14 public sentimentRating = [1, 2, 3, 4, 5]; 15 @Input() public rate = 0; 16 @Output() public changed = new EventEmitter(); 17 18 public onRating(rating: number): void { 19 this.rate = rating; 20 this.changed.emit(rating); 21 } 22 } 23

Slide 53

Slide 53 text

THE COMPONENT WE'LL WRITE A HARNESS FOR @Component({ selector: 'app-sentiment-rating', template: ` i ? 'accent' : ''"> = rate">favorite_border i">favorite ` }) export class SentimentRatingComponent{ public sentimentRating = [1, 2, 3, 4, 5]; @Input() public rate = 0; @Output() public changed = new EventEmitter(); public onRating(rating: number): void { this.rate = rating; this.changed.emit(rating); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 selector: 'app-sentiment-rating', @Component({ 1 2 template: ` 3 i ? 'accent' : ''"> 7 8 = rate">favorite_border 9 i">favorite 10 11 ` 12 }) 13 export class SentimentRatingComponent{ 14 public sentimentRating = [1, 2, 3, 4, 5]; 15 @Input() public rate = 0; 16 @Output() public changed = new EventEmitter(); 17 18 public onRating(rating: number): void { 19 this.rate = rating; 20 this.changed.emit(rating); 21 } 22 } 23 = rate">favorite_border i">favorite @Component({ 1 selector: 'app-sentiment-rating', 2 template: ` 3 4 *ngFor="let rating of sentimentRating; index as i" 5 (click)="onRating(rating)" 6 [color]="rate > i ? 'accent' : ''"> 7 8 9 10 11 ` 12 }) 13 export class SentimentRatingComponent{ 14 public sentimentRating = [1, 2, 3, 4, 5]; 15 @Input() public rate = 0; 16 @Output() public changed = new EventEmitter(); 17 18 public onRating(rating: number): void { 19 this.rate = rating; 20 this.changed.emit(rating); 21 } 22 } 23

Slide 54

Slide 54 text

DEFINE THE FILTERS INTERFACE TO SUPPORT QUERYING import { BaseHarnessFilters } from '@angular/cdk/testing'; export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { rate?: number; } 1 2 3 4 5

Slide 55

Slide 55 text

DEFINE THE FILTERS INTERFACE TO SUPPORT QUERYING import { BaseHarnessFilters } from '@angular/cdk/testing'; export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { rate?: number; } 1 2 3 4 5 import { BaseHarnessFilters } from '@angular/cdk/testing'; export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { 1 2 3 rate?: number; 4 } 5

Slide 56

Slide 56 text

DEFINE THE FILTERS INTERFACE TO SUPPORT QUERYING import { BaseHarnessFilters } from '@angular/cdk/testing'; export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { rate?: number; } 1 2 3 4 5 import { BaseHarnessFilters } from '@angular/cdk/testing'; export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { 1 2 3 rate?: number; 4 } 5 rate?: number; import { BaseHarnessFilters } from '@angular/cdk/testing'; 1 2 export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { 3 4 } 5

Slide 57

Slide 57 text

DEFINE THE FILTERS INTERFACE TO SUPPORT QUERYING import { BaseHarnessFilters } from '@angular/cdk/testing'; export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { rate?: number; } 1 2 3 4 5 import { BaseHarnessFilters } from '@angular/cdk/testing'; export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { 1 2 3 rate?: number; 4 } 5 rate?: number; import { BaseHarnessFilters } from '@angular/cdk/testing'; 1 2 export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { 3 4 } 5 import { BaseHarnessFilters } from '@angular/cdk/testing'; export interface SentimentRatingHarnessFilters extends BaseHarnessFilters { rate?: number; } 1 2 3 4 5

Slide 58

Slide 58 text

IMPLEMENT YOUR COMPONENT TEST HARNESS import { ComponentHarness } from '@angular/cdk/testing'; export class SentimentRatingHarness extends ComponentHarness { } 1 2 3 4 5

Slide 59

Slide 59 text

IMPLEMENT YOUR COMPONENT TEST HARNESS static hostSelector = 'app-sentiment-rating'; import { ComponentHarness } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 4 } 5

Slide 60

Slide 60 text

IMPLEMENT YOUR COMPONENT TEST HARNESS private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 6 7 public async getRate(): Promise { 8 9 } 10 11 public async setRate(rate: number): Promise { 12 13 } 14 } 15

Slide 61

Slide 61 text

IMPLEMENT YOUR COMPONENT TEST HARNESS private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 6 7 public async getRate(): Promise { 8 9 } 10 11 public async setRate(rate: number): Promise { 12 13 } 14 } 15 public async getRate(): Promise { } import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 8 9 10 11 public async setRate(rate: number): Promise { 12 13 } 14 } 15

Slide 62

Slide 62 text

IMPLEMENT YOUR COMPONENT TEST HARNESS private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 6 7 public async getRate(): Promise { 8 9 } 10 11 public async setRate(rate: number): Promise { 12 13 } 14 } 15 public async getRate(): Promise { } import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 8 9 10 11 public async setRate(rate: number): Promise { 12 13 } 14 } 15 public async setRate(rate: number): Promise { } import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 9 } 10 11 12 13 14 } 15

Slide 63

Slide 63 text

IMPLEMENT YOUR COMPONENT TEST HARNESS private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 6 7 public async getRate(): Promise { 8 9 } 10 11 public async setRate(rate: number): Promise { 12 13 } 14 } 15 public async getRate(): Promise { } import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 8 9 10 11 public async setRate(rate: number): Promise { 12 13 } 14 } 15 public async setRate(rate: number): Promise { } import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 9 } 10 11 12 13 14 } 15 import { AsyncFactoryFn, ComponentHarness, TestElement } from '@angular/cdk/testing'; export class SentimentRatingHarness extends ComponentHarness { static hostSelector = 'app-sentiment-rating'; private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); public async getRate(): Promise { } public async setRate(rate: number): Promise { } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Slide 64

Slide 64 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async getRate(): Promise { const btns = await this._rateButtons(); return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); } import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 8 9 10 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16

Slide 65

Slide 65 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async getRate(): Promise { const btns = await this._rateButtons(); return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); } import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 8 9 10 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16 const btns = await this._rateButtons(); import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16

Slide 66

Slide 66 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async getRate(): Promise { const btns = await this._rateButtons(); return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); } import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 8 9 10 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16 const btns = await this._rateButtons(); import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 10 } 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16

Slide 67

Slide 67 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async getRate(): Promise { const btns = await this._rateButtons(); return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); } import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 8 9 10 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16 const btns = await this._rateButtons(); import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 10 } 11 12 public async setRate(rate: number): Promise { 13 14 } 15 } 16 import { AsyncFactoryFn, ComponentHarness, parallel, TestElement } from '@angular/cdk/testing'; export class SentimentRatingHarness extends ComponentHarness { static hostSelector = 'app-sentiment-rating'; private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); public async getRate(): Promise { const btns = await this._rateButtons(); return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc : acc, 0); } public async setRate(rate: number): Promise { } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 68

Slide 68 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async setRate(rate: number): Promise { if (rate <= 0) throw Error('Rate is invalid'); const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); return (await btns[rate - 1]).click(); } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 13 14 15 16 17 18 19 } 20

Slide 69

Slide 69 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async setRate(rate: number): Promise { if (rate <= 0) throw Error('Rate is invalid'); const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); return (await btns[rate - 1]).click(); } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 13 14 15 16 17 18 19 } 20 if (rate <= 0) throw Error('Rate is invalid'); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 14 15 const btns = await this._rateButtons(); 16 if (btns.length < rate) throw Error('Rate exceeds supported rate options'); 17 return (await btns[rate - 1]).click(); 18 } 19 } 20

Slide 70

Slide 70 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async setRate(rate: number): Promise { if (rate <= 0) throw Error('Rate is invalid'); const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); return (await btns[rate - 1]).click(); } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 13 14 15 16 17 18 19 } 20 if (rate <= 0) throw Error('Rate is invalid'); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 14 15 const btns = await this._rateButtons(); 16 if (btns.length < rate) throw Error('Rate exceeds supported rate options'); 17 return (await btns[rate - 1]).click(); 18 } 19 } 20 const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 if (rate <= 0) throw Error('Rate is invalid'); 14 15 16 17 return (await btns[rate - 1]).click(); 18 } 19 } 20

Slide 71

Slide 71 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async setRate(rate: number): Promise { if (rate <= 0) throw Error('Rate is invalid'); const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); return (await btns[rate - 1]).click(); } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 13 14 15 16 17 18 19 } 20 if (rate <= 0) throw Error('Rate is invalid'); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 14 15 const btns = await this._rateButtons(); 16 if (btns.length < rate) throw Error('Rate exceeds supported rate options'); 17 return (await btns[rate - 1]).click(); 18 } 19 } 20 const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 if (rate <= 0) throw Error('Rate is invalid'); 14 15 16 17 return (await btns[rate - 1]).click(); 18 } 19 } 20 return (await btns[rate - 1]).click(); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 if (rate <= 0) throw Error('Rate is invalid'); 14 15 const btns = await this._rateButtons(); 16 if (btns.length < rate) throw Error('Rate exceeds supported rate options'); 17 18 } 19 } 20

Slide 72

Slide 72 text

IMPLEMENT YOUR COMPONENT TEST HARNESS public async setRate(rate: number): Promise { if (rate <= 0) throw Error('Rate is invalid'); const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); return (await btns[rate - 1]).click(); } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 13 14 15 16 17 18 19 } 20 if (rate <= 0) throw Error('Rate is invalid'); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 14 15 const btns = await this._rateButtons(); 16 if (btns.length < rate) throw Error('Rate exceeds supported rate options'); 17 return (await btns[rate - 1]).click(); 18 } 19 } 20 const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 if (rate <= 0) throw Error('Rate is invalid'); 14 15 16 17 return (await btns[rate - 1]).click(); 18 } 19 } 20 return (await btns[rate - 1]).click(); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 export class SentimentRatingHarness extends ComponentHarness { 3 static hostSelector = 'app-sentiment-rating'; 4 5 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 6 7 public async getRate(): Promise { 8 const btns = await this._rateButtons(); 9 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 10 } 11 12 public async setRate(rate: number): Promise { 13 if (rate <= 0) throw Error('Rate is invalid'); 14 15 const btns = await this._rateButtons(); 16 if (btns.length < rate) throw Error('Rate exceeds supported rate options'); 17 18 } 19 } 20 import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; export class SentimentRatingHarness extends ComponentHarness { static hostSelector = 'app-sentiment-rating'; private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); public async getRate(): Promise { const btns = await this._rateButtons(); return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); } public async setRate(rate: number): Promise { if (rate <= 0) throw Error('Rate is invalid'); const btns = await this._rateButtons(); if (btns.length < rate) throw Error('Rate exceeds supported rate options'); return (await btns[rate - 1]).click(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

Slide 73

Slide 73 text

IMPLEMENT YOUR COMPONENT TEST HARNESS import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24

Slide 74

Slide 74 text

IMPLEMENT YOUR COMPONENT TEST HARNESS import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24

Slide 75

Slide 75 text

IMPLEMENT YOUR COMPONENT TEST HARNESS import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 return new HarnessPredicate(SentimentRatingHarness, options) .addOption('rate', options.rate, ); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 8 9 async (harness, rate) => await harness.getRate() === rate 10 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24

Slide 76

Slide 76 text

IMPLEMENT YOUR COMPONENT TEST HARNESS import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 return new HarnessPredicate(SentimentRatingHarness, options) .addOption('rate', options.rate, ); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 8 9 async (harness, rate) => await harness.getRate() === rate 10 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 async (harness, rate) => await harness.getRate() === rate import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 10 ); 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24

Slide 77

Slide 77 text

IMPLEMENT YOUR COMPONENT TEST HARNESS import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { } import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 async (harness, rate) => await harness.getRate() === rate 10 ); 11 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 return new HarnessPredicate(SentimentRatingHarness, options) .addOption('rate', options.rate, ); import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 8 9 async (harness, rate) => await harness.getRate() === rate 10 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 async (harness, rate) => await harness.getRate() === rate import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; 1 import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; 2 3 export class SentimentRatingHarness extends ComponentHarness { 4 static hostSelector = 'app-sentiment-rating'; 5 6 static with(options: SentimentRatingHarnessFilters): HarnessPredicate { 7 return new HarnessPredicate(SentimentRatingHarness, options) 8 .addOption('rate', options.rate, 9 10 ); 11 } 12 13 private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); 14 15 public async getRate(): Promise { 16 const btns = await this._rateButtons(); 17 return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); 18 } 19 20 public async setRate(rate: number): Promise { 21 if (rate <= 0) throw Error('Rate is invalid'); 22 23 const btns = await this._rateButtons(); 24 import { AsyncFactoryFn, ComponentHarness, HarnessPredicate, parallel, TestElement } from '@angular/cdk/testing'; import { SentimentRatingHarnessFilters } from './sentiment-rating-harness-filters'; export class SentimentRatingHarness extends ComponentHarness { static hostSelector = 'app-sentiment-rating'; static with(options: SentimentRatingHarnessFilters): HarnessPredicate { return new HarnessPredicate(SentimentRatingHarness, options) .addOption('rate', options.rate, async (harness, rate) => await harness.getRate() === rate ); } private _rateButtons: AsyncFactoryFn = this.locatorForAll('button'); public async getRate(): Promise { const btns = await this._rateButtons(); return (await parallel(() => btns.map(b => b.text()))).reduce((acc, curr) => curr === 'favorite' ? ++acc: acc, 0); } public async setRate(rate: number): Promise { if (rate <= 0) throw Error('Rate is invalid'); const btns = await this._rateButtons(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

Slide 78

Slide 78 text

SCALES WITH COMPONENT COMPLEXITY More methods for locating elements

Slide 79

Slide 79 text

SCALES WITH COMPONENT COMPLEXITY More methods for locating elements Supports components that host other components

Slide 80

Slide 80 text

TEST YOUR COMPONENT TEST HARNESS Don't forget to test your harness - treat it like publishing an API

Slide 81

Slide 81 text

TEST YOUR COMPONENT TEST HARNESS Don't forget to test your harness - treat it like publishing an API Create a test host for your component and write tests utilizing your component test harness

Slide 82

Slide 82 text

TIDY TESTS THAT BRING YOU JOY Are you inspired to to tidy up your tests?

Slide 83

Slide 83 text

WANT TO LEARN MORE? Slides Tidy Task Todo App Using Angular Material component harnesses guide on Test harness API docs on @ALISADUNCAN alisaduncan.github.io/component-harness https://github.com/alisaduncan/component-harness-code material.angular.io material.angular.io