且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

SAP 电商云 Spartacus UI Checkout Spinner 显示的单元测试代码增强版

更新时间:2022-02-18 09:40:22

import { Component, Type } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router';
import { CheckoutDeliveryFacade } from '@spartacus/checkout/root';
import { DeliveryMode, I18nTestingModule } from '@spartacus/core';
import { Observable, of } from 'rxjs';
import { LoaderState } from '../../../../../projects/core/src/state/utils/loader';
import { CheckoutConfigService } from '../../services/checkout-config.service';
import { CheckoutStepService } from '../../services/checkout-step.service';
import { DeliveryModeComponent } from './delivery-mode.component';

import createSpy = jasmine.createSpy;

@Component({
  selector: 'cx-spinner',
  template: '',
})
class MockSpinnerComponent {}

class MockCheckoutDeliveryService {
  loadSupportedDeliveryModes = createSpy();
  setDeliveryMode = createSpy();

  getSupportedDeliveryModes(): Observable<DeliveryMode[]> {
    return of();
  }
  getSelectedDeliveryMode(): Observable<DeliveryMode> {
    return of();
  }
  getLoadSupportedDeliveryModeProcess(): Observable<LoaderState<void>> {
    return of();
  }
  getSetDeliveryModeProcess(): Observable<LoaderState<void>> {
    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'],
  },
};

const mockDeliveryMode1: DeliveryMode = {
  code: 'standard-gross',
  name: 'Standard Delivery',
  deliveryCost: { formattedValue: '$10.00' },
};

const mockDeliveryMode2: DeliveryMode = {
  code: 'premium-gross',
  name: 'Premium Delivery',
  deliveryCost: { formattedValue: '$20.00' },
};

const mockSupportedDeliveryModes: DeliveryMode[] = [
  mockDeliveryMode1,
  mockDeliveryMode2,
];

describe('DeliveryModeComponent', () => {
  let component: DeliveryModeComponent;
  let fixture: ComponentFixture<DeliveryModeComponent>;
  let mockCheckoutDeliveryService: CheckoutDeliveryFacade;
  let mockCheckoutConfigService: CheckoutConfigService;
  let checkoutStepService: CheckoutStepService;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        imports: [ReactiveFormsModule, I18nTestingModule],
        declarations: [DeliveryModeComponent, MockSpinnerComponent],
        providers: [
          {
            provide: CheckoutDeliveryFacade,
            useClass: MockCheckoutDeliveryService,
          },
          { provide: CheckoutStepService, useClass: MockCheckoutStepService },
          {
            provide: CheckoutConfigService,
            useClass: MockCheckoutConfigService,
          },
          { provide: ActivatedRoute, useValue: mockActivatedRoute },
        ],
      }).compileComponents();

      mockCheckoutDeliveryService = TestBed.inject(CheckoutDeliveryFacade);
      mockCheckoutConfigService = TestBed.inject(CheckoutConfigService);
      checkoutStepService = TestBed.inject(
        CheckoutStepService as Type<CheckoutStepService>
      );
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(DeliveryModeComponent);
    component = fixture.componentInstance;
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });

  it('should get supported delivery modes', () => {
    spyOn(
      mockCheckoutDeliveryService,
      'getSupportedDeliveryModes'
    ).and.returnValue(of(mockSupportedDeliveryModes));
    component.ngOnInit();

    component.supportedDeliveryModes$.subscribe((modes) => {
      expect(modes).toBe(mockSupportedDeliveryModes);
    });
  });

  it('should pre-select preferred delivery mode if not chosen before', () => {
    spyOn(
      mockCheckoutDeliveryService,
      'getSupportedDeliveryModes'
    ).and.returnValue(of(mockSupportedDeliveryModes));
    spyOn(
      mockCheckoutDeliveryService,
      'getSelectedDeliveryMode'
    ).and.returnValue(of(null));
    spyOn(
      mockCheckoutConfigService,
      'getPreferredDeliveryMode'
    ).and.returnValue(mockDeliveryMode1.code);

    component.ngOnInit();

    expect(
      mockCheckoutConfigService.getPreferredDeliveryMode
    ).toHaveBeenCalledWith(mockSupportedDeliveryModes);
    expect(component.mode.controls['deliveryModeId'].value).toBe(
      mockDeliveryMode1.code
    );
  });

  it('should select the delivery mode, which has been chosen before', () => {
    spyOn(
      mockCheckoutDeliveryService,
      'getSupportedDeliveryModes'
    ).and.returnValue(of(mockSupportedDeliveryModes));
    spyOn(
      mockCheckoutDeliveryService,
      'getSelectedDeliveryMode'
    ).and.returnValue(of(mockDeliveryMode2));
    spyOn(
      mockCheckoutConfigService,
      'getPreferredDeliveryMode'
    ).and.returnValue(mockDeliveryMode1.code);

    component.ngOnInit();

    expect(
      mockCheckoutConfigService.getPreferredDeliveryMode
    ).not.toHaveBeenCalled();
    expect(component.mode.controls['deliveryModeId'].value).toBe(
      mockDeliveryMode2.code
    );
  });

  it('should change step after invoking back()', () => {
    component.back();
    expect(checkoutStepService.back).toHaveBeenCalledWith(
      <any>mockActivatedRoute
    );
  });

  it('should get deliveryModeInvalid()', () => {
    const invalid = component.deliveryModeInvalid;

    expect(invalid).toBe(true);
  });

  it('should reload delivery modes on error', () => {
    spyOn(
      mockCheckoutDeliveryService,
      'getLoadSupportedDeliveryModeProcess'
    ).and.returnValue(of({ loading: false, success: false, error: true }));

    component.ngOnInit();

    expect(
      mockCheckoutDeliveryService.loadSupportedDeliveryModes
    ).toHaveBeenCalledTimes(1);
  });

  describe('Shipping method radio input', () => {
    const getRadioInput = () =>
      fixture.debugElement.query(By.css('.form-check .form-check-input'));

    it('should be displayed by default', () => {
      spyOn(
        mockCheckoutDeliveryService,
        'getSupportedDeliveryModes'
      ).and.returnValue(of(mockSupportedDeliveryModes));
      component.ngOnInit();

      fixture.detectChanges();

      expect(getRadioInput().nativeElement).toBeTruthy();
    });

    it('should not be displayed when there is another ongoing request', () => {
      spyOn(
        mockCheckoutDeliveryService,
        'getSupportedDeliveryModes'
      ).and.returnValue(of(mockSupportedDeliveryModes));
      component.ngOnInit();

      component.deliveryModeLoaded$ = of(false);
      fixture.detectChanges();

      expect(getRadioInput()).not.toBeTruthy();
    });
  });

  describe('UI continue button', () => {
    const getContinueBtn = () =>
      fixture.debugElement.query(By.css('.cx-checkout-btns .btn-primary'));
    const setDeliveryModeId = (value: string) => {
      component.mode.controls['deliveryModeId'].setValue(value);
    };

    it('should be disabled when delivery mode is not selected', () => {
      setDeliveryModeId(null);
      fixture.detectChanges();

      expect(getContinueBtn().nativeElement.disabled).toBe(true);
    });

    it('should be enabled when delivery mode is selected', () => {
      setDeliveryModeId(mockDeliveryMode1.code);
      fixture.detectChanges();

      expect(getContinueBtn().nativeElement.disabled).toBe(false);
    });

    it('should call "next" function after being clicked', () => {
      spyOn(component, 'next');

      setDeliveryModeId(mockDeliveryMode1.code);
      fixture.detectChanges();
      getContinueBtn().nativeElement.click();
      fixture.detectChanges();

      expect(component.next).toHaveBeenCalled();
    });
  });

  describe('UI back button', () => {
    const getBackBtn = () =>
      fixture.debugElement.query(By.css('.cx-checkout-btns .btn-action'));

    it('should call "back" function after being clicked', () => {
      spyOn(component, 'back');

      fixture.detectChanges();
      getBackBtn().nativeElement.click();

      expect(component.back).toHaveBeenCalled();
    });
  });
});