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