import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import {RouterTestingModule} from '@angular/router/testing';
import { CarsComponent } from './cars.component';
import { UrlService } from 'src/app/services/url.service';
import { of } from 'rxjs';
import { Router, RouterModule } from '@angular/router';
import { RouterLinkDirectiveStub, click } from 'src/testing';
import { DebugElement } from '@angular/core';
import { AppModule } from 'src/app/app.module';
import { By } from '@angular/platform-browser';
import { IAutomobile } from 'src/app/models/automobile';
const testData: IAutomobile[] = [
{
'id': 'C1',
'name': 'Sweptail Rolls Royce',
'price': 13,
'image': 'https://i.imgur.com/LldGUJL.jpg ',
'desc': 'The Rolls-Royce Sweptail is hand-built, and inspired by coachbuilding of the 1920s and 1930s. The Sweptail was commissioned bespoke in 2013 as a one-off automobile, at the request of a super-yacht and aircraft specialist who had a unique idea in mind. Giles Taylor, director of design at Rolls-Royce Motor Cars described the Sweptail as "the automotive equivalent of Haute couture"',
'tags': '6.75 L V12 | 453 Hp | 150 MPH'
},
{
'id': 'C2',
'name': 'Koenigsegg CCXR Trevita',
'price': 4.8,
'image': 'https://i.imgur.com/nrK7N62.jpg',
'desc': 'The Koenigsegg CCX is a mid-engine sports car manufactured by Swedish automotive manufacturer Koenigsegg Automotive AB. The project began with the aim of making a global car, designed and engineered to comply with global safety and environment regulations, particularly to enter the United States car market.',
'tags': '4.7L V8 | 1004 HP | 254 MPH'
},
];
let component: CarsComponent;
let fixture: ComponentFixture<CarsComponent>;
let urlService: jasmine.SpyObj<UrlService>;
let getCarsSpy: jasmine.Spy;
let router: Router;
let routerLinks: RouterLinkDirectiveStub[];
let routerLinksDE: DebugElement[];
describe('CarsComponent', () => {
beforeEach(async(() => {
urlService = jasmine.createSpyObj('UrlService', ['getCars']);
TestBed.configureTestingModule({
imports: [ AppModule ]
})
// Get rid of app's Router configuration otherwise many failures.
// Doing so removes Router declarations; add the Router stubs
.overrideModule(AppModule, {
remove: {imports: [RouterModule]},
add: {
imports: [RouterTestingModule.withRoutes([{path: 'Cars/C1', component: CarsComponent}])],
declarations: [ RouterLinkDirectiveStub ],
providers: [{ provide: UrlService, useValue: urlService }]
}
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(CarsComponent);
component = fixture.componentInstance;
router = TestBed.get(Router);
urlService = TestBed.get(UrlService);
getCarsSpy = urlService.getCars.and.returnValue(of(testData));
fixture.detectChanges(); // trigger initial changes // must to detectChanges after creating component instance;
});
}));
beforeEach(() => {
routerLinksDE = fixture.debugElement.queryAll(By.directive(RouterLinkDirectiveStub));
routerLinks = routerLinksDE.map(de => de.injector.get(RouterLinkDirectiveStub));
});
it('getCars api call should be called', () => {
expect(getCarsSpy.calls.any()).toBe(true, 'should call getAllBooks function');
});
it('should set service data to cars array', () => {
expect(component.cars).toEqual(testData, 'should set cars value with service data');
});
it('should hide loader when data is successfully fetched from service', () => {
expect(component.loader).toBe(false, 'loader should be disabled');
});
it('should show car names in template', () => {
const carNames: HTMLCollectionOf<Element> = document.getElementsByClassName('item');
expect(carNames.length).toBe(testData.length, 'should display all the book names in template, as received by service');
});
// ROUTER LINKS TEST
it('all the car names should have routerLinks in template', async(() => {
expect(routerLinks.length).toBe(testData.length, 'each car should have routerLink');
}));
it('should have approprate routerLinks assigned in template', async(() => {
expect(routerLinks[0].linkParams).toContain(`${testData[0].id}`, 'the first routerLink sould have 1st carId');
expect(routerLinks[1].linkParams).toContain(`${testData[1].id}`, 'the second routerLink sould have 2nd carId');
}));
// this test triggers navigation, which affects DetailsComponent tests
it('should navigate to /car/id when clicked on that car name', () => {
const carLinkDe = routerLinksDE[0]; // taking first routerLink for test
const carLink = routerLinks[0];
fixture.ngZone.run(() => {
click(carLinkDe); // trigger click event on the state name
fixture.detectChanges(); // update app to detect changes
expect(carLink.navigatedTo).toContain(testData[0].id);
});
});
});