From 6e0481572fee60ad9f822adf71ce50cba5a7f978 Mon Sep 17 00:00:00 2001 From: shejialuo Date: Mon, 10 Jan 2022 21:24:22 +0800 Subject: [PATCH] docs(docs-infra): change the testing guide due to deprecation (#44674) The jasmine used in the docs is ```typescript expect().(predicate, failOutput) ``` The new version should be ```typescript expect().withContext(failOutput).(predicate) ``` So, this commit mainly focuses on changing the former to latter with format below ```typescript expect() .withContext(failOutput) .(predicate) ``` And for RxJs, see https://rxjs.dev/deprecations/subscribe-arguments. > For example someone could name functions poorly and confuse the next reader: `source$.subscribe(doSomething, doSomethingElse, lol)` with that signature, you have to know unapparent details about subscribe, where using a partial observer solves that neatly: `source$.subscribe({ next: doSomething, error: doSomethingElse, complete: lol }`). This commit also does this conversion. Last, remove the unused imported `async` from file `twain.component.marbles.spec.ts`. PR Close #44674 --- .../src/app/app.component.router.spec.ts | 8 +- .../testing/src/app/app.component.spec.ts | 8 +- .../dashboard.component.no-testbed.spec.ts | 30 +-- .../app/dashboard/dashboard.component.spec.ts | 19 +- .../testing/src/app/demo/async-helper.spec.ts | 17 +- .../testing/src/app/demo/demo.spec.ts | 32 ++- .../testing/src/app/demo/demo.testbed.spec.ts | 197 ++++++++++++------ .../hero-detail.component.no-testbed.spec.ts | 16 +- .../app/hero/hero-detail.component.spec.ts | 56 +++-- .../src/app/hero/hero-list.component.spec.ts | 28 ++- .../src/app/model/hero.service.spec.ts | 92 ++++---- .../src/app/model/testing/http-client.spec.ts | 36 ++-- .../app/shared/highlight.directive.spec.ts | 8 +- .../app/twain/twain.component.marbles.spec.ts | 26 ++- .../src/app/twain/twain.component.spec.ts | 87 ++++++-- .../src/app/welcome/welcome.component.spec.ts | 16 +- 16 files changed, 468 insertions(+), 208 deletions(-) diff --git a/aio/content/examples/testing/src/app/app.component.router.spec.ts b/aio/content/examples/testing/src/app/app.component.router.spec.ts index 8e17162120e4b..c6676a19c3260 100644 --- a/aio/content/examples/testing/src/app/app.component.router.spec.ts +++ b/aio/content/examples/testing/src/app/app.component.router.spec.ts @@ -170,11 +170,15 @@ class Page { } function expectPathToBe(path: string, expectationFailOutput?: any) { - expect(location.path()).toEqual(path, expectationFailOutput || 'location.path()'); + expect(location.path()) + .withContext(expectationFailOutput || 'location.path()') + .toEqual(path); } function expectElementOf(type: Type): any { const el = fixture.debugElement.query(By.directive(type)); - expect(el).toBeTruthy('expected an element for ' + type.name); + expect(el) + .withContext(`expected an element for ${type.name}`) + .toBeTruthy(); return el; } diff --git a/aio/content/examples/testing/src/app/app.component.spec.ts b/aio/content/examples/testing/src/app/app.component.spec.ts index 2c0971b2c9282..781a3bbe42a68 100644 --- a/aio/content/examples/testing/src/app/app.component.spec.ts +++ b/aio/content/examples/testing/src/app/app.component.spec.ts @@ -121,7 +121,9 @@ function tests() { // #docregion tests it('can get RouterLinks from template', () => { - expect(routerLinks.length).toBe(3, 'should have 3 routerLinks'); + expect(routerLinks.length) + .withContext('should have 3 routerLinks') + .toBe(3); expect(routerLinks[0].linkParams).toBe('/dashboard'); expect(routerLinks[1].linkParams).toBe('/heroes'); expect(routerLinks[2].linkParams).toBe('/about'); @@ -131,7 +133,9 @@ function tests() { const heroesLinkDe = linkDes[1]; // heroes link DebugElement const heroesLink = routerLinks[1]; // heroes link directive - expect(heroesLink.navigatedTo).toBeNull('should not have navigated yet'); + expect(heroesLink.navigatedTo) + .withContext('should not have navigated yet') + .toBeNull(); heroesLinkDe.triggerEventHandler('click', null); fixture.detectChanges(); diff --git a/aio/content/examples/testing/src/app/dashboard/dashboard.component.no-testbed.spec.ts b/aio/content/examples/testing/src/app/dashboard/dashboard.component.no-testbed.spec.ts index ee93170bfca4a..116d8a2fa9e2f 100644 --- a/aio/content/examples/testing/src/app/dashboard/dashboard.component.no-testbed.spec.ts +++ b/aio/content/examples/testing/src/app/dashboard/dashboard.component.no-testbed.spec.ts @@ -23,27 +23,31 @@ describe('DashboardComponent class only', () => { }); it('should NOT have heroes before calling OnInit', () => { - expect(comp.heroes.length).toBe(0, - 'should not have heroes before OnInit'); + expect(comp.heroes.length) + .withContext('should not have heroes before OnInit') + .toBe(0); }); it('should NOT have heroes immediately after OnInit', () => { comp.ngOnInit(); // ngOnInit -> getHeroes - expect(comp.heroes.length).toBe(0, - 'should not have heroes until service promise resolves'); + expect(comp.heroes.length) + .withContext('should not have heroes until service promise resolves') + .toBe(0); }); it('should HAVE heroes after HeroService gets them', (done: DoneFn) => { comp.ngOnInit(); // ngOnInit -> getHeroes heroService.lastResult // the one from getHeroes - .subscribe( - () => { + .subscribe({ + next: () => { // throw new Error('deliberate error'); // see it fail gracefully - expect(comp.heroes.length).toBeGreaterThan(0, - 'should have heroes after service promise resolves'); - done(); - }, - done.fail); + expect(comp.heroes.length) + .withContext('should have heroes after service promise resolves') + .toBeGreaterThan(0); + done(); + }, + error: done.fail + }); }); it('should tell ROUTER to navigate by hero id', () => { @@ -53,7 +57,9 @@ describe('DashboardComponent class only', () => { comp.gotoDetail(hero); const navArgs = spy.calls.mostRecent().args[0]; - expect(navArgs).toBe('/heroes/42', 'should nav to HeroDetail for Hero 42'); + expect(navArgs) + .withContext('should nav to HeroDetail for Hero 42') + .toBe('/heroes/42'); }); }); diff --git a/aio/content/examples/testing/src/app/dashboard/dashboard.component.spec.ts b/aio/content/examples/testing/src/app/dashboard/dashboard.component.spec.ts index ced494def2a3d..1a896d496f4c9 100644 --- a/aio/content/examples/testing/src/app/dashboard/dashboard.component.spec.ts +++ b/aio/content/examples/testing/src/app/dashboard/dashboard.component.spec.ts @@ -87,13 +87,17 @@ function compileAndCreate() { function tests(heroClick: () => void) { it('should NOT have heroes before ngOnInit', () => { - expect(comp.heroes.length).toBe(0, 'should not have heroes before ngOnInit'); + expect(comp.heroes.length) + .withContext('should not have heroes before ngOnInit') + .toBe(0); }); it('should NOT have heroes immediately after ngOnInit', () => { fixture.detectChanges(); // runs initial lifecycle hooks - expect(comp.heroes.length).toBe(0, 'should not have heroes until service promise resolves'); + expect(comp.heroes.length) + .withContext('should not have heroes until service promise resolves') + .toBe(0); }); describe('after get dashboard heroes', () => { @@ -109,14 +113,17 @@ function tests(heroClick: () => void) { it('should HAVE heroes', () => { expect(comp.heroes.length) - .toBeGreaterThan(0, 'should have heroes after service promise resolves'); + .withContext('should have heroes after service promise resolves') + .toBeGreaterThan(0); }); it('should DISPLAY heroes', () => { // Find and examine the displayed heroes // Look for them in the DOM by css class const heroes = fixture.nativeElement.querySelectorAll('dashboard-hero'); - expect(heroes.length).toBe(4, 'should display 4 heroes'); + expect(heroes.length) + .withContext('should display 4 heroes') + .toBe(4); }); // #docregion navigate-test @@ -129,7 +136,9 @@ function tests(heroClick: () => void) { // expecting to navigate to id of the component's first hero const id = comp.heroes[0].id; - expect(navArgs).toBe('/heroes/' + id, 'should nav to HeroDetail for first hero'); + expect(navArgs) + .withContext('should nav to HeroDetail for first hero') + .toBe('/heroes/' + id); }); // #enddocregion navigate-test }); diff --git a/aio/content/examples/testing/src/app/demo/async-helper.spec.ts b/aio/content/examples/testing/src/app/demo/async-helper.spec.ts index c6ebbf5cb307e..3f75b8184b931 100644 --- a/aio/content/examples/testing/src/app/demo/async-helper.spec.ts +++ b/aio/content/examples/testing/src/app/demo/async-helper.spec.ts @@ -11,7 +11,9 @@ describe('Angular async helper', () => { }); afterEach(() => { - expect(actuallyDone).toBe(true, 'actuallyDone should be true'); + expect(actuallyDone) + .withContext('actuallyDone should be true') + .toBe(true); }); it('should run normal test', () => { @@ -59,17 +61,24 @@ describe('Angular async helper', () => { // Use done. Can also use async or fakeAsync. it('should run async test with successful delayed Observable', (done: DoneFn) => { const source = of(true).pipe(delay(10)); - source.subscribe(val => actuallyDone = true, err => fail(err), done); + source.subscribe({ + next: val => actuallyDone = true, + error: err => fail(err), + complete: done}); }); it('should run async test with successful delayed Observable', waitForAsync(() => { const source = of(true).pipe(delay(10)); - source.subscribe(val => actuallyDone = true, err => fail(err)); + source.subscribe({ + next: val => actuallyDone = true, + error: err => fail(err)}); })); it('should run async test with successful delayed Observable', fakeAsync(() => { const source = of(true).pipe(delay(10)); - source.subscribe(val => actuallyDone = true, err => fail(err)); + source.subscribe({ + next: val => actuallyDone = true, + error: err => fail(err)}); tick(10); })); diff --git a/aio/content/examples/testing/src/app/demo/demo.spec.ts b/aio/content/examples/testing/src/app/demo/demo.spec.ts index bf427155242c2..9ef0d6138e3b2 100644 --- a/aio/content/examples/testing/src/app/demo/demo.spec.ts +++ b/aio/content/examples/testing/src/app/demo/demo.spec.ts @@ -74,9 +74,11 @@ describe('demo (no TestBed):', () => { masterService = new MasterService(valueServiceSpy); expect(masterService.getValue()) - .toBe(stubValue, 'service returned stub value'); + .withContext('service returned stub value') + .toBe(stubValue); expect(valueServiceSpy.getValue.calls.count()) - .toBe(1, 'spy method was called once'); + .withContext('spy method was called once') + .toBe(1); expect(valueServiceSpy.getValue.calls.mostRecent().returnValue) .toBe(stubValue); }); @@ -90,9 +92,11 @@ describe('demo (no TestBed):', () => { const { masterService, stubValue, valueServiceSpy } = setup(); // #enddocregion no-before-each-setup-call expect(masterService.getValue()) - .toBe(stubValue, 'service returned stub value'); + .withContext('service returned stub value') + .toBe(stubValue); expect(valueServiceSpy.getValue.calls.count()) - .toBe(1, 'spy method was called once'); + .withContext('spy method was called once') + .toBe(1); expect(valueServiceSpy.getValue.calls.mostRecent().returnValue) .toBe(stubValue); }); @@ -131,18 +135,28 @@ describe('demo (no TestBed):', () => { describe('LightswitchComp', () => { it('#clicked() should toggle #isOn', () => { const comp = new LightswitchComponent(); - expect(comp.isOn).toBe(false, 'off at first'); + expect(comp.isOn) + .withContext('off at first') + .toBe(false); comp.clicked(); - expect(comp.isOn).toBe(true, 'on after click'); + expect(comp.isOn) + .withContext('on after click') + .toBe(true); comp.clicked(); - expect(comp.isOn).toBe(false, 'off after second click'); + expect(comp.isOn) + .withContext('off after second click') + .toBe(false); }); it('#clicked() should set #message to "is on"', () => { const comp = new LightswitchComponent(); - expect(comp.message).toMatch(/is off/i, 'off at first'); + expect(comp.message) + .withContext('off at first') + .toMatch(/is off/i); comp.clicked(); - expect(comp.message).toMatch(/is on/i, 'on after clicked'); + expect(comp.message) + .withContext('on after clicked') + .toMatch(/is on/i); }); }); // #enddocregion Lightswitch diff --git a/aio/content/examples/testing/src/app/demo/demo.testbed.spec.ts b/aio/content/examples/testing/src/app/demo/demo.testbed.spec.ts index 1bb7efa82da6f..872d2d3a36c25 100644 --- a/aio/content/examples/testing/src/app/demo/demo.testbed.spec.ts +++ b/aio/content/examples/testing/src/app/demo/demo.testbed.spec.ts @@ -119,9 +119,11 @@ describe('demo (with TestBed):', () => { valueServiceSpy.getValue.and.returnValue(stubValue); expect(masterService.getValue()) - .toBe(stubValue, 'service returned stub value'); + .withContext('service returned stub value') + .toBe(stubValue); expect(valueServiceSpy.getValue.calls.count()) - .toBe(1, 'spy method was called once'); + .withContext('spy method was called once') + .toBe(1); expect(valueServiceSpy.getValue.calls.mostRecent().returnValue) .toBe(stubValue); }); @@ -201,7 +203,9 @@ describe('demo (with TestBed):', () => { fixture.detectChanges(); const heroes = fixture.debugElement.queryAll(By.css('.hero')); - expect(heroes.length).toBeGreaterThan(0, 'has heroes'); + expect(heroes.length) + .withContext('has heroes') + .toBeGreaterThan(0); const comp = fixture.componentInstance; const hero = comp.heroes[0]; @@ -222,12 +226,18 @@ describe('demo (with TestBed):', () => { const ngForRow = fixture.debugElement.query(By.directive(IoComponent)); // first hero ngForRow const hero = ngForRow.context.hero; // the hero object passed into the row - expect(hero.name).toBe(heroName, 'ngRow.context.hero'); + expect(hero.name) + .withContext('ngRow.context.hero') + .toBe(heroName); const rowComp = ngForRow.componentInstance; // jasmine.any is an "instance-of-type" test. - expect(rowComp).toEqual(jasmine.any(IoComponent), 'component is IoComp'); - expect(rowComp.hero.name).toBe(heroName, 'component.hero'); + expect(rowComp) + .withContext('component is IoComp') + .toEqual(jasmine.any(IoComponent)); + expect(rowComp.hero.name) + .withContext('component.hero') + .toBe(heroName); }); it('should support clicking a button', () => { @@ -236,11 +246,15 @@ describe('demo (with TestBed):', () => { const span = fixture.debugElement.query(By.css('span')).nativeElement; fixture.detectChanges(); - expect(span.textContent).toMatch(/is off/i, 'before click'); + expect(span.textContent) + .withContext('before click') + .toMatch(/is off/i); click(btn); fixture.detectChanges(); - expect(span.textContent).toMatch(/is on/i, 'after click'); + expect(span.textContent) + .withContext('after click') + .toMatch(/is on/i); }); // ngModel is async so we must wait for it with promise-based `whenStable` @@ -254,20 +268,23 @@ describe('demo (with TestBed):', () => { const comp = fixture.componentInstance; const input = fixture.debugElement.query(By.css('input')).nativeElement as HTMLInputElement; - expect(comp.name).toBe(expectedOrigName, - `At start name should be ${expectedOrigName} `); + expect(comp.name) + .withContext(`At start name should be ${expectedOrigName} `) + .toBe(expectedOrigName); // wait until ngModel binds comp.name to input box fixture.whenStable().then(() => { - expect(input.value).toBe(expectedOrigName, - `After ngModel updates input box, input.value should be ${expectedOrigName} `); + expect(input.value) + .withContext(`After ngModel updates input box, input.value should be ${expectedOrigName} `) + .toBe(expectedOrigName); // simulate user entering new name in input input.value = expectedNewName; // that change doesn't flow to the component immediately - expect(comp.name).toBe(expectedOrigName, - `comp.name should still be ${expectedOrigName} after value change, before binding happens`); + expect(comp.name) + .withContext(`comp.name should still be ${expectedOrigName} after value change, before binding happens`) + .toBe(expectedOrigName); // Dispatch a DOM event so that Angular learns of input value change. // then wait while ngModel pushes input.box value to comp.name @@ -277,8 +294,9 @@ describe('demo (with TestBed):', () => { return fixture.whenStable(); }) .then(() => { - expect(comp.name).toBe(expectedNewName, - `After ngModel updates the model, comp.name should be ${expectedNewName} `); + expect(comp.name) + .withContext(`After ngModel updates the model, comp.name should be ${expectedNewName} `) + .toBe(expectedNewName); }); })); @@ -294,20 +312,23 @@ describe('demo (with TestBed):', () => { const comp = fixture.componentInstance; const input = fixture.debugElement.query(By.css('input')).nativeElement as HTMLInputElement; - expect(comp.name).toBe(expectedOrigName, - `At start name should be ${expectedOrigName} `); + expect(comp.name) + .withContext(`At start name should be ${expectedOrigName} `) + .toBe(expectedOrigName); // wait until ngModel binds comp.name to input box tick(); - expect(input.value).toBe(expectedOrigName, - `After ngModel updates input box, input.value should be ${expectedOrigName} `); + expect(input.value) + .withContext(`After ngModel updates input box, input.value should be ${expectedOrigName} `) + .toBe(expectedOrigName); // simulate user entering new name in input input.value = expectedNewName; // that change doesn't flow to the component immediately - expect(comp.name).toBe(expectedOrigName, - `comp.name should still be ${expectedOrigName} after value change, before binding happens`); + expect(comp.name) + .withContext(`comp.name should still be ${expectedOrigName} after value change, before binding happens`) + .toBe(expectedOrigName); // Dispatch a DOM event so that Angular learns of input value change. // then wait a tick while ngModel pushes input.box value to comp.name @@ -315,8 +336,9 @@ describe('demo (with TestBed):', () => { // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill input.dispatchEvent(new Event('input')); tick(); - expect(comp.name).toBe(expectedNewName, - `After ngModel updates the model, comp.name should be ${expectedNewName} `); + expect(comp.name) + .withContext(`After ngModel updates the model, comp.name should be ${expectedNewName} `) + .toBe(expectedNewName); })); it('ReversePipeComp should reverse the input text', fakeAsync(() => { @@ -341,8 +363,12 @@ describe('demo (with TestBed):', () => { input.dispatchEvent(new Event('input')); tick(); fixture.detectChanges(); - expect(span.textContent).toBe(expectedText, 'output span'); - expect(comp.text).toBe(inputText, 'component.text'); + expect(span.textContent) + .withContext('output span') + .toBe(expectedText); + expect(comp.text) + .withContext('component.text') + .toBe(inputText); })); // Use this technique to find attached directives of any kind @@ -352,12 +378,18 @@ describe('demo (with TestBed):', () => { const inputEl = fixture.debugElement.query(By.css('input')); - expect(inputEl.providerTokens).toContain(NgModel, 'NgModel directive'); + expect(inputEl.providerTokens) + .withContext('NgModel directive') + .toContain(NgModel); const ngControl = inputEl.injector.get(NgControl); - expect(ngControl).toEqual(jasmine.any(NgControl), 'NgControl directive'); + expect(ngControl) + .withContext('NgControl directive') + .toEqual(jasmine.any(NgControl)); - expect(inputEl.listeners.length).toBeGreaterThan(2, 'several listeners attached'); + expect(inputEl.listeners.length) + .withContext('several listeners attached') + .toBeGreaterThan(2); }); it('BankAccountComponent should set attributes, styles, classes, and properties', () => { @@ -370,16 +402,30 @@ describe('demo (with TestBed):', () => { const childComp = el.componentInstance as BankAccountComponent; expect(childComp).toEqual(jasmine.any(BankAccountComponent)); - expect(el.context).toBe(childComp, 'context is the child component'); - - expect(el.attributes['account']).toBe(childComp.id, 'account attribute'); - expect(el.attributes['bank']).toBe(childComp.bank, 'bank attribute'); - - expect(el.classes['closed']).toBe(true, 'closed class'); - expect(el.classes['open']).toBeFalsy('open class'); - - expect(el.styles['color']).toBe(comp.color, 'color style'); - expect(el.styles['width']).toBe(comp.width + 'px', 'width style'); + expect(el.context) + .withContext('context is the child component') + .toBe(childComp); + + expect(el.attributes['account']) + .withContext('account attribute') + .toBe(childComp.id); + expect(el.attributes['bank']) + .withContext('bank attribute') + .toBe(childComp.bank); + + expect(el.classes['closed']) + .withContext('closed class') + .toBe(true); + expect(el.classes['open']) + .withContext('open class') + .toBeFalsy(); + + expect(el.styles['color']) + .withContext('color style') + .toBe(comp.color); + expect(el.styles['width']) + .withContext('width style') + .toBe(comp.width + 'px'); // Removed on 12/02/2016 when ceased public discussion of the `Renderer`. Revive in future? // expect(el.properties['customProperty']).toBe(true, 'customProperty'); @@ -423,9 +469,15 @@ describe('demo (with TestBed):', () => { // Explore the providerTokens const tokens = fixture.debugElement.providerTokens; - expect(tokens).toContain(fixture.componentInstance.constructor, 'component ctor'); - expect(tokens).toContain(TestProvidersComponent, 'TestProvidersComp'); - expect(tokens).toContain(ValueService, 'ValueService'); + expect(tokens) + .withContext('component ctor') + .toContain(fixture.componentInstance.constructor); + expect(tokens) + .withContext('TestProvidersComp') + .toContain(TestProvidersComponent); + expect(tokens) + .withContext('ValueService') + .toContain(ValueService); }); it("should override TestViewProvidersComp's ValueService viewProvider", () => { @@ -471,12 +523,22 @@ describe('demo (with TestBed):', () => { const tcProvider = fixture.debugElement.injector.get(ValueService) as ValueService; const tpcProvider = fixture.debugElement.children[0].injector.get(ValueService) as FakeValueService; - expect(testBedProvider).not.toBe(tcProvider, 'testBed/tc not same providers'); - expect(testBedProvider).not.toBe(tpcProvider, 'testBed/tpc not same providers'); + expect(testBedProvider) + .withContext('testBed/tc not same providers') + .not.toBe(tcProvider); + expect(testBedProvider) + .withContext('testBed/tpc not same providers') + .not.toBe(tpcProvider); - expect(testBedProvider instanceof ValueService).toBe(true, 'testBedProvider is ValueService'); - expect(tcProvider).toEqual({} as ValueService, 'tcProvider is {}'); - expect(tpcProvider instanceof FakeValueService).toBe(true, 'tpcProvider is FakeValueService'); + expect(testBedProvider instanceof ValueService) + .withContext('testBedProvider is ValueService') + .toBe(true); + expect(tcProvider) + .withContext('tcProvider is {}') + .toEqual({} as ValueService); + expect(tpcProvider instanceof FakeValueService) + .withContext('tpcProvider is FakeValueService') + .toBe(true); }); it('can access template local variables as references', () => { @@ -505,16 +567,21 @@ describe('demo (with TestBed):', () => { const el = fixture.debugElement.children[0]; const comp = el.componentInstance; - expect(comp.children.toArray().length).toBe(4, - 'three different child components and an ElementRef with #content'); + expect(comp.children.toArray().length) + .withContext('three different child components and an ElementRef with #content') + .toBe(4); - expect(el.references['nc']).toBe(comp, '#nc reference to component'); + expect(el.references['nc']) + .withContext('#nc reference to component') + .toBe(comp); // #docregion custom-predicate // Filter for DebugElements with a #content reference const contentRefs = el.queryAll( de => de.references['content']); // #enddocregion custom-predicate - expect(contentRefs.length).toBe(4, 'elements w/ a #content reference'); + expect(contentRefs.length) + .withContext('elements w/ a #content reference') + .toBe(4); }); }); @@ -565,7 +632,9 @@ describe('demo (with TestBed):', () => { }); it('should instantiate parent component', () => { - expect(parent).not.toBeNull('parent component should exist'); + expect(parent) + .withContext('parent component should exist') + .not.toBeNull(); }); it('parent component OnInit should NOT be called before first detectChanges()', () => { @@ -580,7 +649,9 @@ describe('demo (with TestBed):', () => { it('child component should exist after OnInit', () => { fixture.detectChanges(); getChild(); - expect(child instanceof MyIfChildComponent).toBe(true, 'should create child'); + expect(child instanceof MyIfChildComponent) + .withContext('should create child') + .toBe(true); }); it("should have called child component's OnInit ", () => { @@ -602,10 +673,12 @@ describe('demo (with TestBed):', () => { parent.parentValue = 'foo'; fixture.detectChanges(); - expect(child.ngOnChangesCounter).toBe(2, - 'expected 2 changes: initial value and changed value'); - expect(child.childValue).toBe('foo', - 'childValue should eq changed parent value'); + expect(child.ngOnChangesCounter) + .withContext('expected 2 changes: initial value and changed value') + .toBe(2); + expect(child.childValue) + .withContext('childValue should eq changed parent value') + .toBe('foo'); }); // must be async test to see child flow to parent @@ -622,10 +695,12 @@ describe('demo (with TestBed):', () => { .then(() => { fixture.detectChanges(); - expect(child.ngOnChangesCounter).toBe(2, - 'expected 2 changes: initial value and changed value'); - expect(parent.parentValue).toBe('bar', - 'parentValue should eq changed parent value'); + expect(child.ngOnChangesCounter) + .withContext('expected 2 changes: initial value and changed value') + .toBe(2); + expect(parent.parentValue) + .withContext('parentValue should eq changed parent value') + .toBe('bar'); }); })); diff --git a/aio/content/examples/testing/src/app/hero/hero-detail.component.no-testbed.spec.ts b/aio/content/examples/testing/src/app/hero/hero-detail.component.no-testbed.spec.ts index 9bd0c5b44a981..e325ed88177c6 100644 --- a/aio/content/examples/testing/src/app/hero/hero-detail.component.no-testbed.spec.ts +++ b/aio/content/examples/testing/src/app/hero/hero-detail.component.no-testbed.spec.ts @@ -36,13 +36,19 @@ describe('HeroDetailComponent - no TestBed', () => { it('should navigate when click cancel', () => { comp.cancel(); - expect(router.navigate.calls.any()).toBe(true, 'router.navigate called'); + expect(router.navigate.calls.any()) + .withContext('router.navigate called') + .toBe(true); }); it('should save when click save', () => { comp.save(); - expect(hds.saveHero.calls.any()).toBe(true, 'HeroDetailService.save called'); - expect(router.navigate.calls.any()).toBe(false, 'router.navigate not called yet'); + expect(hds.saveHero.calls.any()) + .withContext('HeroDetailService.save called') + .toBe(true); + expect(router.navigate.calls.any()) + .withContext('router.navigate not called yet') + .toBe(false); }); it('should navigate when click save resolves', (done: DoneFn) => { @@ -50,7 +56,9 @@ describe('HeroDetailComponent - no TestBed', () => { // waits for async save to complete before navigating hds.saveHero.calls.first().returnValue .subscribe(() => { - expect(router.navigate.calls.any()).toBe(true, 'router.navigate called'); + expect(router.navigate.calls.any()) + .withContext('router.navigate called') + .toBe(true); done(); }); }); diff --git a/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts b/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts index d5bc49acb31a1..01db4882c6400 100644 --- a/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts +++ b/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts @@ -87,7 +87,9 @@ function overrideSetup() { }); it('should have called `getHero`', () => { - expect(hdsSpy.getHero.calls.count()).toBe(1, 'getHero called once'); + expect(hdsSpy.getHero.calls.count()) + .withContext('getHero called once') + .toBe(1, 'getHero called once'); }); it("should display stub hero's name", () => { @@ -104,15 +106,25 @@ function overrideSetup() { // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill page.nameInput.dispatchEvent(new Event('input')); // tell Angular - expect(component.hero.name).toBe(newName, 'component hero has new name'); - expect(hdsSpy.testHero.name).toBe(origName, 'service hero unchanged before save'); + expect(component.hero.name) + .withContext('component hero has new name') + .toBe(newName); + expect(hdsSpy.testHero.name) + .withContext('service hero unchanged before save') + .toBe(origName); click(page.saveBtn); - expect(hdsSpy.saveHero.calls.count()).toBe(1, 'saveHero called once'); + expect(hdsSpy.saveHero.calls.count()) + .withContext('saveHero called once') + .toBe(1); tick(); // wait for async save to complete - expect(hdsSpy.testHero.name).toBe(newName, 'service hero has new name after save'); - expect(page.navigateSpy.calls.any()).toBe(true, 'router.navigate called'); + expect(hdsSpy.testHero.name) + .withContext('service hero has new name after save') + .toBe(newName); + expect(page.navigateSpy.calls.any()) + .withContext('router.navigate called') + .toBe(true); })); // #enddocregion override-tests @@ -122,7 +134,9 @@ function overrideSetup() { // use `fixture.debugElement.injector` to get service from component const componentService = fixture.debugElement.injector.get(HeroDetailService); - expect(fixtureService).not.toBe(componentService, 'service injected from fixture'); + expect(fixtureService) + .withContext('service injected from fixture') + .not.toBe(componentService); })); } @@ -170,7 +184,9 @@ function heroModuleSetup() { it('should navigate when click cancel', () => { click(page.cancelBtn); - expect(page.navigateSpy.calls.any()).toBe(true, 'router.navigate called'); + expect(page.navigateSpy.calls.any()) + .withContext('router.navigate called') + .toBe(true); }); it('should save when click save but not navigate immediately', () => { @@ -180,14 +196,20 @@ function heroModuleSetup() { const saveSpy = spyOn(hds, 'saveHero').and.callThrough(); click(page.saveBtn); - expect(saveSpy.calls.any()).toBe(true, 'HeroDetailService.save called'); - expect(page.navigateSpy.calls.any()).toBe(false, 'router.navigate not called'); + expect(saveSpy.calls.any()) + .withContext('HeroDetailService.save called') + .toBe(true); + expect(page.navigateSpy.calls.any()) + .withContext('router.navigate not called') + .toBe(false); }); it('should navigate when click save and save resolves', fakeAsync(() => { click(page.saveBtn); tick(); // wait for async save to complete - expect(page.navigateSpy.calls.any()).toBe(true, 'router.navigate called'); + expect(page.navigateSpy.calls.any()) + .withContext('router.navigate called') + .toBe(true); })); // #docregion title-case-pipe @@ -240,8 +262,12 @@ function heroModuleSetup() { }); it('should try to navigate back to hero list', () => { - expect(page.gotoListSpy.calls.any()).toBe(true, 'comp.gotoList called'); - expect(page.navigateSpy.calls.any()).toBe(true, 'router.navigate called'); + expect(page.gotoListSpy.calls.any()) + .withContext('comp.gotoList called') + .toBe(true); + expect(page.navigateSpy.calls.any()) + .withContext('router.navigate called') + .toBe(true); }); }); // #enddocregion route-bad-id @@ -258,7 +284,9 @@ function heroModuleSetup() { // get `HeroDetailService` with component's own injector service = fixture.debugElement.injector.get(HeroDetailService); - expect(service).toBeDefined('debugElement.injector'); + expect(service) + .withContext('debugElement.injector') + .toBeDefined(); }); } diff --git a/aio/content/examples/testing/src/app/hero/hero-list.component.spec.ts b/aio/content/examples/testing/src/app/hero/hero-list.component.spec.ts index 125f473d321fc..23d88cd9a79f0 100644 --- a/aio/content/examples/testing/src/app/hero/hero-list.component.spec.ts +++ b/aio/content/examples/testing/src/app/hero/hero-list.component.spec.ts @@ -46,8 +46,12 @@ describe('HeroListComponent', () => { it('1st hero should match 1st test hero', () => { const expectedHero = HEROES[0]; const actualHero = page.heroRows[0].textContent; - expect(actualHero).toContain(expectedHero.id.toString(), 'hero.id'); - expect(actualHero).toContain(expectedHero.name, 'hero.name'); + expect(actualHero) + .withContext('hero.id') + .toContain(expectedHero.id.toString()); + expect(actualHero) + .withContext('hero.name') + .toContain(expectedHero.name); }); it('should select hero on click', fakeAsync(() => { @@ -72,14 +76,20 @@ describe('HeroListComponent', () => { tick(); // should have navigated - expect(page.navSpy.calls.any()).toBe(true, 'navigate called'); + expect(page.navSpy.calls.any()) + .withContext('navigate called') + .toBe(true); // composed hero detail will be URL like 'heroes/42' // expect link array with the route path and hero id // first argument to router.navigate is link array const navArgs = page.navSpy.calls.first().args[0]; - expect(navArgs[0]).toContain('heroes', 'nav to heroes detail URL'); - expect(navArgs[1]).toBe(expectedHero.id, 'expected hero.id'); + expect(navArgs[0]) + .withContext('nav to heroes detail URL') + .toContain('heroes'); + expect(navArgs[1]) + .withContext('expected hero.id') + .toBe(expectedHero.id); })); it('should find `HighlightDirective` with `By.directive', () => { @@ -97,11 +107,15 @@ describe('HeroListComponent', () => { // different browsers report color values differently const isExpectedColor = bgColor === 'gold' || bgColor === 'rgb(255, 215, 0)'; - expect(isExpectedColor).toBe(true, 'backgroundColor'); + expect(isExpectedColor) + .withContext('backgroundColor') + .toBe(true); }); it("the `HighlightDirective` is among the element's providers", () => { - expect(page.highlightDe.providerTokens).toContain(HighlightDirective, 'HighlightDirective'); + expect(page.highlightDe.providerTokens) + .withContext('HighlightDirective') + .toContain(HighlightDirective); }); }); diff --git a/aio/content/examples/testing/src/app/model/hero.service.spec.ts b/aio/content/examples/testing/src/app/model/hero.service.spec.ts index 90384c3808e7b..8914cae42ec54 100644 --- a/aio/content/examples/testing/src/app/model/hero.service.spec.ts +++ b/aio/content/examples/testing/src/app/model/hero.service.spec.ts @@ -26,14 +26,18 @@ describe ('HeroesService (with spies)', () => { httpClientSpy.get.and.returnValue(asyncData(expectedHeroes)); - heroService.getHeroes().subscribe( - heroes => { - expect(heroes).toEqual(expectedHeroes, 'expected heroes'); + heroService.getHeroes().subscribe({ + next: heroes => { + expect(heroes) + .withContext('expected heroes') + .toEqual(expectedHeroes); done(); }, - done.fail - ); - expect(httpClientSpy.get.calls.count()).toBe(1, 'one call'); + error: done.fail + }); + expect(httpClientSpy.get.calls.count()) + .withContext('one call') + .toBe(1); }); it('should return an error when the server returns a 404', (done: DoneFn) => { @@ -44,13 +48,13 @@ describe ('HeroesService (with spies)', () => { httpClientSpy.get.and.returnValue(asyncError(errorResponse)); - heroService.getHeroes().subscribe( - heroes => done.fail('expected an error, not heroes'), - error => { + heroService.getHeroes().subscribe({ + next: heroes => done.fail('expected an error, not heroes'), + error: error => { expect(error.message).toContain('test 404 error'); done(); } - ); + }); }); // #enddocregion test-with-spies @@ -94,10 +98,12 @@ describe('HeroesService (with mocks)', () => { }); it('should return expected heroes (called once)', () => { - heroService.getHeroes().subscribe( - heroes => expect(heroes).toEqual(expectedHeroes, 'should return expected heroes'), - fail - ); + heroService.getHeroes().subscribe({ + next: heroes => expect(heroes) + .withContext('should return expected heroes') + .toEqual(expectedHeroes), + error: fail + }); // HeroService should have made one request to GET heroes from expected URL const req = httpTestingController.expectOne(heroService.heroesUrl); @@ -108,10 +114,12 @@ describe('HeroesService (with mocks)', () => { }); it('should be OK returning no heroes', () => { - heroService.getHeroes().subscribe( - heroes => expect(heroes.length).toEqual(0, 'should have empty heroes array'), - fail - ); + heroService.getHeroes().subscribe({ + next: heroes => expect(heroes.length) + .withContext('should have empty heroes array') + .toEqual(0), + error: fail + }); const req = httpTestingController.expectOne(heroService.heroesUrl); req.flush([]); // Respond with no heroes @@ -119,10 +127,10 @@ describe('HeroesService (with mocks)', () => { it('should turn 404 into a user-friendly error', () => { const msg = 'Deliberate 404'; - heroService.getHeroes().subscribe( - heroes => fail('expected to fail'), - error => expect(error.message).toContain(msg) - ); + heroService.getHeroes().subscribe({ + next: heroes => fail('expected to fail'), + error: error => expect(error.message).toContain(msg) + }); const req = httpTestingController.expectOne(heroService.heroesUrl); @@ -133,13 +141,17 @@ describe('HeroesService (with mocks)', () => { it('should return expected heroes (called multiple times)', () => { heroService.getHeroes().subscribe(); heroService.getHeroes().subscribe(); - heroService.getHeroes().subscribe( - heroes => expect(heroes).toEqual(expectedHeroes, 'should return expected heroes'), - fail - ); + heroService.getHeroes().subscribe({ + next: heroes => expect(heroes) + .withContext('should return expected heroes') + .toEqual(expectedHeroes), + error: fail + }); const requests = httpTestingController.match(heroService.heroesUrl); - expect(requests.length).toEqual(3, 'calls to getHeroes()'); + expect(requests.length) + .withContext('calls to getHeroes()') + .toEqual(3); // Respond to each request with different mock hero results requests[0].flush([]); @@ -156,10 +168,12 @@ describe('HeroesService (with mocks)', () => { const updateHero: Hero = { id: 1, name: 'A' }; - heroService.updateHero(updateHero).subscribe( - data => expect(data).toEqual(updateHero, 'should return the hero'), - fail - ); + heroService.updateHero(updateHero).subscribe({ + next: data => expect(data) + .withContext('should return the hero') + .toEqual(updateHero), + error: fail + }); // HeroService should have made one request to PUT hero const req = httpTestingController.expectOne(heroService.heroesUrl); @@ -175,10 +189,10 @@ describe('HeroesService (with mocks)', () => { it('should turn 404 error into user-facing error', () => { const msg = 'Deliberate 404'; const updateHero: Hero = { id: 1, name: 'A' }; - heroService.updateHero(updateHero).subscribe( - heroes => fail('expected to fail'), - error => expect(error.message).toContain(msg) - ); + heroService.updateHero(updateHero).subscribe({ + next: heroes => fail('expected to fail'), + error: error => expect(error.message).toContain(msg) + }); const req = httpTestingController.expectOne(heroService.heroesUrl); @@ -192,13 +206,13 @@ describe('HeroesService (with mocks)', () => { const errorEvent = new ProgressEvent('error'); const updateHero: Hero = { id: 1, name: 'A' }; - heroService.updateHero(updateHero).subscribe( - heroes => fail('expected to fail'), - error => { + heroService.updateHero(updateHero).subscribe({ + next: heroes => fail('expected to fail'), + error: error => { expect(error).toBe(errorEvent); done(); } - ); + }); const req = httpTestingController.expectOne(heroService.heroesUrl); diff --git a/aio/content/examples/testing/src/app/model/testing/http-client.spec.ts b/aio/content/examples/testing/src/app/model/testing/http-client.spec.ts index 3b1fccbf369f9..d266eb548a8a3 100644 --- a/aio/content/examples/testing/src/app/model/testing/http-client.spec.ts +++ b/aio/content/examples/testing/src/app/model/testing/http-client.spec.ts @@ -86,13 +86,19 @@ describe('HttpClient testing', () => { // Make three requests in a row httpClient.get(testUrl) - .subscribe(d => expect(d.length).toEqual(0, 'should have no data')); + .subscribe(d => expect(d.length) + .withContext('should have no data') + .toEqual(0)); httpClient.get(testUrl) - .subscribe(d => expect(d).toEqual([testData[0]], 'should be one element array')); + .subscribe(d => expect(d) + .withContext('should be one element array') + .toEqual([testData[0]])); httpClient.get(testUrl) - .subscribe(d => expect(d).toEqual(testData, 'should be expected data')); + .subscribe(d => expect(d) + .withContext('should be expected data') + .toEqual(testData)); // get all pending requests that match the given URL const requests = httpTestingController.match(testUrl); @@ -107,12 +113,16 @@ describe('HttpClient testing', () => { it('can test for 404 error', () => { const emsg = 'deliberate 404 error'; - httpClient.get(testUrl).subscribe( - data => fail('should have failed with the 404 error'), - (error: HttpErrorResponse) => { - expect(error.status).toEqual(404, 'status'); - expect(error.error).toEqual(emsg, 'message'); - } + httpClient.get(testUrl).subscribe({ + next: data => fail('should have failed with the 404 error'), + error: (error: HttpErrorResponse) => { + expect(error.status) + .withContext('status') + .toEqual(404); + expect(error.error) + .withContext('message') + .toEqual(emsg); + }} ); const req = httpTestingController.expectOne(testUrl); @@ -126,12 +136,12 @@ describe('HttpClient testing', () => { // the network level. Connection timeout, DNS error, offline, etc. const errorEvent = new ProgressEvent('error'); - httpClient.get(testUrl).subscribe( - data => fail('should have failed with the network error'), - (error: HttpErrorResponse) => { + httpClient.get(testUrl).subscribe({ + next: data => fail('should have failed with the network error'), + error: (error: HttpErrorResponse) => { expect(error.error).toBe(errorEvent); done(); - } + }} ); const req = httpTestingController.expectOne(testUrl); diff --git a/aio/content/examples/testing/src/app/shared/highlight.directive.spec.ts b/aio/content/examples/testing/src/app/shared/highlight.directive.spec.ts index 07b68763d2743..1cb8bd2bee71b 100644 --- a/aio/content/examples/testing/src/app/shared/highlight.directive.spec.ts +++ b/aio/content/examples/testing/src/app/shared/highlight.directive.spec.ts @@ -56,7 +56,9 @@ describe('HighlightDirective', () => { it('should bind background to value color', () => { // easier to work with nativeElement const input = des[2].nativeElement as HTMLInputElement; - expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor'); + expect(input.style.backgroundColor) + .withContext('initial backgroundColor') + .toBe('cyan'); input.value = 'green'; @@ -66,7 +68,9 @@ describe('HighlightDirective', () => { input.dispatchEvent(new Event('input')); fixture.detectChanges(); - expect(input.style.backgroundColor).toBe('green', 'changed backgroundColor'); + expect(input.style.backgroundColor) + .withContext('changed backgroundColor') + .toBe('green'); }); diff --git a/aio/content/examples/testing/src/app/twain/twain.component.marbles.spec.ts b/aio/content/examples/testing/src/app/twain/twain.component.marbles.spec.ts index 1b2ec7e52a040..f1b709d2e6f1f 100644 --- a/aio/content/examples/testing/src/app/twain/twain.component.marbles.spec.ts +++ b/aio/content/examples/testing/src/app/twain/twain.component.marbles.spec.ts @@ -1,5 +1,5 @@ // #docplaster -import { async, fakeAsync, ComponentFixture, TestBed, tick } from '@angular/core/testing'; +import { fakeAsync, ComponentFixture, TestBed, tick } from '@angular/core/testing'; // #docregion import-marbles import { cold, getTestScheduler } from 'jasmine-marbles'; @@ -51,7 +51,9 @@ describe('TwainComponent (marbles)', () => { getQuoteSpy.and.returnValue( q$ ); fixture.detectChanges(); // ngOnInit() - expect(quoteEl.textContent).toBe('...', 'should show placeholder'); + expect(quoteEl.textContent) + .withContext('should show placeholder') + .toBe('...'); // #docregion test-scheduler-flush getTestScheduler().flush(); // flush the observables @@ -59,8 +61,12 @@ describe('TwainComponent (marbles)', () => { fixture.detectChanges(); // update view - expect(quoteEl.textContent).toBe(testQuote, 'should show quote'); - expect(errorMessage()).toBeNull('should not show error'); + expect(quoteEl.textContent) + .withContext('should show quote') + .toBe(testQuote); + expect(errorMessage()) + .withContext('should not show error') + .toBeNull(); }); // #enddocregion get-quote-test @@ -74,14 +80,20 @@ describe('TwainComponent (marbles)', () => { getQuoteSpy.and.returnValue( q$ ); fixture.detectChanges(); // ngOnInit() - expect(quoteEl.textContent).toBe('...', 'should show placeholder'); + expect(quoteEl.textContent) + .withContext('should show placeholder') + .toBe('...'); getTestScheduler().flush(); // flush the observables tick(); // component shows error after a setTimeout() fixture.detectChanges(); // update error message - expect(errorMessage()).toMatch(/test failure/, 'should display error'); - expect(quoteEl.textContent).toBe('...', 'should show placeholder'); + expect(errorMessage()) + .withContext('should display error') + .toMatch(/test failure/); + expect(quoteEl.textContent) + .withContext('should show placeholder') + .toBe('...'); })); // #enddocregion error-test }); diff --git a/aio/content/examples/testing/src/app/twain/twain.component.spec.ts b/aio/content/examples/testing/src/app/twain/twain.component.spec.ts index ed4a425822ba8..e59d2302bc251 100644 --- a/aio/content/examples/testing/src/app/twain/twain.component.spec.ts +++ b/aio/content/examples/testing/src/app/twain/twain.component.spec.ts @@ -47,9 +47,15 @@ describe('TwainComponent', () => { describe('when test with synchronous observable', () => { it('should not show quote before OnInit', () => { - expect(quoteEl.textContent).toBe('', 'nothing displayed'); - expect(errorMessage()).toBeNull('should not show error element'); - expect(getQuoteSpy.calls.any()).toBe(false, 'getQuote not yet called'); + expect(quoteEl.textContent) + .withContext('nothing displayed') + .toBe(''); + expect(errorMessage()) + .withContext('should not show error element') + .toBeNull(); + expect(getQuoteSpy.calls.any()) + .withContext('getQuote not yet called') + .toBe(false); }); // The quote would not be immediately available if the service were truly async. @@ -59,7 +65,9 @@ describe('TwainComponent', () => { // sync spy result shows testQuote immediately after init expect(quoteEl.textContent).toBe(testQuote); - expect(getQuoteSpy.calls.any()).toBe(true, 'getQuote called'); + expect(getQuoteSpy.calls.any()) + .withContext('getQuote called') + .toBe(true); }); // #enddocregion sync-test @@ -69,8 +77,7 @@ describe('TwainComponent', () => { // #docregion error-test it('should display error when TwainService fails', fakeAsync(() => { // tell spy to return an error observable - getQuoteSpy.and.returnValue(throwError('TwainService test failure')); - + getQuoteSpy.and.returnValue(throwError(() => new Error('TwainService test failure'))); fixture.detectChanges(); // onInit() // sync spy errors immediately after init @@ -78,8 +85,12 @@ describe('TwainComponent', () => { fixture.detectChanges(); // update errorMessage within setTimeout() - expect(errorMessage()).toMatch(/test failure/, 'should display error'); - expect(quoteEl.textContent).toBe('...', 'should show placeholder'); + expect(errorMessage()) + .withContext('should display error') + .toMatch(/test failure/, ); + expect(quoteEl.textContent) + .withContext('should show placeholder') + .toBe('...'); })); // #enddocregion error-test }); @@ -93,42 +104,64 @@ describe('TwainComponent', () => { }); it('should not show quote before OnInit', () => { - expect(quoteEl.textContent).toBe('', 'nothing displayed'); - expect(errorMessage()).toBeNull('should not show error element'); - expect(getQuoteSpy.calls.any()).toBe(false, 'getQuote not yet called'); + expect(quoteEl.textContent) + .withContext('nothing displayed') + .toBe(''); + expect(errorMessage()) + .withContext('should not show error element') + .toBeNull(); + expect(getQuoteSpy.calls.any()) + .withContext('getQuote not yet called') + .toBe(false); }); it('should still not show quote after component initialized', () => { fixture.detectChanges(); // getQuote service is async => still has not returned with quote // so should show the start value, '...' - expect(quoteEl.textContent).toBe('...', 'should show placeholder'); - expect(errorMessage()).toBeNull('should not show error'); - expect(getQuoteSpy.calls.any()).toBe(true, 'getQuote called'); + expect(quoteEl.textContent) + .withContext('should show placeholder') + .toBe('...'); + expect(errorMessage()) + .withContext('should not show error') + .toBeNull(); + expect(getQuoteSpy.calls.any()) + .withContext('getQuote called') + .toBe(true); }); // #docregion fake-async-test it('should show quote after getQuote (fakeAsync)', fakeAsync(() => { fixture.detectChanges(); // ngOnInit() - expect(quoteEl.textContent).toBe('...', 'should show placeholder'); + expect(quoteEl.textContent) + .withContext('should show placeholder') + .toBe('...'); tick(); // flush the observable to get the quote fixture.detectChanges(); // update view - expect(quoteEl.textContent).toBe(testQuote, 'should show quote'); - expect(errorMessage()).toBeNull('should not show error'); + expect(quoteEl.textContent) + .withContext('should show quote') + .toBe(testQuote); + expect(errorMessage()) + .withContext('should not show error') + .toBeNull(); })); // #enddocregion fake-async-test // #docregion waitForAsync-test it('should show quote after getQuote (waitForAsync)', waitForAsync(() => { fixture.detectChanges(); // ngOnInit() - expect(quoteEl.textContent).toBe('...', 'should show placeholder'); + expect(quoteEl.textContent) + .withContext('should show placeholder') + .toBe('...'); fixture.whenStable().then(() => { // wait for async getQuote fixture.detectChanges(); // update view with quote expect(quoteEl.textContent).toBe(testQuote); - expect(errorMessage()).toBeNull('should not show error'); + expect(errorMessage()) + .withContext('should not show error') + .toBeNull(); }); })); // #enddocregion waitForAsync-test @@ -141,7 +174,9 @@ describe('TwainComponent', () => { component.quote.pipe(last()).subscribe(() => { fixture.detectChanges(); // update view with quote expect(quoteEl.textContent).toBe(testQuote); - expect(errorMessage()).toBeNull('should not show error'); + expect(errorMessage()) + .withContext('should not show error') + .toBeNull(); done(); }); }); @@ -155,7 +190,9 @@ describe('TwainComponent', () => { getQuoteSpy.calls.mostRecent().returnValue.subscribe(() => { fixture.detectChanges(); // update view with quote expect(quoteEl.textContent).toBe(testQuote); - expect(errorMessage()).toBeNull('should not show error'); + expect(errorMessage()) + .withContext('should not show error') + .toBeNull(); done(); }); }); @@ -169,8 +206,12 @@ describe('TwainComponent', () => { tick(); // component shows error after a setTimeout() fixture.detectChanges(); // update error message - expect(errorMessage()).toMatch(/test failure/, 'should display error'); - expect(quoteEl.textContent).toBe('...', 'should show placeholder'); + expect(errorMessage()) + .withContext('should display error') + .toMatch(/test failure/); + expect(quoteEl.textContent) + .withContext('should show placeholder') + .toBe('...'); })); }); }); diff --git a/aio/content/examples/testing/src/app/welcome/welcome.component.spec.ts b/aio/content/examples/testing/src/app/welcome/welcome.component.spec.ts index 96b34a403fb91..edfbc2c5700a0 100644 --- a/aio/content/examples/testing/src/app/welcome/welcome.component.spec.ts +++ b/aio/content/examples/testing/src/app/welcome/welcome.component.spec.ts @@ -105,8 +105,12 @@ describe('WelcomeComponent', () => { it('should welcome the user', () => { fixture.detectChanges(); const content = el.textContent; - expect(content).toContain('Welcome', '"Welcome ..."'); - expect(content).toContain('Test User', 'expected name'); + expect(content) + .withContext('"Welcome ..."') + .toContain('Welcome'); + expect(content) + .withContext('expected name') + .toContain('Test User'); }); it('should welcome "Bubba"', () => { @@ -119,8 +123,12 @@ describe('WelcomeComponent', () => { userService.isLoggedIn = false; // welcome message hasn't been shown yet fixture.detectChanges(); const content = el.textContent; - expect(content).not.toContain('Welcome', 'not welcomed'); - expect(content).toMatch(/log in/i, '"log in"'); + expect(content) + .withContext('not welcomed') + .not.toContain('Welcome'); + expect(content) + .withContext('"log in"') + .toMatch(/log in/i); }); // #enddocregion tests