Home > Net >  fixture.detectchanges() method resulting in test case to fail
fixture.detectchanges() method resulting in test case to fail

Time:11-08

I am unit testing my component using Karma and jasmine. fixture.detechChanges() should be use for every test in order to detect the changes. But this method is making the test case to fail and giving error

Error: InvalidPipeArgument: 'Unable to convert "Invalid Date" into a date' for pipe 'DatePipe'

Since I am new to this can anyone let me know why this is behaving like this and what is the correct place to use this.

Out the three test cases 2nd test case is failing. I have done lot of research but i am not sure why my test case is failing.

below is my code

resource-overview.component.ts

import { ResourceOverviewService } from '../../../../services/detail/resource-overview/resource-overview.service'
import {
  Component,
  OnInit,
} from '@angular/core';
import { UtilsService } from '../../../../services/common/utils.service';
import { Router, ActivatedRoute } from '@angular/router';
@Component({
  selector: 'app-resource-overview',
  templateUrl: './resource-overview.component.html',
  styleUrls: ['./resource-overview.component.scss']
})
export class ResourceOverviewComponent implements OnInit {
  uuid = '--';
  resourceName = '--';
  status = '--';
  provider = '--';
  providerAccount = '--';
  resourceCategory = '--';
  resourceType = '--';
  region = '--';
  overviewTags = '--';
  correlationID = '--';
  showMore = true;
  crn = '--'
  lastupdated : any;
  providerAccountName = '--'
  resourceId = '--'
  creationDate = '--'
  description = '--'
  tags = '--'
  dateTimeFormat = 'MMM d, y h:mm a';
  lastUpdateTime: any;
  dateTimeZone: any;
  keyValue : { key: any, value: any }[] = [];
  id = '--';
  providerLastUpdated = '--';
  providerStatus = '--';
  breadcrumbs = [];
  launchpadURL = `/launchpad`;
  
  constructor(
    private readonly resourceOverviewService: ResourceOverviewService,
    private readonly utilsService: UtilsService,
    private readonly router: Router,
    readonly route: ActivatedRoute,
  ) {}

  ngOnInit() {
    this.id = this.route.snapshot.queryParamMap.get('id');
    this.getDetails();
    // this.route.queryParams.subscribe(params => {
    //   console.log("---", params)
    //   this.id = params.id
    //   this.getDetails();
    // })
  }

  viewInventory(){
    this.router.navigate(['..'], {
      relativeTo: this.route
    });
  }

  getDetails() {
    if (this.id) {
      this.getResourceOverview();
    }
  }

  getResourceOverview(): void {
    const resourceID = `search_keys=${"resource_id"}&search=${this.id}`
    this.resourceId = this.id
    this.resourceOverviewService
      .getResourceOverview(resourceID)
      .subscribe(
        (response) => {
          const result = response as any;
          if (result && result.raw_items[0]) {
            this.uuid = result.raw_items[0].uuid  || '--';
            this.resourceName = result.raw_items[0].gpd.provider_resource_name || '--';
            this.status = result.raw_items[0].gpd.status || '--'; 
            this.provider = result.raw_items[0].gpd.provider_code || '--';
            this.providerAccountName = result.raw_items[0].gpd.provider_account_name || '--';
            this.providerAccount = result.raw_items[0].gpd.provider_account || '--';
            this.resourceCategory = result.raw_items[0].gpd.service_category || '--';
            this.resourceType = result.raw_items[0].gpd.provider_resource_type || '--';
            this.region = result.raw_items[0].gpd.provider_resource_location || '--';
            this.keyValue = result.raw_items[0].gpd.tags;
            this.crn = result.raw_items[0].gpd.provider_resource_crn || '--';
            this.lastupdated = new Date(Date.parse(result.raw_items[0].gpd.discovery_lastupdated)) || '--' ;
            this.providerLastUpdated = result.raw_items[0].gpd.provider_resource_lastupdated || '--' ;
            this.dateTimeZone = this.utilsService.getLocalBrowserTime(this.lastupdated) || '--' ;
            this.dateTimeFormat = this.utilsService.getDateTimeFormat() || '--' ;
            this.creationDate = result.raw_items[0].gpd.provider_resource_created || '--' ;
            this.correlationID = result.raw_items[0].gpd.correlation_id || '--' ;
            this.providerStatus = result.raw_items[0].gpd.provider_status || '--' ;
          }
        },
        (error) => {
          console.log('Resource overview details failed with error', error);
        }
      );
  }
}

resource-overview.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpWrapperService } from '../../../../services/common/http-wrapper.service';
import { ResourceOverviewComponent } from './resource-overview.component';
import { NGXLogger } from 'ngx-logger';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { CarbonComponentsModule } from '@cloudMatrix-CAM/cb-carbon-components-lib';
import { CarbonModule } from '../../../../../carbon.module';
import { ApiContractService, CbTranslationUiLibModule, TranslationService,AuthenticationContractService } from '@cloudMatrix-CAM/cb-common-ui';
import { MockNGXLogger } from '../../../../components/discovery-inventory/list-view/list-view.component.spec';
import { convertToParamMap, Router } from '@angular/router';
import { ApiService } from '../../../../services/common/api.service';
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
import { CbCommonDiscoveryManagementMicroUiService } from '../../../../services/cb-common-discovery-management-micro-ui.service';
import { ActivatedRoute } from "@angular/router";
import { throwError, of, Observable  } from 'rxjs';
import { RouterModule } from '@angular/router'
import { ResourceOverviewService } from 'projects/cb-common-discovery-management-micro-ui/src/app/services/detail/resource-overview/resource-overview.service';

class mockHttpWrapperService {
  readonly isApiReady = false;
}

describe('ResourceOverviewComponent', () => {
  let component: ResourceOverviewComponent;
  let fixture: ComponentFixture<ResourceOverviewComponent>;
  let resourceOverviewService: any;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ResourceOverviewComponent ],
      imports: [
        CarbonComponentsModule,CarbonModule,CbTranslationUiLibModule.forRoot(),
        RouterModule.forRoot([]),
      ],
      providers: [
        { provide: NGXLogger, useClass: MockNGXLogger },
        { provide: HttpWrapperService, useClass: mockHttpWrapperService },
        { provide: Router, useClass: class { navigate = jasmine.createSpy("navigate"); }},
        ApiService,
        ApiContractService,
        ResourceOverviewService,
        TranslationService,
        CbCommonDiscoveryManagementMicroUiService,
        {
          provide: ActivatedRoute,
          useValue: {
            params: of({ id: 'aeb24ca0549c', code: 'IBM' }),
            snapshot: {
              queryParamMap: convertToParamMap({
                id: 'aeb24ca0549c',
                code: 'IBM',
              })
            }
          }
        },
      ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    })
    .compileComponents()
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(ResourceOverviewComponent);
    component = fixture.debugElement.componentInstance;
    // fixture.detectChanges();
  });

  it('should create component ', () => {
    expect(component).toBeTruthy();
  });

  it('should init object for action tabs', async () => {
    await fixture.whenStable();
    component.ngOnInit();
    fixture.detectChanges();
    expect(component).toBeTruthy();
  });

  it('should get resource data ', () => {
    resourceOverviewService = TestBed.inject(ResourceOverviewService);
    const data = {"raw_items":[{"uuid":"123456789:i-0105345f167219fbf:vms","gpd":{"version":"0.1","discovery_lastupdated":"2021-09-08T05:36:08Z","correlation_id":"aws-123456789-i-0105345f167219fbf","core_credential_id":["credid"],"provider_code":"aws","provider_account":"123456789","provider_account_name":"accountname","provider_account_details":{"cb84865d-3477-40f3-9131-4462273cfedb":"accountname"},"provider_resource_type":"vms","provider_resource_id":"i-0105345f167219fbf","provider_resource_name":"cd-642","provider_resource_lastupdated":"","provider_resource_created":"2021-08-30T06:12:49Z","provider_resource_crn":"arn:aws:ec2:us-east-1:123456789:vms/i-0105345f167219fbf","provider_resource_location":"us-east-1","provider_status":"running","location_geo_coordinates":{"latitude":38.86726,"longitude":-77.233959},"status":"Active","service_category":"compute","tags":[{"Key":"Name","Value":"cd-642"}],"raw_config_data":null,"extended_data":null}}],"meta":null,"pagination":{"count":1,"total":1,"offset":0,"limit":10,"first":"https://local-core.gravitant.net/discovery/api/inventory/v1/service?limit=10","last":"https://local-core.gravitant.net/discovery/api/inventory/v1/service?limit=10","next":"","prev":""}};
    spyOn(resourceOverviewService, "getResourceOverview").and.callFake(() => {
      return of(data)
    })
    component.getDetails();
    fixture.detectChanges();
    expect(component).toBeTruthy();
  });
});

resource-overview.component.html

<div >
  <div  >
  <ibm-breadcrumb>
    <ibm-breadcrumb-item href="{{launchpadURL}}">
            Portal
        </ibm-breadcrumb-item>
        <ibm-breadcrumb-item >
    <a (click)="viewInventory()">Inventory Resource view</a>
  </ibm-breadcrumb-item>
        <ibm-breadcrumb-item >
            {{ id }}
        </ibm-breadcrumb-item>
    </ibm-breadcrumb>
  <div  >
    <label >{{ id }}</label>
  </div>
  <div>
    <label >{{ 'discovery_inventory.resource_overview.discovery_lastupdated' | translate }}
        {{lastupdated  | date : dateTimeFormat}} {{dateTimeZone}}</label>
  </div>
</div>
  <div  >
    <div  >
      <label >{{"discovery_inventory.resource_overview.overview" | translate}}</label>
    </div>
    <div >
      <div >
        <div >
          <div >
            <label >{{"discovery_inventory.resource_overview.id" | translate}}</label>
            <span
              
              title="{{ id }}"
              >{{ id }}</span
            >
          </div>
        </div>
        <div >
          <div >
            <label >{{"discovery_inventory.resource_overview.resource_type" | translate}}</label>
            <span
              
              title="{{ resourceType }}"
              >{{ resourceType }}</span
            >
          </div>
        </div>
        </div>
        <div >
        <div >
          <div >
            <label >{{
              "discovery_inventory.resource_overview.region" | translate
            }}</label>
            <span
              
              title="{{ region }}"
              >{{ region }}</span
            >
          </div>
        </div>
        <div >
          <div >
            <label >{{
              "discovery_inventory.resource_overview.tag_key_value" | translate
            }}</label>
            <div *ngFor="let k of keyValue; let i = index;">
            <span *ngIf="i!=(keyValue.length-1)"
            
            title="{{ id }}"
            >{{ k.Key }} : {{ k.Value }} ,
            </span>
            <span *ngIf="i==(keyValue.length-1)"
            
            title="{{ id }}"
            >{{ k.Key }}:{{ k.Value }}
            </span>
          </div>
          </div>
        </div>
      </div>
       <div >
        <div >
          <div >
            <label >{{
              "discovery_inventory.resource_overview.correlation_id" | translate
            }}</label>
            <span
              
              title="{{ correlationID }}"
              >{{ correlationID }}</span
            >
          </div>
        </div>
        <div >
          <div >
            <label >{{
              "discovery_inventory.resource_overview.resource_category" | translate
            }}</label>
            <span
              
              title="{{ resourceCategory }}"
              >{{ resourceCategory }}</span
            >
          </div>
        </div>
        </div>
        <div *ngIf="showMore">
        <div >
       <div >
          <div >
            <label >{{
              "discovery_inventory.resource_overview.status" | translate
            }}</label>
            <span
              
              title="{{ status }}"
              >{{ status }}</span
            >
          </div>
        </div>
        <div >
          <div >
            <label >{{
              "discovery_inventory.resource_overview.provider" | translate
            }}</label>
            <span
              
              title="{{ provider }}"
              >{{ provider }}</span
            >
          </div>
        </div>
      </div>
      
        <div >
          <div >
            <div >
              <label >{{
                "discovery_inventory.resource_overview.provider_account" | translate
              }}</label>
              <span
                
                title="{{ providerAccount }}"
                >{{ providerAccount }}</span
              >
            </div>
          </div>
          <div >
            <div >
              <label >{{
                "discovery_inventory.resource_overview.resource_name" | translate
              }}</label>
              <span
                
                title="{{ resourceName }}"
                >{{ resourceName }}</span
              >
            </div>
          </div>
        </div>
       
      </div>
      <div >
        <div  (click)="showMore = !showMore">
          <label *ngIf="showMore">{{
            "discovery_inventory.resource_overview.show_less" | translate
          }}</label>
          <label *ngIf="!showMore">{{
            "discovery_inventory.resource_overview.show_more" | translate
          }}</label>
        </div>
      </div>
    </div>
  </div>
  <div  >
    <div  >
      <label >{{"discovery_inventory.resource_overview.configuration_details" | translate}}</label>
    </div>
    <div >
      <div >
        <div >
          <div >
            <label >{{"discovery_inventory.resource_overview.account_id" | translate}}</label>
            <span
              
              title="{{ providerAccount }}"
              >{{ providerAccount }}</span
            >
          </div>
        </div>
        <div >
          <div >
            <label >{{
              "discovery_inventory.resource_overview.provider_account_name" | translate
            }}</label>
            <span
              
              title="{{ providerAccount }}"
              >{{ providerAccountName }}</span
            >
          </div>
        </div>
        
    </div>
    <div >
    <div >
      <div >
        <label >{{"discovery_inventory.resource_overview.creation_date" | translate}}</label>
        <span
          
          title="{{ creationDate }}"
          >{{ creationDate }}</span
        >
      </div>
    </div>
     <div >
            <div >
              <label >{{
                "discovery_inventory.resource_overview.provider_lastupdated" | translate
              }}</label>
              <span
                
                title="{{ providerLastUpdated }}"
                >{{ providerLastUpdated }}</span
              >
            </div>
          </div>
    </div>
    <div >
      <div >
        <div >
          <label >{{
            "discovery_inventory.resource_overview.arn" | translate
          }}</label>
          <span
            
            title="{{ crn }}"
            >{{ crn }}</span
          >
        </div>
      </div>
      <div >
        <div >
          <label >{{"discovery_inventory.resource_overview.provider_status" | translate}}</label>
          <span
            
            title="{{ providerStatus }}"
            >{{ providerStatus }}</span
          >
        </div>
      </div>
  </div>
  <div >
    <div >
      <div >
        <label >{{"discovery_inventory.resource_overview.tags" | translate}}</label>
        <div>
        <span >
          <table>
          <tr>
            <th>Key</th>
            <th>Value</th>
          </tr>
          <tr *ngFor="let k of keyValue; let i = index;">
            <td>{{k.Key}}</td>
            <td>{{k.Value}}</td>
          </tr>
        </table>
      </span>
    </div>
      </div>
    </div>
    
  </div>
    </div>
  </div>
</div>

CodePudding user response:

2nd test

You are calling component.ngOnInit(); which internally calls getDetails();

3rd test

You are calling getDetails();

IMO, both are doing same, either you can delete your 2nd test and have your 3rd test calling component.ngOnInit(); with the mock api response or have the same mock API response in your 2nd test.

CodePudding user response:

The problem

The problem is that your test is constructing and providing an invalid date to the angular DatePipe. Since the pipe lives in your HTML, you only see the error after running detectChanges since the template is only rendered then.

The error happens in your lastUpdated | date:dateTimeFormat part here

<label >{{ 'discovery_inventory.resource_overview.discovery_lastupdated' | translate }}
        {{lastupdated  | date : dateTimeFormat}} {{dateTimeZone}}</label>

Since your getResourceOverview is executed in your test (due to an ID being set), the following line will also be executed:

// This will construct an invalid date, since date is always truthy (even when called with "new Date(undefined)", 
// "|| '--'" will never be executed
this.lastupdated = new Date(Date.parse(result.raw_items[0].gpd.discovery_lastupdated)) || '--' ;

This effectively assings an invalid date (constructed with new Date(undefined)) to your lastupdated instance variable, hence leading to the error you are seeing.

The solution

You either have to make sure that you are providing all the nescessary data in your test and/or make your code more robust. In this case I highly recommend checking for undefined/null correctly before providing data to your pipe:

<label >
  {{ 'discovery_inventory.resource_overview.discovery_lastupdated' | translate }}
  
  <!-- 
    Use the terniary operator to conditionally use the pipe or fall back 
    to "--" ("--" itself would be an invalid argument for the pipe, so do 
    not provide this to the pipe directly!) 
  -->
  {{ (lastupdated ? (lastupdated | date : dateTimeFormat) : '--' }} {{dateTimeZone}}
</label>

Additionally, you should make sure to assign undefined if you cannot construct a valid date to your variable:

this.lastupdated = result.raw_items[0].gpd.discovery_lastupdated 
  ? new Date(Date.parse(result.raw_items[0].gpd.discovery_lastupdated)) 
  : undefined ;

CodePudding user response:

describe('ResourceOverviewComponent', () => {
  let component: ResourceOverviewComponent;
  let fixture: ComponentFixture<ResourceOverviewComponent>;
  let resourceOverviewService: any;
  const data = {"raw_items":[{"uuid":"123456789:i-0105345f167219fbf:vms","gpd":{"version":"0.1","discovery_lastupdated":"2021-09-08T05:36:08Z","correlation_id":"aws-123456789-i-0105345f167219fbf","core_credential_id":["credid"],"provider_code":"aws","provider_account":"123456789","provider_account_name":"accountname","provider_account_details":{"cb84865d-3477-40f3-9131-4462273cfedb":"accountname"},"provider_resource_type":"vms","provider_resource_id":"i-0105345f167219fbf","provider_resource_name":"cd-642","provider_resource_lastupdated":"","provider_resource_created":"2021-08-30T06:12:49Z","provider_resource_crn":"arn:aws:ec2:us-east-1:123456789:vms/i-0105345f167219fbf","provider_resource_location":"us-east-1","provider_status":"running","location_geo_coordinates":{"latitude":38.86726,"longitude":-77.233959},"status":"Active","service_category":"compute","tags":[{"Key":"Name","Value":"cd-642"}],"raw_config_data":null,"extended_data":null}}],"meta":null,"pagination":{"count":1,"total":1,"offset":0,"limit":10,"first":"https://local-core.gravitant.net/discovery/api/inventory/v1/service?limit=10","last":"https://local-core.gravitant.net/discovery/api/inventory/v1/service?limit=10","next":"","prev":""}};;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ResourceOverviewComponent ],
      imports: [
        CarbonComponentsModule,CarbonModule,CbTranslationUiLibModule.forRoot(),
        RouterModule.forRoot([]),
      ],
      providers: [
        { provide: NGXLogger, useClass: MockNGXLogger },
        { provide: HttpWrapperService, useClass: mockHttpWrapperService },
        { provide: Router, useClass: class { navigate = jasmine.createSpy("navigate"); }},
        ApiService,
        ApiContractService,
        ResourceOverviewService,
        TranslationService,
        CbCommonDiscoveryManagementMicroUiService,
        {
          provide: ActivatedRoute,
          useValue: {
            params: of({ id: 'aeb24ca0549c', code: 'IBM' }),
            snapshot: {
              queryParamMap: convertToParamMap({
                id: 'aeb24ca0549c',
                code: 'IBM',
              })
            }
          }
        },
      ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    })
    .compileComponents()
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(ResourceOverviewComponent);
    component = fixture.componentInstance;
    resourceOverviewService = TestBed.inject(ResourceOverviewService);
    spyOn(resourceOverviewService, "getResourceOverview").and.callFake(() => {
      return of(data)
    })
    fixture.detectChanges();
  });

  it('should create component ', () => {
    expect(component).toBeTruthy();
  });

  it('should init object for action tabs', async () => {
    component.ngOnInit();
    expect(component).toBeTruthy();
  });

  it('should get resource data ', () => {
    component.getDetails();
    expect(component).toBeTruthy();
  });
});
  • Related