import {async, ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing'; import {ProductComponent} from './product.component'; import {Component, ViewChild} from "@angular/core"; import {Product, ProductPreRequisites, ProductVersion} from "../../model/product"; import {ProductsService} from "../../services/products.service"; import {JobsService} from "../../services/jobs.service"; import {ReactiveFormsModule} from "@angular/forms"; import {of} from "rxjs"; import {By} from "@angular/platform-browser"; import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; // mock a host container @Component({ template: '<app-product [product]="product" [type]="type" [queues]="queues"></app-product>' }) class HostComponent { product: Product = { category: "CALIBRATION", completed: false, configurations: [], epoch: 1, extraInfoForJobName: "", fileNames: ["PPR.xml"], id: 18469, latestProductVersion: {present: true}, minitiles: [], name: "ProductName", notes: null, preRequisites: [{ archiveIds: {}, id: 345, name: 'preReq', ready: true, requiredProduct: { category: "CALIBRATION", completed: false, configurations: [], epoch: 1, extraInfoForJobName: "", fileNames: ["PPR.xml"], id: 987654, latestProductVersion: {present: true}, minitiles: [], name: "ProductName2", notes: null, preRequisites: [], status: "READY", tags: [], typeId: 1, typeName: "calibration", phaseCenterRA: '1.23456789', phaseCenterDec: '1.23456789', imageSizeX: '1.23456789', imageSizeY: '1.23456789', fluxCalibrators: [], fluxPosition: '', lstEnd: null, lstStart: null, optId: 321, phaseCalibrators: [], polCalibrators: [], submitted: '', versions: [{ archiveIds: {}, configurations: [], id: 456, jobSpecifications: {}, notes: '', productEpoch: 1, productId: 852, productName: 'ProductName3', productType: 1, status: 'READY', versionNumber: 1 } as ProductVersion, { archiveIds: {}, configurations: [], id: 753, jobSpecifications: {}, notes: '', productEpoch: 1, productId: 951, productName: 'ProductName4', productType: 1, status: 'READY', versionNumber: 2 } as ProductVersion] } as Product } as ProductPreRequisites ], status: "COMPLETED", tags: [], typeId: 1, typeName: "calibration", phaseCenterRA: '1.23456789', phaseCenterDec: '1.23456789', imageSizeX: '1.23456789', imageSizeY: '1.23456789', fluxCalibrators: [], fluxPosition: '', lstEnd: null, lstStart: null, optId: 123, phaseCalibrators: [], polCalibrators: [], submitted: '', versions: [] }; type: number = 1; queues: Array<any> = ["calibration", "compression"]; @ViewChild(ProductComponent, /* TODO: add static flag */ {}) productComponent : ProductComponent; } describe('ProductComponent', () => { let component: HostComponent; let fixture: ComponentFixture<HostComponent>; let productServiceSpy: { getProductTypeConfig: jasmine.Spy, getProductConfig: jasmine.Spy, updateProductStatus: jasmine.Spy }; let jobServiceSpy: { createJob: jasmine.Spy }; beforeEach(async(() => { productServiceSpy = jasmine.createSpyObj('ProductService', ['getProductTypeConfig', 'getProductConfig', 'updateProductStatus']); jobServiceSpy = jasmine.createSpyObj('JobService', ['createJob']); TestBed.configureTestingModule({ declarations: [HostComponent, ProductComponent], providers: [ {provide: ProductsService, useValue: productServiceSpy}, {provide: JobsService, useValue: jobServiceSpy} ], imports: [ReactiveFormsModule, BrowserAnimationsModule] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(HostComponent); component = fixture.componentInstance; productServiceSpy = TestBed.get(ProductsService); jobServiceSpy = TestBed.get(JobsService); }); it('should create', () => { expect(component).toBeTruthy(); }); it('getName() should return the product name', () => { fixture.detectChanges(); //ngOnInit expect(component.productComponent.getName()).toEqual('ProductName', 'returns product.name'); component.productComponent.product.typeName = 'schedblock'; expect(component.productComponent.getName()).toEqual('ProductName (123)', 'returns product.name + product.optId'); }); it('getStatusBadgeClass() should return the correct class by product.status', () => { fixture.detectChanges(); //ngOnInit expect(component.productComponent.getStatusBadgeClass()).toEqual('badge-success', 'badge-success returned for COMPLETED status'); component.productComponent.product.status = 'READY'; expect(component.productComponent.getStatusBadgeClass()).toEqual('badge-info', 'badge-info returned for READY status'); component.productComponent.product.status = 'PROCESSING'; expect(component.productComponent.getStatusBadgeClass()).toEqual('badge-light', 'badge-light returned for other statuses'); }); it('showImageDetails() should return true for image types', () => { fixture.detectChanges(); //ngOnInit expect(component.productComponent.showImageDetails()).toBeFalsy('returns false for non-image type'); component.productComponent.product.typeName = 'quicklook'; expect(component.productComponent.showImageDetails()).toBeTruthy('returns true for image type'); }); it('getPhaseCenterRA() should return a string version of the number with a set precision', () => { fixture.detectChanges(); //ngOnInit expect(component.productComponent.getPhaseCenterRA()).toEqual('1.2346'); }); it('getPhaseCenterDec() should return a string version of the number with a set precision', () => { fixture.detectChanges(); //ngOnInit expect(component.productComponent.getPhaseCenterDec()).toEqual('1.2346'); }); it('getImageSizeX() should return a string version of the number with a set precision', () => { fixture.detectChanges(); //ngOnInit expect(component.productComponent.getImageSizeX()).toEqual('1.2346'); }); it('getImageSizeY() should return a string version of the number with a set precision', () => { fixture.detectChanges(); //ngOnInit expect(component.productComponent.getImageSizeY()).toEqual('1.2346'); }); it('showDetails() should call the productService to get details and toggle the detailsExposed value', fakeAsync(() => { fixture.detectChanges(); //ngOnInit expect(component.productComponent.detailsExposed).toBeFalsy('detailsExposed starts false'); component.productComponent.showDetails(); expect(productServiceSpy.getProductTypeConfig).toHaveBeenCalledTimes(1); expect(productServiceSpy.getProductConfig).toHaveBeenCalledTimes(2); expect(component.productComponent.detailsExposed).toBeTruthy('detailsExposed set to true'); })); it('hideDetails() should toggle the detailsExposed value', () => { component.productComponent.detailsExposed = true; fixture.detectChanges(); //ngOnInit component.productComponent.hideDetails(); expect(component.productComponent.detailsExposed).toBeFalsy('detailsExposed set to false'); }); it('updateStatus() should call product service and update the product status', fakeAsync(() => { let newStatus = 'PROCESSING'; productServiceSpy.updateProductStatus.and.returnValue(of(newStatus)); fixture.detectChanges(); //ngOnInit expect(component.productComponent.formGroup.get('status').value).toEqual(component.productComponent.product.status, 'form control holds products status'); component.productComponent.updateStatus(); expect(productServiceSpy.updateProductStatus).toHaveBeenCalledTimes(0); component.productComponent.formGroup.get('status').setValue(newStatus); component.productComponent.updateStatus(); expect(productServiceSpy.updateProductStatus).toHaveBeenCalledTimes(1); expect(component.productComponent.formGroup.get('status').value).toEqual(component.productComponent.product.status, 'form control holds products status'); })); it('createJob() should call the job service with the right parameters', fakeAsync(() => { jobServiceSpy.createJob.and.returnValue(of('Response')); fixture.detectChanges(); // ngOnInit expect(component.productComponent.jobFormGroup.get('queue').value).toEqual(component.productComponent.queues[0], 'jobForm queue starts with first queue value'); component.productComponent.createJob(); expect(jobServiceSpy.createJob).toHaveBeenCalledWith(component.productComponent.product.id, component.productComponent.queues[0], 1); })); it('the more button should call showDetails()', fakeAsync(() => { let showDetailSpy = spyOn(component.productComponent, 'showDetails'); fixture.detectChanges(); // ngOnInit let moreButton: HTMLButtonElement = fixture.debugElement.query(By.css('.more_button')).nativeElement; moreButton.click(); fixture.detectChanges(); expect(showDetailSpy).toHaveBeenCalledTimes(1); })); it('the less button should call hideDetails()', fakeAsync(() => { component.productComponent.detailsExposed = true; let hideDetailSpy = spyOn(component.productComponent, 'hideDetails'); fixture.detectChanges(); // ngOnInit let lessButton: HTMLButtonElement = fixture.debugElement.query(By.css('.less_button')).nativeElement; lessButton.click(); fixture.detectChanges(); expect(hideDetailSpy).toHaveBeenCalled(); })); it('the change status form should call updateStatus()', () => { component.productComponent.detailsExposed = true; let updateStatusSpy = spyOn(component.productComponent, 'updateStatus'); fixture.detectChanges(); //ngOnInit let submitButton: HTMLButtonElement = fixture.debugElement.query(By.css('.status_change_btn')).nativeElement; submitButton.click(); expect(updateStatusSpy).toHaveBeenCalled(); }); it('the create job button should call createJob()', () => { component.productComponent.detailsExposed = true; component.productComponent.showProductForm = true; component.product.status = 'READY'; let createJobSpy = spyOn(component.productComponent, 'createJob'); fixture.detectChanges(); //ngOnInit let createJobButton: HTMLButtonElement = fixture.debugElement.query(By.css('.create_job_btn')).nativeElement; createJobButton.click(); expect(createJobSpy).toHaveBeenCalled(); }); });