]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { Component, NgModule, NO_ERRORS_SCHEMA, TemplateRef, ViewChild } from '@angular/core'; |
2 | import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; | |
3 | import { NgForm, ReactiveFormsModule } from '@angular/forms'; | |
4 | import { By } from '@angular/platform-browser'; | |
5 | ||
6 | import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap/modal'; | |
7 | import { Observable, Subscriber, timer as observableTimer } from 'rxjs'; | |
8 | ||
9f95a23c | 9 | import { configureTestBed, modalServiceShow } from '../../../../testing/unit-test-helper'; |
11fdf7f2 TL |
10 | import { DirectivesModule } from '../../directives/directives.module'; |
11 | import { CriticalConfirmationModalComponent } from './critical-confirmation-modal.component'; | |
12 | ||
13 | @NgModule({ | |
14 | entryComponents: [CriticalConfirmationModalComponent] | |
15 | }) | |
16 | export class MockModule {} | |
17 | ||
18 | @Component({ | |
19 | template: ` | |
9f95a23c TL |
20 | <button type="button" class="btn btn-secondary" (click)="openCtrlDriven()"> |
21 | <i class="fa fa-times"></i>Deletion Ctrl-Test | |
11fdf7f2 TL |
22 | <ng-template #ctrlDescription> |
23 | The spinner is handled by the controller if you have use the modal as ViewChild in order to | |
24 | use it's functions to stop the spinner or close the dialog. | |
25 | </ng-template> | |
26 | </button> | |
27 | ||
9f95a23c TL |
28 | <button type="button" class="btn btn-secondary" (click)="openModalDriven()"> |
29 | <i class="fa fa-times"></i>Deletion Modal-Test | |
11fdf7f2 TL |
30 | <ng-template #modalDescription> |
31 | The spinner is handled by the modal if your given deletion function returns a Observable. | |
32 | </ng-template> | |
33 | </button> | |
34 | ` | |
35 | }) | |
36 | class MockComponent { | |
9f95a23c | 37 | @ViewChild('ctrlDescription', { static: true }) |
11fdf7f2 | 38 | ctrlDescription: TemplateRef<any>; |
9f95a23c | 39 | @ViewChild('modalDescription', { static: true }) |
11fdf7f2 TL |
40 | modalDescription: TemplateRef<any>; |
41 | someData = [1, 2, 3, 4, 5]; | |
42 | finished: number[]; | |
43 | ctrlRef: BsModalRef; | |
44 | modalRef: BsModalRef; | |
45 | ||
46 | // Normally private - public was needed for the tests | |
47 | constructor(public modalService: BsModalService) {} | |
48 | ||
49 | openCtrlDriven() { | |
50 | this.ctrlRef = this.modalService.show(CriticalConfirmationModalComponent, { | |
51 | initialState: { | |
52 | submitAction: this.fakeDeleteController.bind(this), | |
53 | bodyTemplate: this.ctrlDescription | |
54 | } | |
55 | }); | |
56 | } | |
57 | ||
58 | openModalDriven() { | |
59 | this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, { | |
60 | initialState: { | |
61 | submitActionObservable: this.fakeDelete(), | |
62 | bodyTemplate: this.modalDescription | |
63 | } | |
64 | }); | |
65 | } | |
66 | ||
67 | finish() { | |
68 | this.finished = [6, 7, 8, 9]; | |
69 | } | |
70 | ||
71 | fakeDelete() { | |
72 | return (): Observable<any> => { | |
73 | return new Observable((observer: Subscriber<any>) => { | |
74 | observableTimer(100).subscribe(() => { | |
75 | observer.next(this.finish()); | |
76 | observer.complete(); | |
77 | }); | |
78 | }); | |
79 | }; | |
80 | } | |
81 | ||
82 | fakeDeleteController() { | |
83 | observableTimer(100).subscribe(() => { | |
84 | this.finish(); | |
85 | this.ctrlRef.hide(); | |
86 | }); | |
87 | } | |
88 | } | |
89 | ||
90 | describe('CriticalConfirmationModalComponent', () => { | |
91 | let mockComponent: MockComponent; | |
92 | let component: CriticalConfirmationModalComponent; | |
93 | let mockFixture: ComponentFixture<MockComponent>; | |
94 | let fixture: ComponentFixture<CriticalConfirmationModalComponent>; | |
95 | ||
96 | configureTestBed({ | |
97 | declarations: [MockComponent, CriticalConfirmationModalComponent], | |
98 | schemas: [NO_ERRORS_SCHEMA], | |
99 | imports: [ModalModule.forRoot(), ReactiveFormsModule, MockModule, DirectivesModule], | |
100 | providers: [BsModalRef] | |
101 | }); | |
102 | ||
103 | beforeEach(() => { | |
104 | mockFixture = TestBed.createComponent(MockComponent); | |
105 | mockComponent = mockFixture.componentInstance; | |
11fdf7f2 | 106 | spyOn(mockComponent.modalService, 'show').and.callFake((_modalComp, config) => { |
9f95a23c TL |
107 | const data = modalServiceShow(CriticalConfirmationModalComponent, config); |
108 | fixture = data.fixture; | |
109 | component = data.component; | |
110 | return data.ref; | |
11fdf7f2 TL |
111 | }); |
112 | mockComponent.openCtrlDriven(); | |
113 | mockFixture.detectChanges(); | |
114 | }); | |
115 | ||
116 | it('should create', () => { | |
117 | expect(component).toBeTruthy(); | |
118 | }); | |
119 | ||
9f95a23c | 120 | it('should focus the checkbox form field', (done) => { |
11fdf7f2 TL |
121 | fixture.detectChanges(); |
122 | fixture.whenStable().then(() => { | |
123 | const focused = fixture.debugElement.query(By.css(':focus')); | |
124 | expect(focused.attributes.id).toBe('confirmation'); | |
125 | expect(focused.attributes.type).toBe('checkbox'); | |
126 | const element = document.getElementById('confirmation'); | |
127 | expect(element === document.activeElement).toBeTruthy(); | |
9f95a23c | 128 | done(); |
11fdf7f2 TL |
129 | }); |
130 | }); | |
131 | ||
132 | it('should throw an error if no action is defined', () => { | |
133 | component = Object.assign(component, { | |
134 | submitAction: null, | |
135 | submitActionObservable: null | |
136 | }); | |
137 | expect(() => component.ngOnInit()).toThrowError('No submit action defined'); | |
138 | }); | |
139 | ||
140 | it('should test if the ctrl driven mock is set correctly through mock component', () => { | |
141 | expect(component.bodyTemplate).toBeTruthy(); | |
142 | expect(component.submitAction).toBeTruthy(); | |
143 | expect(component.submitActionObservable).not.toBeTruthy(); | |
144 | }); | |
145 | ||
146 | it('should test if the modal driven mock is set correctly through mock component', () => { | |
147 | mockComponent.openModalDriven(); | |
148 | expect(component.bodyTemplate).toBeTruthy(); | |
149 | expect(component.submitActionObservable).toBeTruthy(); | |
150 | expect(component.submitAction).not.toBeTruthy(); | |
151 | }); | |
152 | ||
153 | describe('component functions', () => { | |
9f95a23c | 154 | const changeValue = (value: boolean) => { |
11fdf7f2 TL |
155 | const ctrl = component.deletionForm.get('confirmation'); |
156 | ctrl.setValue(value); | |
157 | ctrl.markAsDirty(); | |
158 | ctrl.updateValueAndValidity(); | |
159 | fixture.detectChanges(); | |
160 | }; | |
161 | ||
162 | it('should test hideModal', () => { | |
163 | expect(component.modalRef).toBeTruthy(); | |
164 | expect(component.hideModal).toBeTruthy(); | |
165 | spyOn(component.modalRef, 'hide').and.callThrough(); | |
166 | expect(component.modalRef.hide).not.toHaveBeenCalled(); | |
167 | component.hideModal(); | |
168 | expect(component.modalRef.hide).toHaveBeenCalled(); | |
169 | }); | |
170 | ||
171 | describe('validate confirmation', () => { | |
172 | const testValidation = (submitted: boolean, error: string, expected: boolean) => { | |
173 | expect( | |
174 | component.deletionForm.showError('confirmation', <NgForm>{ submitted: submitted }, error) | |
175 | ).toBe(expected); | |
176 | }; | |
177 | ||
178 | beforeEach(() => { | |
179 | component.deletionForm.reset(); | |
180 | }); | |
181 | ||
182 | it('should test empty values', () => { | |
183 | component.deletionForm.reset(); | |
184 | testValidation(false, undefined, false); | |
185 | testValidation(true, 'required', true); | |
186 | component.deletionForm.reset(); | |
187 | changeValue(true); | |
188 | changeValue(false); | |
189 | testValidation(true, 'required', true); | |
190 | }); | |
191 | }); | |
192 | ||
193 | describe('deletion call', () => { | |
194 | beforeEach(() => { | |
195 | spyOn(component, 'stopLoadingSpinner').and.callThrough(); | |
196 | spyOn(component, 'hideModal').and.callThrough(); | |
197 | }); | |
198 | ||
199 | describe('Controller driven', () => { | |
200 | beforeEach(() => { | |
201 | spyOn(component, 'submitAction').and.callThrough(); | |
202 | spyOn(mockComponent.ctrlRef, 'hide').and.callThrough(); | |
203 | }); | |
204 | ||
801d1391 | 205 | it('should test fake deletion that closes modal', fakeAsync(() => { |
11fdf7f2 TL |
206 | // Before deletionCall |
207 | expect(component.submitAction).not.toHaveBeenCalled(); | |
208 | // During deletionCall | |
209 | component.callSubmitAction(); | |
210 | expect(component.stopLoadingSpinner).not.toHaveBeenCalled(); | |
211 | expect(component.hideModal).not.toHaveBeenCalled(); | |
212 | expect(mockComponent.ctrlRef.hide).not.toHaveBeenCalled(); | |
213 | expect(component.submitAction).toHaveBeenCalled(); | |
214 | expect(mockComponent.finished).toBe(undefined); | |
215 | // After deletionCall | |
216 | tick(2000); | |
217 | expect(component.hideModal).not.toHaveBeenCalled(); | |
218 | expect(mockComponent.ctrlRef.hide).toHaveBeenCalled(); | |
219 | expect(mockComponent.finished).toEqual([6, 7, 8, 9]); | |
220 | })); | |
221 | }); | |
222 | ||
223 | describe('Modal driven', () => { | |
224 | beforeEach(() => { | |
225 | mockComponent.openModalDriven(); | |
226 | spyOn(component, 'stopLoadingSpinner').and.callThrough(); | |
227 | spyOn(component, 'hideModal').and.callThrough(); | |
228 | spyOn(mockComponent, 'fakeDelete').and.callThrough(); | |
229 | }); | |
230 | ||
801d1391 | 231 | it('should delete and close modal', fakeAsync(() => { |
11fdf7f2 TL |
232 | // During deletionCall |
233 | component.callSubmitAction(); | |
234 | expect(mockComponent.finished).toBe(undefined); | |
235 | expect(component.hideModal).not.toHaveBeenCalled(); | |
236 | // After deletionCall | |
237 | tick(2000); | |
238 | expect(mockComponent.finished).toEqual([6, 7, 8, 9]); | |
239 | expect(component.stopLoadingSpinner).not.toHaveBeenCalled(); | |
240 | expect(component.hideModal).toHaveBeenCalled(); | |
241 | })); | |
242 | }); | |
243 | }); | |
244 | }); | |
245 | }); |