工作中使用jasmine遇到的一个html element和Component绑定属性失去同步的问题

简介: 工作中使用jasmine遇到的一个html element和Component绑定属性失去同步的问题

background

file location: projects\storefrontlib\src\cms-components\checkout\components\delivery-mode\delivery-mode.component.html


we are now talking about property binding of “continue” button’s disabled property.


Expected behavior

when component property deliveryModelnvalid = true, continue button’s disabled property will be set as true as well, so button is disabled, and vice versa.

image.pngimage.pngso if we set this.mode.controls[‘deliveryModeId’] to ‘’ or null, deliveryModeInvalid will equal to true.


Jerry’s observation in unit test

if we change the value of this.mode.controls[‘deliveryModeId’] TWICE in the same test spec, [disabled] and deliveryModeInvalid will LOSE synchronization, even fixture.detectChanges is called manually.


See my test below.


Paste the following source code to replace your delivery-mode.component.spec.ts and launch it:


import { Component } from '@angular/core';

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { ReactiveFormsModule } from '@angular/forms';

import { ActivatedRoute } from '@angular/router';

import {

 CheckoutDeliveryService,

 DeliveryMode,

 I18nTestingModule,

} from '@spartacus/core';

import { Observable, of } from 'rxjs';

import { CheckoutConfigService } from '../../services/checkout-config.service';

import { CheckoutStepService } from '../../services/checkout-step.service';

import { DeliveryModeComponent } from './delivery-mode.component';

import createSpy = jasmine.createSpy;

import { LoaderState } from '../../../../../../core/src/state/utils/loader';

import { By } from '@angular/platform-browser';


@Component({

 selector: 'cx-spinner',

 template: '',

})

class MockSpinnerComponent {}


class MockCheckoutDeliveryService {

 loadSupportedDeliveryModes = createSpy();

 setDeliveryMode = createSpy();

 getSupportedDeliveryModes(): Observable {

   return of();

 }

 getSelectedDeliveryMode(): Observable {

   return of();

 }

 getLoadSupportedDeliveryModeProcess(): Observable> {

   return of();

 }

}


class MockCheckoutConfigService {

 getPreferredDeliveryMode(): string {

   return '';

 }

}


class MockCheckoutStepService {

 next = createSpy();

 back = createSpy();

 getBackBntText(): string {

   return 'common.back';

 }

}


const mockActivatedRoute = {

 snapshot: {

   url: ['checkout', 'delivery-mode'],

 },

};


describe('DeliveryModeComponent', () => {

 let component: DeliveryModeComponent;

 let fixture: ComponentFixture;


 beforeEach(async(() => {

   TestBed.configureTestingModule({

     imports: [ReactiveFormsModule, I18nTestingModule],

     declarations: [DeliveryModeComponent, MockSpinnerComponent],

     providers: [

       {

         provide: CheckoutDeliveryService,

         useClass: MockCheckoutDeliveryService,

       },

       { provide: CheckoutStepService, useClass: MockCheckoutStepService },

       { provide: CheckoutConfigService, useClass: MockCheckoutConfigService },

       { provide: ActivatedRoute, useValue: mockActivatedRoute },

     ],

   }).compileComponents();

 }));


 beforeEach(() => {

   fixture = TestBed.createComponent(DeliveryModeComponent);

   component = fixture.componentInstance;

   console.log('----------- a new test comes ------------------');

 });



 describe('continue button', () =>{


   const getContinueBtn = () => fixture.debugElement.query(By.css('.cx-checkout-btns .btn-primary'));

   const setDeliveryModeId = (value: string) =>  

     component.mode.controls['deliveryModeId'].setValue(value);


   function setDeliveryModeIdNull(){

     setDeliveryModeId(null);

     fixture.detectChanges();

     trace(null);

   }

   function setDeliveryModeIdValid(){

     setDeliveryModeId('a');

     fixture.detectChanges();

     trace('a');

   }


   function trace(id){

     const button = getContinueBtn();

     console.log('************** Delivery Mode id is set as: ' + id + '**************');

     console.log('Flag component.deliveryModeInvalid: ' +

     component.deliveryModeInvalid + ' button.disabled: ' + button.nativeElement.disabled );

   }


   function valid_then_null(){

     setDeliveryModeIdValid();


     setDeliveryModeIdNull();

   }


   function null_then_valid(){

     setDeliveryModeIdNull();


     setDeliveryModeIdValid();

   }

   

   it('first valid then null', () => {

     valid_then_null();

     expect(component).toBeTruthy();

   });


   it('null then valid', () => {

     null_then_valid();

     expect(component).toBeTruthy();

   });


   it('only valid', () => {

     setDeliveryModeIdValid();

     expect(component).toBeTruthy();

   });


   it('only null', () => {

     setDeliveryModeIdNull();

     expect(component).toBeTruthy();

   });

 });

});


In this unit test file I construct four test specs:


(1) set delivery mode id to a valid value first, then set null;

(2) set delivery mode id to null first, then set a valid value to it;

(3) only set a valid value;

(4) only set null;


In each test spec, once I manupulate the value of this.mode.controls[‘deliveryModeId’], then use console.log to display the following pair of values:


component.deliveryModeInvalid

button.nativeElement.disabled

In theory the two must always be equal.


Test result

image.pngimage.png

Conclusion

Avoid change delivery mode id TWICE in a single test spec. The case to set id as A in beforeEach and set id as B in a test spec SHOULD also be considered as TWICE, so the two value will lose synchronization as well.


目录
相关文章
WK
|
2月前
|
存储 移动开发 前端开发
HTML5新增了哪些其他元素和属性
这段文字介绍了HTML5中新增的多种元素和属性,包括页面布局元素如header、nav等,表单元素如email、tel输入框等,以及其他元素如canvas、svg等。此外,还介绍了全局及表单属性,例如contenteditable、placeholder等,这些新功能显著增强了HTML5在现代网页设计与开发中的实用性与灵活性。
WK
55 1
|
1天前
|
移动开发 HTML5
HTML5 表单属性3
`<input>` 标签的 `formaction`、`formenctype` 和 `formmethod` 属性分别用于指定表单提交的 URL 地址、数据编码类型和提交方法,这些属性可覆盖 `<form>` 标签中的相应属性,并且主要适用于 `type="submit"` 和 `type="image"` 的输入类型。
|
23天前
|
搜索推荐 前端开发 UED
哪些 HTML 全局属性在 SEO 优化中比较重要?
【10月更文挑战第27天】这些HTML全局属性通过不同的方式为搜索引擎提供了更丰富、准确的页面信息,有助于提高页面的可索引性、相关性和用户体验,从而在SEO优化中发挥着重要的作用。开发者应充分重视并合理运用这些属性,以提升网站在搜索引擎中的排名和流量。
|
23天前
|
前端开发 搜索推荐 算法
|
23天前
|
前端开发 JavaScript 开发者
HTML 中的全局属性和局部属性是否可以相互覆盖?
【10月更文挑战第27天】HTML中的全局属性和局部属性在正常使用情况下不会相互覆盖,但在涉及CSS样式和JavaScript操作等特殊情况下,可能会出现类似覆盖的效果。开发者需要理解属性的功能、作用域和优先级,遵循最佳实践,以确保HTML文档的结构清晰、功能正常且易于维护。
|
23天前
|
存储 移动开发 前端开发
HTML全局属性
【10月更文挑战第27天】
HTML 属性参考手册
HTML属性参考手册提供了常用的HTML属性列表,包括`class`、`id`、`style`、`title`等,用于定义元素的样式、唯一标识、额外信息等。此外,还包括`href`、`src`、`alt`、`name`、`value`、`target`、`type`和`placeholder`等,分别用于链接、资源路径、替代文本、表单元素名称和值、链接打开方式、表单元素类型及占位符文本的定义。
HTML 属性
HTML属性为元素提供额外信息,格式为name="value",置于开始标签内。如<a href="http://www.runoob.com">,其中href为属性名,URL为值。属性值应加引号,推荐使用小写。
|
3月前
|
移动开发 前端开发 JavaScript
Twaver-HTML5基础学习(13)连线(Link)连线的绑定与展开
本文介绍了Twaver HTML5中连线(Link)的绑定与展开功能,包括分组绑定、自环绑定、绑定与展开以及展开间隙等属性的设置。通过示例代码展示了如何在React组件中创建Link并设置其绑定属性,实现连线的分组管理。
29 4
Twaver-HTML5基础学习(13)连线(Link)连线的绑定与展开
|
3月前
|
移动开发 前端开发 HTML5
Twaver-HTML5基础学习(8)拓扑元素(Element)_网元(Element)、节点(Node)
本文介绍了Twaver HTML5中的拓扑元素(Element),包括网元(Element)、节点(Node)和连线(Link)的基本概念和使用方法。文章详细解释了Element的属性和方法,并通过示例代码展示了如何在React组件中创建节点、设置节点属性和样式。
47 1
Twaver-HTML5基础学习(8)拓扑元素(Element)_网元(Element)、节点(Node)

热门文章

最新文章

下一篇
无影云桌面