Testing *ngIf show/hide component dependent on dat

2019-08-17 04:01发布

问题:

I am trying to test a show/hide data component. Without its data (currently undefined), a simple block of text is displayed.

However when i then feed the component with data, the first block should become hidden whilst the second block that consists of the list of data in a table should become visible, however null is being returned (withDataBlock).

Therefore tests errors on the last expect of the test, even though i add fixture.detectChanges() method beforehand.

component:

 import { Component, OnInit, OnChanges, Input, SimpleChanges, OnDestroy, ViewChildren, QueryList } from '@angular/core';
import { Response } from '@angular/http';
import { AccountSearchService } from './../../services/account-search.service';
import { ISearchResult, ISearchResultContentItem, IContentItemStatus } from './../../entities/search.entity';
import { SortByPipe } from './../../pipes/sort.pipe';
import { FilterPipe } from './../../pipes/filter.pipe';
import { CapitalisePipe } from './../../pipes/capitalise.pipe';
import { Subject, Subscription } from 'rxjs';

@Component({
    selector: 'app-account-list',
    templateUrl: './account-list.component.html',
    styleUrls: ['./account-list.component.scss'],
    inputs: ['inputSearchTerm']
})
export class AccountListComponent implements OnInit, OnChanges, OnDestroy {
    private searchTerm: string = undefined;
    private sortByParam: string = "name";
    private initialText = "Perform search above to view results below.";
    private subs: Subscription = undefined;
    private searchResults: ISearchResult = undefined;
    private accountsCollection: Array<ISearchResultContentItem> = undefined;

    @Input() public set inputSearchTerm(value: string) {
        this.searchTerm = value;
    }

    constructor(
        private _accountSearchService: AccountSearchService
    ) {

    }

    ngOnInit() {

    }

    ngOnChanges(changes: SimpleChanges) {
        if (!changes.inputSearchTerm.firstChange) {

            let curValue = changes.inputSearchTerm.currentValue;

            this.subs = this._accountSearchService.SearchAccounts()
                .subscribe((response: Response) => {

                    if (response.status == 200) {
                        this.searchResults = <ISearchResult>response.json();
                        this.accountsCollection = <Array<ISearchResultContentItem>>this.searchResults.content;
                    }
                }
            )
        }
    }

    ngOnDestroy() {
        if (this.subs)
            this.subs.unsubscribe();
    }

}

HTML:

<section>
    <div id="accounts-list" class="accounts-list">
        <div id="accountsListNoData" class="row" *ngIf="!accountsCollection">
            <div class="col-12">
                <p>{{ initialText }}</p>
            </div>
        </div>
        <div id="accountsListWithData" class="row accountsListWithData" *ngIf="accountsCollection">
            <div class="col-12">

                <div class="row" *ngIf="searchTerm">
                    <div class="col-6">
                        <p>You searched for: "{{ searchTerm }}"</p>
                    </div>
                    <div class="col-6 text-right" *ngIf="searchResults">
                        <p>{{ 'Page: ' + (searchResults.page+1) + ' of ' + searchResults.totalPages }}</p>
                    </div>
                </div>

                <div class="row" *ngIf="(accountsCollection | filter: searchTerm).length === 0">
                    <div class="col-12">
                        <p>No results found</p>
                    </div>
                </div>

                <div class="row">
                    <div class="col-12" *ngIf="(accountsCollection | filter: searchTerm).length > 0">
                        <div class="table-responsive">

                            <table id="accountsListTable" class="table">
                                <thead class="thead-brand">
                                    <tr>
                                        <th scope="col">
                                            <a (click)="sortByParam='id'" title="Sort by Account ID">Account ID</a>
                                        </th>
                                        <th scope="col">
                                            <a (click)="sortByParam='name'" title="Sort by Account Name">Account name</a>
                                        </th>
                                        <th scope="col">
                                            <a href="#" title="Sort by account number">Account number</a>
                                        </th>
                                        <th scope="col">
                                            <a href="#" title="Sort by account sort code">Sort code</a>
                                        </th>
                                        <th scope="col">
                                            <a (click)="sortByParam='balance'" title="Sort by balance">Balance</a>
                                        </th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr *ngFor="let account of accountsCollection | filter: searchTerm | sortBy: sortByParam">
                                        <td scope="row">{{ account.id }}</td>
                                        <td>
                                            <strong>{{ account.name | capitalise }}</strong>
                                        </td>
                                        <td [innerHTML]="account.identifiers[0].accountNumber"></td>
                                        <td [innerHTML]="account.identifiers[0].sortCode"></td>
                                        <td [innerHTML]="'&pound;' + account.balance"></td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>

Test:

import { async, ComponentFixture, TestBed, tick } from '@angular/core/testing';
import { AccountListComponent } from './account-list.component';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { SortByPipe } from './../../pipes/sort.pipe';
import { FilterPipe } from './../../pipes/filter.pipe';
import { CapitalisePipe } from './../../pipes/capitalise.pipe';
import { Http, HttpModule } from '@angular/http';
import { AccountSearchService } from '../../services/account-search.service';
import { ISearchResult, IContentItemStatus, IIdentifiers, ISearchResultContentItem } from './../../entities/search.entity';

describe("AccountListComponent", () => {
    let component: AccountListComponent;
    let fixture: ComponentFixture<AccountListComponent>;
    let debEl: DebugElement;

    beforeEach(async () => {
        TestBed.configureTestingModule({
            declarations: [
                AccountListComponent, CapitalisePipe, SortByPipe, FilterPipe
            ],
            imports: [HttpModule],
            providers: [AccountSearchService]
        }).compileComponents()
    })

    beforeEach(() => {
        fixture = TestBed.createComponent(AccountListComponent);
        component = fixture.componentInstance;
        debEl = fixture.debugElement;
    })


    it("should initialise the component", () => {
        fixture.detectChanges();
        fixture.whenStable().then(() => {
            expect(component).toBeTruthy();
        });
    })

    it("should show accounts-list__noData block without data", () => {

        expect((<any>component).searchResults).toBeUndefined;

        fixture.detectChanges();

        //test initial instruction text
        let alEl = debEl.query(By.css(".accounts-list"));
        let withoutDataBlock = <HTMLDivElement>alEl.nativeElement.querySelector("p");
        expect(withoutDataBlock.textContent).toEqual((<any>component).initialText);
    })

    it("Show accounts-list__withData block with passed in data", fakeAsync((done) => {

    let alEl = debEl.query(By.css(".accounts-list"));
    let withDataBlock = <HTMLDivElement>alEl.nativeElement.querySelector("#accountsListWithData");

    expect((<any>component).searchTerm).toBeUndefined;
    expect(withDataBlock).toBeUndefined;

    const testAccounts = <ISearchResult>{
        content: <Array<ISearchResultContentItem>>[
            {
                id: "A02017NU",
                name: "1010 SOFTWARE LTD",
                balance: "46.91",
                currency: "GBP",
                status: IContentItemStatus.ACTIVE,
                identifiers: <Array<IIdentifiers>>[
                    {
                        type: "SCAN",
                        accountNumber: "00001232",
                        sortCode: "000000"
                    }
                ],
                customerId: "C0200098",
                externalReference: "Account 1"
            }
        ],
        page: 0,
        size: 15,
        totalPages: 407,
        totalSize: 60960944
    };

    component.inputSearchTerm = "software";

    const inputSearchTerm = <SimpleChange> {
        currentValue: "software",
        firstChange: false,
        previousValue: undefined
    }

    component.ngOnChanges({ inputSearchTerm });
    fixture.checkNoChanges();
    fixture.detectChanges();
    done();
    expect((<any>component).searchTerm).toBeDefined;


}))

})

Error:

ERROR: 'Unhandled Promise rejection:', 'Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'ng:///DynamicTestModule/AccountListComponent.ngfactory.js'.', '; Zone:', '<root>', '; Task:', 'Promise.then', '; Value:', DOMException{stack: 'Error: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'ng:///DynamicTestModule/AccountListComponent.ngfactory.js'.
    at http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:2990:1
    at XMLHttpRequest.proto.(anonymous function) [as send] (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:1394:1)
    at Array.<anonymous> (http://localhost:9876/absolute/Users/victoroyea/Documents/Sites/modulr/modulr/node_modules/source-map-support/browser-source-map-support.js?f894bb45358bceea281c12011b12863c67d3942c:111:263)