]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / testing / unit-test-helper.ts
1 import { LOCALE_ID, TRANSLATIONS, TRANSLATIONS_FORMAT } from '@angular/core';
2 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 import { AbstractControl } from '@angular/forms';
4 import { By } from '@angular/platform-browser';
5
6 import { I18n } from '@ngx-translate/i18n-polyfill';
7 import * as _ from 'lodash';
8
9 import { TableActionsComponent } from '../app/shared/datatable/table-actions/table-actions.component';
10 import { CdFormGroup } from '../app/shared/forms/cd-form-group';
11 import { Permission } from '../app/shared/models/permissions';
12 import {
13 AlertmanagerAlert,
14 AlertmanagerNotification,
15 AlertmanagerNotificationAlert,
16 PrometheusRule
17 } from '../app/shared/models/prometheus-alerts';
18 import { _DEV_ } from '../unit-test-configuration';
19
20 export function configureTestBed(configuration, useOldMethod?) {
21 if (_DEV_ && !useOldMethod) {
22 const resetTestingModule = TestBed.resetTestingModule;
23 beforeAll((done) =>
24 (async () => {
25 TestBed.resetTestingModule();
26 TestBed.configureTestingModule(configuration);
27 // prevent Angular from resetting testing module
28 TestBed.resetTestingModule = () => TestBed;
29 })()
30 .then(done)
31 .catch(done.fail)
32 );
33 afterAll(() => {
34 TestBed.resetTestingModule = resetTestingModule;
35 });
36 } else {
37 beforeEach(async(() => {
38 TestBed.configureTestingModule(configuration);
39 }));
40 }
41 }
42
43 export class PermissionHelper {
44 tableActions: TableActionsComponent;
45 permission: Permission;
46 getTableActionComponent: () => TableActionsComponent;
47
48 constructor(permission: Permission, getTableActionComponent: () => TableActionsComponent) {
49 this.permission = permission;
50 this.getTableActionComponent = getTableActionComponent;
51 }
52
53 setPermissionsAndGetActions(
54 createPerm: number | boolean,
55 updatePerm: number | boolean,
56 deletePerm: number | boolean
57 ): TableActionsComponent {
58 this.permission.create = Boolean(createPerm);
59 this.permission.update = Boolean(updatePerm);
60 this.permission.delete = Boolean(deletePerm);
61 this.tableActions = this.getTableActionComponent();
62 return this.tableActions;
63 }
64
65 // Overwrite if needed
66 createSelection(): object {
67 return {};
68 }
69
70 testScenarios({
71 fn,
72 empty,
73 single,
74 singleExecuting,
75 multiple
76 }: {
77 fn: () => any;
78 empty: any;
79 single: any;
80 singleExecuting?: any; // uses 'single' if not defined
81 multiple?: any; // uses 'empty' if not defined
82 }) {
83 this.testScenario(
84 // 'multiple selections'
85 [this.createSelection(), this.createSelection()],
86 fn,
87 _.isUndefined(multiple) ? empty : multiple
88 );
89 const executing = this.createSelection();
90 executing['cdExecuting'] = 'someAction';
91 this.testScenario(
92 // 'select executing item'
93 [executing],
94 fn,
95 _.isUndefined(singleExecuting) ? single : singleExecuting
96 );
97 this.testScenario([this.createSelection()], fn, single); // 'select non-executing item'
98 this.testScenario([], fn, empty); // 'no selection'
99 }
100
101 private testScenario(selection: object[], fn: () => any, expected: any) {
102 this.setSelection(selection);
103 expect(fn()).toBe(expected);
104 }
105
106 setSelection(selection: object[]) {
107 this.tableActions.selection.selected = selection;
108 this.tableActions.selection.update();
109 }
110 }
111
112 export class FormHelper {
113 form: CdFormGroup;
114
115 constructor(form: CdFormGroup) {
116 this.form = form;
117 }
118
119 /**
120 * Changes multiple values in multiple controls
121 */
122 setMultipleValues(values: { [controlName: string]: any }, markAsDirty?: boolean) {
123 Object.keys(values).forEach((key) => {
124 this.setValue(key, values[key], markAsDirty);
125 });
126 }
127
128 /**
129 * Changes the value of a control
130 */
131 setValue(control: AbstractControl | string, value: any, markAsDirty?: boolean): AbstractControl {
132 control = this.getControl(control);
133 if (markAsDirty) {
134 control.markAsDirty();
135 }
136 control.setValue(value);
137 return control;
138 }
139
140 private getControl(control: AbstractControl | string): AbstractControl {
141 if (typeof control === 'string') {
142 return this.form.get(control);
143 }
144 return control;
145 }
146
147 /**
148 * Change the value of the control and expect the control to be valid afterwards.
149 */
150 expectValidChange(control: AbstractControl | string, value: any, markAsDirty?: boolean) {
151 this.expectValid(this.setValue(control, value, markAsDirty));
152 }
153
154 /**
155 * Expect that the given control is valid.
156 */
157 expectValid(control: AbstractControl | string) {
158 // 'isValid' would be false for disabled controls
159 expect(this.getControl(control).errors).toBe(null);
160 }
161
162 /**
163 * Change the value of the control and expect a specific error.
164 */
165 expectErrorChange(
166 control: AbstractControl | string,
167 value: any,
168 error: string,
169 markAsDirty?: boolean
170 ) {
171 this.expectError(this.setValue(control, value, markAsDirty), error);
172 }
173
174 /**
175 * Expect a specific error for the given control.
176 */
177 expectError(control: AbstractControl | string, error: string) {
178 expect(this.getControl(control).hasError(error)).toBeTruthy();
179 }
180 }
181
182 export class FixtureHelper {
183 fixture: ComponentFixture<any>;
184
185 constructor(fixture: ComponentFixture<any>) {
186 this.fixture = fixture;
187 }
188
189 /**
190 * Expect a list of id elements to be visible or not.
191 */
192 expectIdElementsVisible(ids: string[], visibility: boolean) {
193 ids.forEach((css) => {
194 this.expectElementVisible(`#${css}`, visibility);
195 });
196 }
197
198 /**
199 * Expect a specific element to be visible or not.
200 */
201 expectElementVisible(css: string, visibility: boolean) {
202 expect(Boolean(this.getElementByCss(css))).toBe(visibility);
203 }
204
205 expectFormFieldToBe(css: string, value: string) {
206 const props = this.getElementByCss(css).properties;
207 expect(props['value'] || props['checked'].toString()).toBe(value);
208 }
209
210 clickElement(css: string) {
211 this.getElementByCss(css).triggerEventHandler('click', null);
212 this.fixture.detectChanges();
213 }
214
215 getText(css: string) {
216 const e = this.getElementByCss(css);
217 return e ? e.nativeElement.textContent.trim() : null;
218 }
219
220 getElementByCss(css: string) {
221 this.fixture.detectChanges();
222 return this.fixture.debugElement.query(By.css(css));
223 }
224 }
225
226 export class PrometheusHelper {
227 createSilence(id) {
228 return {
229 id: id,
230 createdBy: `Creator of ${id}`,
231 comment: `A comment for ${id}`,
232 startsAt: new Date('2022-02-22T22:22:00').toISOString(),
233 endsAt: new Date('2022-02-23T22:22:00').toISOString(),
234 matchers: [
235 {
236 name: 'job',
237 value: 'someJob',
238 isRegex: true
239 }
240 ]
241 };
242 }
243
244 createRule(name, severity, alerts: any[]): PrometheusRule {
245 return {
246 name: name,
247 labels: {
248 severity: severity
249 },
250 alerts: alerts
251 } as PrometheusRule;
252 }
253
254 createAlert(name, state = 'active', timeMultiplier = 1): AlertmanagerAlert {
255 return {
256 fingerprint: name,
257 status: { state },
258 labels: {
259 alertname: name,
260 instance: 'someInstance',
261 job: 'someJob',
262 severity: 'someSeverity'
263 },
264 annotations: {
265 summary: `${name} is ${state}`
266 },
267 generatorURL: `http://${name}`,
268 startsAt: new Date(new Date('2022-02-22').getTime() * timeMultiplier).toString()
269 } as AlertmanagerAlert;
270 }
271
272 createNotificationAlert(name, status = 'firing'): AlertmanagerNotificationAlert {
273 return {
274 status: status,
275 labels: {
276 alertname: name
277 },
278 annotations: {
279 summary: `${name} is ${status}`
280 },
281 generatorURL: `http://${name}`
282 } as AlertmanagerNotificationAlert;
283 }
284
285 createNotification(alertNumber = 1, status = 'firing'): AlertmanagerNotification {
286 const alerts = [];
287 for (let i = 0; i < alertNumber; i++) {
288 alerts.push(this.createNotificationAlert('alert' + i, status));
289 }
290 return { alerts, status } as AlertmanagerNotification;
291 }
292
293 createLink(url) {
294 return `<a href="${url}" target="_blank"><i class="fa fa-line-chart"></i></a>`;
295 }
296 }
297
298 const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
299 <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
300 <file source-language="en" datatype="plaintext" original="ng2.template">
301 <body>
302 </body>
303 </file>
304 </xliff>
305 `;
306
307 const i18nProviders = [
308 { provide: TRANSLATIONS_FORMAT, useValue: 'xlf' },
309 { provide: TRANSLATIONS, useValue: XLIFF },
310 { provide: LOCALE_ID, useValue: 'en' },
311 I18n
312 ];
313
314 export { i18nProviders };