- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个带标签的组件。我需要导航到一个选项卡,然后测试可查看的输入等。我可以从控制台日志中看到我正在执行的操作正在调用我正在使用以下过程进行监视的函数。我还可以看到这是以正确的顺序发生的。
不幸的是,这会产生一个没有错误的成功测试,根据 ng test 在测试中没有发现任何期望。 (规范没有期望)
将期望放在 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>
最佳答案
那是因为 whenStable()
不适合 fakeAsync()
功能,因为它是 async
函数的东西。
For using
fakeAsync()
efficiently, one must rely ontick()
orflush()
.
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();
})
);
编辑:
Replaced
flush()
withflushMicrotasks()
followed byfixture.detectChanges()
, it will allow the DOM to be updated with the response after resolving the Promise. On second thought, you can use this approach withflush()
ortick()
as well.
关于 Angular Testing - 如果包含在 whenStable 中,则预计不会看到,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62806027/
第一个例子 我有以下测试: import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Com
我有一个带标签的组件。我需要导航到一个选项卡,然后测试可查看的输入等。我可以从控制台日志中看到我正在执行的操作正在调用我正在使用以下过程进行监视的函数。我还可以看到这是以正确的顺序发生的。 不幸的是,
我很清楚这里的这个错误: https://github.com/angular/angular/issues/10148 其中提到需要调用 fixture.detectChanges(); 然后调用
我在一个 Angular2 组件的 ngOnInit 方法中运行一个 http 调用(使用 obervable): ngOnInit() { this.descriptorService.g
我正在为一个应用程序编写 jasmine/karma/webpack 单元测试,其中有很多内部 promise 在代码深处得到解决。我想使用 Angular 的异步、fixture.detectCha
你能帮我区分这两个东西吗。 根据我的理解,如果你只使用 observable,你可以使用 detectChanges()。因此,您可以直接更改组件属性或监视服务调用并返回一个可观察对象,然后调用 de
请注意,这并非特定于 Protractor。问题在于 Angular 2 的内置 Testability service Protractor 碰巧使用。 Protractor 调用 Testabil
尽管 whenStable 返回一个 promise ,但我不允许使用 await。 下面是我的tsconfig "moduleResolution": "node", "emitDecoratorM
我知道 async 和 fakeAsync 方法设置了某种监听器来记录所有异步操作,以便 Angular Testing 框架可以使用 whenStable 和 tick() 来管理等待所有这些东西完
我想知道这两种处理Angular框架异步调用的方法在测试时的区别: 第一个使用 jasmine 方法 async/await 第二个使用 Angular 方法 async/fixture.whenSt
我正在向我的 Angular 7 应用程序添加一些单元测试,但我不确定如何处理与 FileReader 的交互,因为使用 async/whenStable、fakeAsync 和 promise 不能
我是一名优秀的程序员,十分优秀!