import { Location } from '@angular/common' ; import { ActivatedRoute , ParamMap } from '@angular/router' ; import { asapScheduler , BehaviorSubject , of as observableOf , Subject , } from 'rxjs' ; import { subscriptionCount } from 'rxjs-subscription-count' ; import { observeOn , take } from 'rxjs/operators' ; import { femaleMarvelHeroes } from '../../test/female-marvel-heroes' ; import { Hero } from '../hero' ; import { HeroService } from '../hero.service' ; import { HeroDetailContainerComponent } from './hero-detail.container' ; describe ( HeroDetailContainerComponent . name , ( ) => { const blackWidow = femaleMarvelHeroes . find ( x => x . name === 'Black Widow' ) ; let container ; const heroServiceStub = createHeroServiceStub ( ) ; const locationStub = createLocationStub ( ) ; let routeParameters ; let routeParametersSubscriptionCount ; let activatedRouteFake ; function createHeroServiceStub ( ) { const stub = jasmine . createSpyObj ( HeroService . name , [ 'getHero' , 'updateHero' , ] ) ; resetHeroServiceStub ( stub ) ; return stub ; } function createLocationStub ( ) { const stub = jasmine . createSpyObj ( Location . name , [ 'back' , ] ) ; resetLocationStub ( stub ) ; return stub ; } function emitRouteParameters ( parameters ) { routeParameters . next ( { get keys ( ) : string [ ] { return Object . keys ( parameters ) ; } , get ( name ) { return parameters [ name ] || null ; } , getAll ( name ) { return this . has ( name ) ? [ parameters [ name ] ] : [ ] ; } , has ( name ) { return Object . keys ( parameters ) . includes ( name ) ; } , } ) ; } function isHero ( x ) { return x != null && typeof x === 'object' && ! Array . isArray ( x ) && typeof x . id === 'number' && typeof x . name === 'string' ; } function resetHeroServiceStub ( stub ) { stub . getHero . and . returnValue ( observableOf ( blackWidow , asapScheduler ) ) . calls . reset ( ) ; stub . updateHero . and . callFake ( ( hero ) => observableOf ( hero , asapScheduler ) ) . calls . reset ( ) ; } function resetLocationStub ( stub ) { stub . back . calls . reset ( ) ; } beforeEach ( ( ) => { routeParameters = new Subject ( ) ; routeParametersSubscriptionCount = new BehaviorSubject ( 0 ) ; activatedRouteFake = { paramMap : routeParameters . asObservable ( ) . pipe ( subscriptionCount ( routeParametersSubscriptionCount ) , observeOn ( asapScheduler ) ) , } as Partial < ActivatedRoute > as any ; container = new HeroDetailContainerComponent ( activatedRouteFake , heroServiceStub as unknown as HeroService , locationStub ) ; } ) ; afterEach ( ( ) => { routeParameters . complete ( ) ; routeParametersSubscriptionCount . complete ( ) ; resetHeroServiceStub ( heroServiceStub ) ; resetLocationStub ( locationStub ) ; } ) ; it ( 'navigates to the previous page' , ( ) => { container . goBack ( ) ; expect ( locationStub . back ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'saves a hero' , ( ) => { container . save ( blackWidow ) ; expect ( heroServiceStub . updateHero ) . toHaveBeenCalledTimes ( 1 ) ; expect ( heroServiceStub . updateHero ) . toHaveBeenCalledWith ( blackWidow ) ; } ) ; describe ( 'hero observable' , ( ) => { it ( 'emits a hero when "id" route parameter changes' , async ( ) => { const emitHero = container . hero$ . pipe ( take ( 1 ) ) . toPromise ( ) ; emitRouteParameters ( { id : '1' } ) ; const hero = await emitHero ; expect ( isHero ( hero ) ) . toBe ( true , 'it must be a hero' ) ; } ) ; it ( 'subscribes to route parameter changes on hero subscription' , ( ) => { expect ( routeParametersSubscriptionCount . value ) . toBe ( 0 ) ; container . hero$ . subscribe ( ) ; expect ( routeParametersSubscriptionCount . value ) . toBe ( 1 ) ; emitRouteParameters ( { id : '1' } ) ; expect ( routeParametersSubscriptionCount . value ) . toBe ( 1 ) ; } ) ; } ) ; } ) ;