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