工作中使用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.


目录
相关文章
|
5月前
|
Web App开发 缓存 搜索推荐
HTML中meta标签中属性详解并设置页面缓存策略
HTML中meta标签中属性详解并设置页面缓存策略
295 0
|
4月前
|
JavaScript
原生JS修改html内容不影响绑定的点击事件 请认准insertAdjacentHTML、insertAdjacentText方法
原生JS修改html内容不影响绑定的点击事件 请认准insertAdjacentHTML、insertAdjacentText方法
原生JS修改html内容不影响绑定的点击事件 请认准insertAdjacentHTML、insertAdjacentText方法
|
1月前
|
存储 开发者 SEO
|
1月前
|
移动开发 JavaScript 前端开发
编程笔记 html5&css&js 028 HTML输入属性(2/2)
编程笔记 html5&css&js 028 HTML输入属性(2/2)
|
1月前
|
JavaScript 前端开发
编程笔记 html5&css&js 027 HTML输入属性(1/2)
编程笔记 html5&css&js 027 HTML输入属性(1/2)
|
1月前
编程笔记 html5&css&js 023 HTML表单属性
编程笔记 html5&css&js 023 HTML表单属性
|
1月前
|
存储 移动开发 前端开发
编程笔记 html5&css&js 010 HTML全局属性
编程笔记 html5&css&js 010 HTML全局属性
|
2月前
|
移动开发 前端开发 JavaScript
分享24个强大的HTML属性,建议每位前端工程师都应该掌握!
HTML属性非常多,除了一些基础属性外,还有许多有用的特别强大的属性
|
3月前
|
XML JavaScript 数据格式
python - bs4提取XML/HTML中某个标签下的属性
python - bs4提取XML/HTML中某个标签下的属性
29 0