Angular Тестирование - ожидайте, что не видно, если оно заключено в whenStable - PullRequest
7 голосов
/ 09 июля 2020

У меня есть компонент с вкладками. Мне нужно перейти на вкладку, а затем проверить видимые поля ввода и т. Д. c. Из журнала консоли я вижу, что действия, которые я предпринимаю, вызывают функцию, за которой я слежу, используя описанный ниже процесс. Я также вижу, что это происходит в правильном порядке.

К сожалению, это дает успешный тест без ошибок, в тесте нет никаких ожиданий согласно ng test. (SPE C НЕ ИМЕЕТ ОЖИДАНИЙ)

Размещение ожидания вне команды whenStable заставляет ожидание выполняться перед командой размытия.

it('should call to save changes when project name is blurred', 
    fakeAsync(() => {
        component.ngOnInit();
        tick();
        const tabLabels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
        console.log(tabLabels);
        tabLabels[1].triggerEventHandler('click', null);
        fixture.detectChanges();
        fixture.whenStable().then(() => {
            const projectName = fixture.debugElement.query(By.css('#projectName'));
            console.log(projectName);
            let mySpy = spyOn(component, 'saveProject');
            projectName.triggerEventHandler('blur', null);
            console.log('Lowered expectations');
            expect(mySpy).toHaveBeenCalled();
        });
    })
);

Вот HTML, связанный с тест.

    <!-- HEADER -->
    <div class="header accent" fxLayout="row"></div>
    <!-- / HEADER -->

    <!-- CONTENT -->
    <div class="content">


        <!-- CENTER -->
        <div class="center p-24" fusePerfectScrollbar>

            <!-- CONTENT -->
            <div class="content p-24" style="box-shadow:0px 0px 0px rgba(0,0,0,0) !important;">

                <mat-tab-group fxFlex="80%"
                               style="margin:0px 10%">
                    
                    <mat-tab id="projectSelectionTab" label="Project Selection">


                        <div fxFlex="80%" 
                             fxLayout="column"
                             style="margin:0px 10%"
                             *ngIf="versionList && projectList">

                            <h1 class="mt-32 mb-20">Select a Project</h1>

                            <mat-divider></mat-divider>


                            <div *ngIf="projectList.length==0">
                                You currently have no designs. Go to the marketplace and select a design to add to your list of projects.
                            </div>

                            <table class="displayTable">


                                <tr (click)="setCurrentProject( project ) "
                                   target="_blank"
                                   class="projectRow"
                                   [ngClass]="{'currentItem' : project==currentProject}"
                                   *ngFor="let project of projectList">

                                    <td class="mat-subheading-2" 
                                        style="width:10%">
                                        <mat-icon style="cursor:pointer" 
                                                  matTooltip="Open in design studio"
                                                  [routerLink]="['/designStudio/project/'+project.uid]">
                                            color_lens
                                        </mat-icon>

                                        <mat-icon style="cursor:pointer" 
                                                  matTooltip="View Quotes for this project"
                                                  [routerLink]="['/invoice/'+versionList[versionList.length-1]['uid']]">
                                            attach_money
                                        </mat-icon>
                                    </td>

                                    <td class="mat-subheading-2" 
                                        style="width:25%">
                                            {{ project.name }} 
                                    </td>


                                    <td class="mat-subheading-2" 
                                        style="width:20%">
                                            {{ project.dateCreated | date:'short' }} 
                                    </td>


                                    <td class="mat-body-1" > 
                                        
                                        <span style="font-weight:bold; margin-right:10px;">Type : {{project.designType}}</span> 
                                        <span style="font-weight:bold; margin-right:10px;">Versions : {{project.versions.length}}</span>
                                        {{ project.description }}
                                    </td>

                                </tr>


                            </table>

                        </div>

                    </mat-tab>


                    <mat-tab id="projectDataTab" label="Project Data" flex="100%">

                        <div flex="100%" 
                             layout="column" 
                             style="width:100%"
                             *ngIf="currentProject.uid">

                            <h2 class="mt-32 mb-20">
                                Project Data -                   
                                    <mat-icon style="cursor:pointer" 
                                              matTooltip="Open in design studio"
                                              [routerLink]="['/designStudio/project/'+currentProject.uid]">
                                        color_lens
                                    </mat-icon>

                            </h2>
                      

                            <!-- TOP ROW OF PROJECT DATA -->
                            <div fxLayout="row" fxLayoutAlign="center" flex="100%">

                                <div fxFlex="50%"fxLayout="column" class="text-center">
                                    <mat-form-field appearance="outline" floatLabel="always" class="w-100-p">
                                        <mat-label>Project Name</mat-label>
                                        <input matInput
                                               id="projectName"
                                               class="form-control" 
                                               placeholder="Product Name"
                                               name="projectName"
                                               #projectName="ngModel"
                                               [(ngModel)]="currentProject['name']"
                                               (blur)="saveProject()"
                                               minlength="5"
                                               maxlength="150"
                                               required>
                                    </mat-form-field>

                                    <div [hidden]="projectName.valid || projectName.pristine || !projectName.errors?.minlength"
                                         class="alert alert-danger">
                                        Project name is required with at least 5 characters
                                    </div>
                                            

                                    <mat-form-field style="min-height:190px" appearance="outline" floatLabel="always" class="w-100-p">
                                        <mat-label>Project Description</mat-label>
                                        <textarea style="min-height:190px"
                                                  matInput 
                                                  id="projectDescription"
                                                  placeholder="Product Description"
                                                  name="description"
                                                  #projectDescription="ngModel"
                                                  [(ngModel)]="currentProject['description']"
                                                  (blur)="saveProject()"
                                                  rows="5"
                                                  required
                                                  minlength="25">
                                        </textarea>
                                    </mat-form-field>

                                    <div [hidden]="projectDescription.valid || projectDescription.pristine || !projectDescription.errors?.minlength"
                                         class="alert alert-danger">
                                        Project description is required with at least 25 characters
                                    </div>


                                </div>


                                <div fxFlex="50" fxLayout="column" fxLayoutAlign="center center">

                                    <div fxFlex="80" style="margin:0px 10%">
                                        <img [src]="(designImageUrl | async)">
                                    </div>


                                    <div fxLayout="row" 
                                         fxLayoutAlign="center" 
                                         class="w-80-p">
                                        <button mat-raised-button 
                                                color="primary"
                                                *ngIf="changesExist"
                                                (click)="saveProject(); changesExist=false;"
                                                style="margin:10px 25%; width:50%">
                                            Save Project Changes
                                        </button>
                                    </div>
                                
                                </div>

                            </div>

                        </div>
                        <!-- / TOP ROW OF PROJECT DATA -->
                        
                    </mat-tab>









                    <mat-tab id="versionDataTab" label="Version Data">


                        <div layout="column" 
                             flex="100%" 
                             style="width:100%"
                             *ngIf="currentProject !== undefined">

                            <h2 class="mt-32 mb-20">Version Data</h2>



                            <!-- Row showing button to create default version -->
                            <div fxLayout="row" 
                                 fxLayoutAlign="center center" 
                                 flex="100%">

                                <div fxFlex="20" style="margin:20px 5%">
                                    <button mat-stroked-button 
                                            color="accent"
                                            (click)="createNewVersion('default')">
                                        New version (default)
                                    </button>
                                </div>

                            </div>


                            <!-- Row showing the version list -->
                            <div fxLayout="row" fxLayoutAlign="center" flex="100%">

                                <table class="displayTable"
                                       fxFlex="60" style="margin:0px 5%">
                                    <thead>
                                        <tr>
                                            <th>VERSION NUMBER AND NAME</th>
                                            <th></th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <tr *ngFor="let version of versionList; index as j"
                                            class="projectRow"
                                            [ngClass]="{'currentItem' : version==currentVersion}"
                                            (click)="onVersionSelected( j )">
                                            <td>{{j+1}} - {{version.name}}</td>
                                            <td>
                                                <mat-icon style="cursor:pointer" 
                                                          matTooltip="View Quotes for this version"
                                                          [routerLink]="['/invoice/'+version['uid']]">
                                                    attach_money
                                                </mat-icon>
                                                <mat-icon style="cursor:pointer" 
                                                          matTooltip="Make copy as latest version"
                                                          (click)="createNewVersion( j )">
                                                    add_circle
                                                </mat-icon>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table>

                                <!--

                                <div fxFlex="40" style="margin:0px 5%">
                                    <mat-form-field class="w-100-p ml-10-p mt-20 form-group">
                                      <mat-label>Select a version to see the details</mat-label>
                                      <mat-select  (selectionChange)="onVersionSelected(versionIndex)"
                                                   name="versionIndex"
                                                   #name="ngModel"
                                                   [(ngModel)]="versionIndex">
                                        <mat-option *ngFor="let version of versionList; index as i" [value]="i">
                                          {{i+1}} - {{version.name}}
                                        </mat-option>
                                      </mat-select>
                                    </mat-form-field>
                                </div>



                                <div fxLayoutAlign="center center" fxFlex="40" style="margin:0px 5%">
                                    <button mat-button class="red-900 mt-8" *ngIf="currentVersion.latest">Submit for purchase</button>
                                    <button mat-button class="primary mt-8" *ngIf="!currentVersion.latest">Recreate as latest version</button>  
                                </div>

                            -->


                            </div>


                            <div fxLayout="row" fxLayoutAlign="center" flex="100%">

                                <!-- VERSIONS DATA -->
                                <div fxLayout="column" 
                                     class="mt-32"
                                     flex="45%"
                                     style="width:40%; margin:0px 5%">

                                    <h3>Data</h3>

                                    <div fxLayout="row"
                                         class="mt-20 mb-32">
                                        <div fxFlex="50">
                                            Estimated price - {{currentVersion.price}}
                                        </div>
                                        <div fxFlex="50">
                                            Date Created - {{currentVersion.dateCreated | date:'short'}}
                                        </div>
                                    </div>


                                    <mat-form-field appearance="outline" 
                                                    floatLabel="always" 
                                                    class="w-100-p">
                                        <mat-label>Version Name</mat-label>
                                        <input matInput 
                                               class="form-control"
                                               placeholder="Version Name"
                                               name="vName"
                                               #versionName="ngModel"
                                               (blur)="versionChangesExist=true"
                                               [(ngModel)]="currentVersion.name"
                                               required
                                               minlength="5"
                                               maxlength="150">
                                    </mat-form-field>

                                    <div [hidden]="versionName.valid || versionName.pristine || !versionName.errors?.minlength"
                                         class="alert alert-danger">
                                        Version name is required with at least 5 characters
                                    </div>


                                    <mat-form-field appearance="outline" 
                                                    floatLabel="always" 
                                                    class="w-100-p">
                                        <mat-label>Version Description</mat-label>
                                        <textarea matInput 
                                                  placeholder="Version Description"
                                                  class="form-control"
                                                  name="versionDescription"
                                                  #versionDescription="ngModel"
                                                  (blur)="versionChangesExist=true"
                                                  [(ngModel)]="currentVersion.description"
                                                  rows="5"
                                                  required>
                                        </textarea>
                                    </mat-form-field>

                                    <div [hidden]="versionDescription.valid || versionDescription.pristine || !versionDescription.errors?.minlength"
                                         class="alert alert-danger">
                                        Version description is required with at least 25 characters
                                    </div>

                                </div>
                                <!--/  VERSION DATA -->



                                <!-- MEASUREMENT LIST -->
                                <div fxLayout="column" 
                                     class="mt-32"
                                     flex="45%"
                                     style="width:40%; margin:0px 5%">

                                    <div fxLayout="row" fxLayoutAlign="center" flex="100%">
                                        <button mat-raised-button 
                                                color="primary"
                                                *ngIf="versionChangesExist"
                                                (click)="saveVersion(); versionChangesExist=false;"
                                                style="margin:10px 15%; width:70%">
                                            Save Version
                                        </button>
                                    </div>

                                     <h3>Measurements</h3>

                                    <table mat-table [dataSource]="currentVersion.measurements" 
                                           class="mat-elevation-z8"
                                           style="box-shadow:none;">

                                      <ng-container matColumnDef="name">
                                        <th mat-header-cell *matHeaderCellDef> Name </th>
                                        <td mat-cell *matCellDef="let meas"> {{meas.name}} </td>
                                      </ng-container>

                                      <ng-container matColumnDef="value">
                                        <th mat-header-cell *matHeaderCellDef> Value </th>
                                        <td mat-cell *matCellDef="let meas"> {{meas.value}} </td>
                                      </ng-container>

                                      <tr mat-header-row *matHeaderRowDef="columnsToDisplayMeas"></tr>
                                      <tr mat-row *matRowDef="let row; columns: columnsToDisplayMeas;"></tr>
                                    </table>

                                </div>
                                <!--/  MEASUREMENT LIST -->

                            </div>

                        </div>
                    
                    </mat-tab>







                    <mat-tab id="designAndPurchaseTab" label="Design and Purchase Status">  


                        <div flex="100%" 
                             layout="column"
                             style="width:100%"
                             *ngIf="currentProject !== undefined">


                            <h2 class="mt-32 mb-20">Design and Purchase Status</h2>


                            <div fxLayout="row"
                                 fxLayoutAlign="center center">
                                <div *ngFor="let stage of projectStages; index as j;"
                                     fxLayoutAlign="center center"
                                     [class]="projectStatus[j] ? 'arrow_box_green' : 'arrow_box_red' "
                                     [ngStyle]="{'z-index':1000-j}" 
                                     (click)="setSelected( j )">
                                    <span>{{stage}}</span>
                                </div>
                            </div>

                            <div fxLayout="row"
                                 fxLayoutAlign="center center">
                                <div *ngFor="let stage of projectStages; index as j;"
                                     fxLayoutAlign="center center">

                                    <div [class]="projectStatus[j] ? 'green_right' : '' "
                                         *ngIf="selectedStatus[j]"></div>

                                    <div [class]="!projectStatus[j] ? 'red_right' : '' "
                                         *ngIf="selectedStatus[j]"></div>

                                    <div *ngIf="!selectedStatus[j]"
                                         class="filler"></div>

                                    <div class="filler"></div>

                                </div>

                            </div>



                            <div fxLayout="row"
                                 fxLayoutAlign="center center">
                                
                                <div *ngFor="let text of stageTexts; index as j;"
                                     fxLayoutAlign="center center">

                                    <div fxFlex="50"
                                         *ngIf="selectedStatus[j] && projectStatus[j]">{{text.done}}</div>

                                    <div fxFlex="50"
                                         *ngIf="selectedStatus[j] && !projectStatus[j]">{{text.notdone}}</div>

                                </div>

                            </div>


                        </div>

                    </mat-tab>
                      





                </mat-tab-group>


            </div>
            <!-- / CONTENT -->

        </div>
        <!-- / CENTER -->

    </div>
    <!-- / CONTENT -->

</div>

1 Ответ

3 голосов
/ 14 июля 2020

Это потому, что whenStable() плохо работает с функцией fakeAsync(), поскольку это материал async функции.

Для эффективного использования fakeAsync() нужно полагаться на tick() или flush().

Возможно, изменение вашего тестового примера, как это, должно работать.

it('should call to save changes when project name is blurred', 
    fakeAsync(() => {
        component.ngOnInit();
        tick();
        const tabLabels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
        console.log(tabLabels);
        tabLabels[1].triggerEventHandler('click', null);
        fixture.detectChanges();

        flushMicrotasks(); // or alternatively flush() or tick(250);

        fixture.detectChanges();

        const projectName = fixture.debugElement.query(By.css('#projectName'));
        console.log(projectName);
        let mySpy = spyOn(component, 'saveProject');
        projectName.triggerEventHandler('blur', null);
        console.log('Lowered expectations');
        expect(mySpy).toHaveBeenCalled();
    })
);

Изменить:

Заменено flush() с flushMicrotasks(), за которым следует fixture.detectChanges(), это позволит DOM обновляться с ответом после разрешения Promise. Если подумать, вы можете использовать этот подход и с flush() или tick().

...