]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
f25c1ecdcd944183c88fb6a9ebfb1de3a8490ecb
[ceph-ci.git] /
1 import { Component, NgModule, NO_ERRORS_SCHEMA, TemplateRef, ViewChild } from '@angular/core';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { ReactiveFormsModule } from '@angular/forms';
4 import { RouterTestingModule } from '@angular/router/testing';
5
6 import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap/modal';
7
8 import { By } from '@angular/platform-browser';
9 import {
10   configureTestBed,
11   FixtureHelper,
12   i18nProviders,
13   modalServiceShow
14 } from '../../../../testing/unit-test-helper';
15 import { BackButtonComponent } from '../back-button/back-button.component';
16 import { ModalComponent } from '../modal/modal.component';
17 import { SubmitButtonComponent } from '../submit-button/submit-button.component';
18 import { ConfirmationModalComponent } from './confirmation-modal.component';
19
20 @NgModule({
21   entryComponents: [ConfirmationModalComponent]
22 })
23 export class MockModule {}
24
25 @Component({
26   template: `
27     <ng-template #fillTpl>
28       Template based description.
29     </ng-template>
30   `
31 })
32 class MockComponent {
33   @ViewChild('fillTpl', { static: true })
34   fillTpl: TemplateRef<any>;
35   modalRef: BsModalRef;
36   returnValue: any;
37
38   // Normally private, but public is needed by tests
39   constructor(public modalService: BsModalService) {}
40
41   private openModal(extendBaseState = {}) {
42     this.modalRef = this.modalService.show(ConfirmationModalComponent, {
43       initialState: Object.assign(
44         {
45           titleText: 'Title is a must have',
46           buttonText: 'Action label',
47           bodyTpl: this.fillTpl,
48           description: 'String based description.',
49           onSubmit: () => {
50             this.returnValue = 'The submit action has to hide manually.';
51             this.modalRef.hide();
52           }
53         },
54         extendBaseState
55       )
56     });
57   }
58
59   basicModal() {
60     this.openModal();
61   }
62
63   customCancelModal() {
64     this.openModal({
65       onCancel: () => (this.returnValue = 'If you have todo something besides hiding the modal.')
66     });
67   }
68 }
69
70 describe('ConfirmationModalComponent', () => {
71   let component: ConfirmationModalComponent;
72   let fixture: ComponentFixture<ConfirmationModalComponent>;
73   let mockComponent: MockComponent;
74   let mockFixture: ComponentFixture<MockComponent>;
75   let modalService: BsModalService;
76   let fh: FixtureHelper;
77
78   /**
79    * The hide method of `BsModalService` doesn't emit events during tests that's why it's mocked.
80    *
81    * The only events of hide are `null`, `'backdrop-click'` and `'esc'` as described here:
82    * https://ngx-universal.herokuapp.com/#/modals#service-events
83    */
84   const hide = (x: string) => modalService.onHide.emit(null || x);
85
86   const expectReturnValue = (v: string) => expect(mockComponent.returnValue).toBe(v);
87
88   configureTestBed({
89     declarations: [
90       ConfirmationModalComponent,
91       BackButtonComponent,
92       MockComponent,
93       ModalComponent,
94       SubmitButtonComponent
95     ],
96     schemas: [NO_ERRORS_SCHEMA],
97     imports: [ModalModule.forRoot(), ReactiveFormsModule, MockModule, RouterTestingModule],
98     providers: [BsModalRef, i18nProviders, SubmitButtonComponent]
99   });
100
101   beforeEach(() => {
102     fh = new FixtureHelper();
103     mockFixture = TestBed.createComponent(MockComponent);
104     mockComponent = mockFixture.componentInstance;
105     mockFixture.detectChanges();
106     modalService = TestBed.get(BsModalService);
107     spyOn(modalService, 'show').and.callFake((_modalComp, config) => {
108       const data = modalServiceShow(ConfirmationModalComponent, config);
109       fixture = data.fixture;
110       component = data.component;
111       spyOn(component.modalRef, 'hide').and.callFake(hide);
112       fh.updateFixture(fixture);
113       return data.ref;
114     });
115   });
116
117   it('should create', () => {
118     mockComponent.basicModal();
119     expect(component).toBeTruthy();
120   });
121
122   describe('Throws errors', () => {
123     const expectError = (config: object, expected: string) => {
124       mockComponent.basicModal();
125       component = Object.assign(component, config);
126       expect(() => component.ngOnInit()).toThrowError(expected);
127     };
128
129     it('has no submit action defined', () => {
130       expectError(
131         {
132           onSubmit: undefined
133         },
134         'No submit action defined'
135       );
136     });
137
138     it('has no title defined', () => {
139       expectError(
140         {
141           titleText: undefined
142         },
143         'No title defined'
144       );
145     });
146
147     it('has no action name defined', () => {
148       expectError(
149         {
150           buttonText: undefined
151         },
152         'No action name defined'
153       );
154     });
155
156     it('has no description defined', () => {
157       expectError(
158         {
159           bodyTpl: undefined,
160           description: undefined
161         },
162         'No description defined'
163       );
164     });
165   });
166
167   describe('basics', () => {
168     beforeEach(() => {
169       mockComponent.basicModal();
170       spyOn(mockComponent.modalRef, 'hide').and.callFake(hide);
171     });
172
173     it('should show the correct title', () => {
174       expect(fh.getText('.modal-title')).toBe('Title is a must have');
175     });
176
177     it('should show the correct action name', () => {
178       expect(fh.getText('.tc_submitButton')).toBe('Action label');
179     });
180
181     it('should use the correct submit action', () => {
182       // In order to ignore the `ElementRef` usage of `SubmitButtonComponent`
183       spyOn(
184         fixture.debugElement.query(By.directive(SubmitButtonComponent)).componentInstance,
185         'focusButton'
186       );
187       fh.clickElement('.tc_submitButton');
188       expect(mockComponent.modalRef.hide).toHaveBeenCalledTimes(1);
189       expect(component.modalRef.hide).toHaveBeenCalledTimes(0);
190       expectReturnValue('The submit action has to hide manually.');
191     });
192
193     it('should use the default cancel action', () => {
194       fh.clickElement('.tc_backButton');
195       expect(mockComponent.modalRef.hide).toHaveBeenCalledTimes(0);
196       expect(component.modalRef.hide).toHaveBeenCalledTimes(1);
197       expectReturnValue(undefined);
198     });
199
200     it('should show the description', () => {
201       expect(fh.getText('.modal-body')).toBe(
202         'Template based description.  String based description.'
203       );
204     });
205   });
206
207   describe('custom cancel action', () => {
208     const expectCancelValue = () =>
209       expectReturnValue('If you have todo something besides hiding the modal.');
210
211     beforeEach(() => {
212       mockComponent.customCancelModal();
213     });
214
215     it('should use custom cancel action', () => {
216       fh.clickElement('.tc_backButton');
217       expectCancelValue();
218     });
219
220     it('should use custom cancel action if escape was pressed', () => {
221       hide('esc');
222       expectCancelValue();
223     });
224
225     it('should use custom cancel action if clicked outside the modal', () => {
226       hide('backdrop-click');
227       expectCancelValue();
228     });
229
230     it('should unsubscribe on destroy', () => {
231       hide('backdrop-click');
232       expectCancelValue();
233       const s = 'This value will not be changed.';
234       mockComponent.returnValue = s;
235       component.ngOnDestroy();
236       hide('backdrop-click');
237       expectReturnValue(s);
238     });
239   });
240 });