+import { Component, NgModule, NO_ERRORS_SCHEMA, TemplateRef, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
-import { BsModalRef } from 'ngx-bootstrap/modal';
+import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap/modal';
-import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
+import { By } from '@angular/platform-browser';
+import {
+ configureTestBed,
+ FixtureHelper,
+ i18nProviders,
+ modalServiceShow
+} from '../../../../testing/unit-test-helper';
import { BackButtonComponent } from '../back-button/back-button.component';
import { ModalComponent } from '../modal/modal.component';
import { SubmitButtonComponent } from '../submit-button/submit-button.component';
import { ConfirmationModalComponent } from './confirmation-modal.component';
+@NgModule({
+ entryComponents: [ConfirmationModalComponent]
+})
+export class MockModule {}
+
+@Component({
+ template: `
+ <ng-template #fillTpl>
+ The description of the confirmation modal is mandatory.
+ </ng-template>
+ `
+})
+class MockComponent {
+ @ViewChild('fillTpl', { static: true })
+ fillTpl: TemplateRef<any>;
+ modalRef: BsModalRef;
+ returnValue: any;
+
+ // Normally private, but public is needed by tests
+ constructor(public modalService: BsModalService) {}
+
+ private openModal(extendBaseState: object = {}) {
+ this.modalRef = this.modalService.show(ConfirmationModalComponent, {
+ initialState: Object.assign(
+ {
+ titleText: 'Title is a must have',
+ buttonText: 'Action label',
+ bodyTpl: this.fillTpl,
+ onSubmit: () => {
+ this.returnValue = 'The submit action has to hide manually.';
+ this.modalRef.hide();
+ }
+ },
+ extendBaseState
+ )
+ });
+ }
+
+ basicModal() {
+ this.openModal();
+ }
+}
+
describe('ConfirmationModalComponent', () => {
let component: ConfirmationModalComponent;
let fixture: ComponentFixture<ConfirmationModalComponent>;
+ let mockComponent: MockComponent;
+ let mockFixture: ComponentFixture<MockComponent>;
+ let modalService: BsModalService;
+ let fh: FixtureHelper;
+
+ /**
+ * The hide method of `BsModalService` doesn't emit events during tests that's why it's mocked.
+ *
+ * The only events of hide are `null`, `'backdrop-click'` and `'esc'` as described here:
+ * https://ngx-universal.herokuapp.com/#/modals#service-events
+ */
+ const hide = (x: string) => modalService.onHide.emit(null || x);
+
+ const expectReturnValue = (v: string) => expect(mockComponent.returnValue).toBe(v);
configureTestBed({
declarations: [
ConfirmationModalComponent,
BackButtonComponent,
- SubmitButtonComponent,
- ModalComponent
+ MockComponent,
+ ModalComponent,
+ SubmitButtonComponent
],
- imports: [ReactiveFormsModule, RouterTestingModule],
- providers: [BsModalRef, i18nProviders]
+ schemas: [NO_ERRORS_SCHEMA],
+ imports: [ModalModule.forRoot(), ReactiveFormsModule, MockModule, RouterTestingModule],
+ providers: [BsModalRef, i18nProviders, SubmitButtonComponent]
});
beforeEach(() => {
- fixture = TestBed.createComponent(ConfirmationModalComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
+ fh = new FixtureHelper();
+ mockFixture = TestBed.createComponent(MockComponent);
+ mockComponent = mockFixture.componentInstance;
+ mockFixture.detectChanges();
+ modalService = TestBed.get(BsModalService);
+ spyOn(modalService, 'show').and.callFake((_modalComp, config) => {
+ const data = modalServiceShow(ConfirmationModalComponent, config);
+ fixture = data.fixture;
+ component = data.component;
+ spyOn(component.modalRef, 'hide').and.callFake(hide);
+ fh.updateFixture(fixture);
+ return data.ref;
+ });
});
it('should create', () => {
+ mockComponent.basicModal();
expect(component).toBeTruthy();
});
+
+ describe('Throws errors', () => {
+ const expectError = (config: object, expected: string) => {
+ mockComponent.basicModal();
+ component = Object.assign(component, config);
+ expect(() => component.ngOnInit()).toThrowError(expected);
+ };
+
+ it('has no submit action defined', () => {
+ expectError(
+ {
+ onSubmit: undefined
+ },
+ 'No submit action defined'
+ );
+ });
+
+ it('has no title defined', () => {
+ expectError(
+ {
+ titleText: undefined
+ },
+ 'No title defined'
+ );
+ });
+
+ it('has no action name defined', () => {
+ expectError(
+ {
+ buttonText: undefined
+ },
+ 'No action name defined'
+ );
+ });
+
+ it('has no description defined', () => {
+ expectError(
+ {
+ bodyTpl: undefined
+ },
+ 'No description defined'
+ );
+ });
+ });
+
+ describe('basics', () => {
+ beforeEach(() => {
+ mockComponent.basicModal();
+ spyOn(mockComponent.modalRef, 'hide').and.callFake(hide);
+ });
+
+ it('should show the correct title', () => {
+ expect(fh.getText('.modal-title')).toBe('Title is a must have');
+ });
+
+ it('should show the correct action name', () => {
+ expect(fh.getText('.tc_submitButton')).toBe('Action label');
+ });
+
+ it('should use the correct submit action', () => {
+ // In order to ignore the `ElementRef` usage of `SubmitButtonComponent`
+ spyOn(
+ fixture.debugElement.query(By.directive(SubmitButtonComponent)).componentInstance,
+ 'focusButton'
+ );
+ fh.clickElement('.tc_submitButton');
+ expect(mockComponent.modalRef.hide).toHaveBeenCalledTimes(1);
+ expect(component.modalRef.hide).toHaveBeenCalledTimes(0);
+ expectReturnValue('The submit action has to hide manually.');
+ });
+
+ it('should use the default cancel action', () => {
+ fh.clickElement('.tc_backButton');
+ expect(mockComponent.modalRef.hide).toHaveBeenCalledTimes(0);
+ expect(component.modalRef.hide).toHaveBeenCalledTimes(1);
+ expectReturnValue(undefined);
+ });
+
+ it('should show the description', () => {
+ expect(fh.getText('.modal-body')).toBe(
+ 'The description of the confirmation modal is mandatory.'
+ );
+ });
+ });
});
import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap/modal';
import { Observable, Subscriber, timer as observableTimer } from 'rxjs';
-import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { configureTestBed, modalServiceShow } from '../../../../testing/unit-test-helper';
import { DirectivesModule } from '../../directives/directives.module';
import { CriticalConfirmationModalComponent } from './critical-confirmation-modal.component';
beforeEach(() => {
mockFixture = TestBed.createComponent(MockComponent);
mockComponent = mockFixture.componentInstance;
- // Mocking the modals as a lot would be left over
spyOn(mockComponent.modalService, 'show').and.callFake((_modalComp, config) => {
- const ref = new BsModalRef();
- fixture = TestBed.createComponent(CriticalConfirmationModalComponent);
- component = fixture.componentInstance;
- if (config.initialState) {
- component = Object.assign(component, config.initialState);
- }
- fixture.detectChanges();
- ref.content = component;
- return ref;
+ const data = modalServiceShow(CriticalConfirmationModalComponent, config);
+ fixture = data.fixture;
+ component = data.component;
+ return data.ref;
});
mockComponent.openCtrlDriven();
mockFixture.detectChanges();
-import { LOCALE_ID, TRANSLATIONS, TRANSLATIONS_FORMAT } from '@angular/core';
+import { LOCALE_ID, TRANSLATIONS, TRANSLATIONS_FORMAT, Type } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AbstractControl } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { I18n } from '@ngx-translate/i18n-polyfill';
+import { BsModalRef } from 'ngx-bootstrap/modal';
import { TableActionsComponent } from '../app/shared/datatable/table-actions/table-actions.component';
import { Icons } from '../app/shared/enum/icons.enum';
}
}
+/**
+ * Use this to mock 'ModalService.show' to make the embedded component with it's fixture usable
+ * in tests. The function gives back all needed parts including the modal reference.
+ *
+ * Please make sure to call this function *inside* your mock and return the reference at the end.
+ */
+export function modalServiceShow(componentClass: Type<any>, modalConfig) {
+ const ref = new BsModalRef();
+ const fixture = TestBed.createComponent(componentClass);
+ let component = fixture.componentInstance;
+ if (modalConfig.initialState) {
+ component = Object.assign(component, modalConfig.initialState);
+ }
+ fixture.detectChanges();
+ ref.content = component;
+ return { ref, fixture, component };
+}
+
export class FixtureHelper {
fixture: ComponentFixture<any>;
- constructor(fixture: ComponentFixture<any>) {
+ constructor(fixture?: ComponentFixture<any>) {
+ if (fixture) {
+ this.updateFixture(fixture);
+ }
+ }
+
+ updateFixture(fixture: ComponentFixture<any>) {
this.fixture = fixture;
}