在我们的项目中,我们可以在整个应用程序中将用户角色分配给多个元素,在缺少所需角色时禁用这些元素。但是,由于对某些按钮应用了其他条件,即使角色检查,也可能禁用组件,因此我们的组件也会接管它,并在必要时禁用它的子组件。这个设计背后的原因是,如果元素被禁用,我们可以很容易地显示工具提示。
// app-role-element
<div matTooltip="{{ tooltipMessage | translate }}" [matTooltipDisabled]="!isDisabled">
<ng-content> </ng-content>
</div>
// .ts
export class AppRoleElement implements AfterConentInit {
@Input('roles') roles: string[];
@ContentChild('roleElement') roleElement: MatButton;
constructor(...){}
ngAfterContentInit(): void {
...
this.setDisabled();
}
setDisabled(): void {
if (this.roleElement) {
if (this.isDisabled) {
this.roleElement.disabled = true; // disable if no role
} else {
this.roleElement.disabled = this.disableCondition; // disable if other condition
}
}
}
}
// usage
<app-role-component
[role]="['required-role']"
[disableCondition]= "isRunning || isUnstable"
[id]="startButtonRoleElem"
>
<button mat-raised-button id="startBtnId" (click)="start()">Start</button>
</app-role-component>
这种方法工作得很好,但是很难进行单元测试。在上面的代码中,如果我要编写一个单元测试,点击开始按钮,按ID选择它,绕过角色元素并调用远程服务,即使它不应该。按ID选择角色元素并不会传播对按钮的单击。
test('to prevent click on a start btn when form is invalid', () => {
spectator.component.runEnabled$ = of(false);
spectator.detectComponentChanges();
const checkExportFolderSpy = jest.spyOn(spectator.component, 'checkExportFolder');
spectator.inject(PreferencesService).validateCurrentExportPath.andReturn(of(VALIDATION_RESULT_OK));
spectator.detectChanges();
spectator.click('#startBtnId');
spectator.detectChanges();
expect(checkExportFolderSpy).not.toHaveBeenCalled();
expect(dispatchSpy).not.toHaveBeenCalled();
});
我们和Spectator以及NgMocks一起使用JEST,我希望利用这个功能并模拟这个组件,但我不知道如何使用。我不太确定我应该在多大程度上去模仿它,我是否应该把点击事件传递给孩子,禁用孩子?有什么想法或建议如何处理这件事?
发布于 2021-03-23 11:37:06
你的案子很复杂。
这很复杂,因为:
MatButton
禁用,因此不能对其进行模拟。AppRoleElement
,所以也不能对它进行模拟。triggerEventHandler
不尊重disabled
属性,总是触发单击因此,在测试中,我们需要:
AppRoleElement
和MatButton
的原样nativeElement
单击按钮下面的代码只使用五模,角色检测已经简化。
import {AfterContentInit, Component, ContentChild, Input, NgModule,} from '@angular/core';
import {MatButton, MatButtonModule} from '@angular/material/button';
import {MockBuilder, MockRender, ngMocks} from 'ng-mocks';
@Component({
selector: 'app-role-component',
template: `
<div>
<ng-content></ng-content>
</div>
`,
})
class AppRoleElement implements AfterContentInit {
@Input() public disable: boolean | null = null;
@ContentChild(MatButton) public roleElement?: MatButton;
public ngAfterContentInit(): void {
this.setDisabled();
}
public setDisabled(): void {
if (this.roleElement) {
this.roleElement.disabled = this.disable;
}
}
}
@NgModule({
declarations: [AppRoleElement],
imports: [MatButtonModule],
})
class AppModule {}
fdescribe('ng-mocks-click', () => {
// Keeping AppRoleElement and MatButton
beforeEach(() => MockBuilder(AppRoleElement, AppModule).keep(MatButton));
it('is not able to click the disabled button', () => {
// specific params for the render
const params = {
disabled: true,
start: jasmine.createSpy('start'),
};
// rendering custom template
MockRender(
`
<app-role-component
[disable]="disabled"
>
<button mat-raised-button id="startBtnId" (click)="start()">Start</button>
</app-role-component>
`,
params,
);
// click on a disable element isn't propagandized
ngMocks.find('button').nativeElement.click();
// asserting
expect(params.start).not.toHaveBeenCalled();
});
// checking the enabled case
it('is able to click the button', () => {
const params = {
disabled: false,
start: jasmine.createSpy('start'),
};
MockRender(
`
<app-role-component
[disable]="disabled"
>
<button mat-raised-button id="startBtnId" (click)="start()">Start</button>
</app-role-component>
`,
params,
);
ngMocks.find('button').nativeElement.click();
expect(params.start).toHaveBeenCalled();
});
});
https://stackoverflow.com/questions/66744417
复制相似问题