1 import { EventEmitter } from '@angular/core';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { ReactiveFormsModule } from '@angular/forms';
4 import { By } from '@angular/platform-browser';
6 import { DirectivesModule } from '~/app/shared/directives/directives.module';
7 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
8 import { RbdConfigurationSourceField } from '~/app/shared/models/configuration';
9 import { DimlessBinaryPerSecondPipe } from '~/app/shared/pipes/dimless-binary-per-second.pipe';
10 import { FormatterService } from '~/app/shared/services/formatter.service';
11 import { RbdConfigurationService } from '~/app/shared/services/rbd-configuration.service';
12 import { SharedModule } from '~/app/shared/shared.module';
13 import { configureTestBed, FormHelper } from '~/testing/unit-test-helper';
14 import { RbdConfigurationFormComponent } from './rbd-configuration-form.component';
16 describe('RbdConfigurationFormComponent', () => {
17 let component: RbdConfigurationFormComponent;
18 let fixture: ComponentFixture<RbdConfigurationFormComponent>;
23 imports: [ReactiveFormsModule, DirectivesModule, SharedModule],
24 declarations: [RbdConfigurationFormComponent],
25 providers: [RbdConfigurationService, FormatterService, DimlessBinaryPerSecondPipe]
29 fixture = TestBed.createComponent(RbdConfigurationFormComponent);
30 component = fixture.componentInstance;
31 component.form = new CdFormGroup({}, null);
32 fh = new FormHelper(component.form);
33 fixture.detectChanges();
34 sections = TestBed.inject(RbdConfigurationService).sections;
37 it('should create', () => {
38 expect(component).toBeTruthy();
41 it('should create all form fields mentioned in RbdConfiguration::OPTIONS', () => {
42 /* Test form creation on a TypeScript level */
43 const actual = Object.keys((component.form.get('configuration') as CdFormGroup).controls);
44 const expected = sections
45 .map((section) => section.options)
46 .reduce((a, b) => a.concat(b))
47 .map((option: Record<string, any>) => option.name);
48 expect(actual).toEqual(expected);
50 /* Test form creation on a template level */
51 const controlDebugElements = fixture.debugElement.queryAll(By.css('input.form-control'));
52 expect(controlDebugElements.length).toBe(expected.length);
53 controlDebugElements.forEach((element) => expect(element.nativeElement).toBeTruthy());
56 it('should only contain values of changed controls if submitted', () => {
58 component.changes.subscribe((getDirtyValues: Function) => {
59 values = getDirtyValues();
61 fh.setValue('configuration.rbd_qos_bps_limit', 0, true);
62 fixture.detectChanges();
64 expect(values).toEqual({ rbd_qos_bps_limit: 0 });
67 describe('test loading of initial data for editing', () => {
69 component.initializeData = new EventEmitter<any>();
70 fixture.detectChanges();
74 it('should return dirty values without any units', () => {
76 component.changes.subscribe((getDirtyValues: Function) => {
77 dirtyValues = getDirtyValues();
80 fh.setValue('configuration.rbd_qos_bps_limit', 55, true);
81 fh.setValue('configuration.rbd_qos_iops_limit', 22, true);
83 expect(dirtyValues['rbd_qos_bps_limit']).toBe(55);
84 expect(dirtyValues['rbd_qos_iops_limit']).toBe(22);
87 it('should load initial data into forms', () => {
88 component.initializeData.emit({
91 name: 'rbd_qos_bps_limit',
96 sourceType: RbdConfigurationSourceField.pool
99 expect(component.form.getValue('configuration.rbd_qos_bps_limit')).toEqual('55 B/s');
102 it('should not load initial data if the source is not the pool itself', () => {
103 component.initializeData.emit({
106 name: 'rbd_qos_bps_limit',
108 source: RbdConfigurationSourceField.image
111 name: 'rbd_qos_iops_limit',
113 source: RbdConfigurationSourceField.global
116 sourceType: RbdConfigurationSourceField.pool
119 expect(component.form.getValue('configuration.rbd_qos_iops_limit')).toEqual('0 IOPS');
120 expect(component.form.getValue('configuration.rbd_qos_bps_limit')).toEqual('0 B/s');
123 it('should not load initial data if the source is not the image itself', () => {
124 component.initializeData.emit({
127 name: 'rbd_qos_bps_limit',
129 source: RbdConfigurationSourceField.pool
132 name: 'rbd_qos_iops_limit',
134 source: RbdConfigurationSourceField.global
137 sourceType: RbdConfigurationSourceField.image
140 expect(component.form.getValue('configuration.rbd_qos_iops_limit')).toEqual('0 IOPS');
141 expect(component.form.getValue('configuration.rbd_qos_bps_limit')).toEqual('0 B/s');
144 it('should always have formatted results', () => {
145 component.initializeData.emit({
148 name: 'rbd_qos_bps_limit',
150 source: RbdConfigurationSourceField.image
153 name: 'rbd_qos_iops_limit',
155 source: RbdConfigurationSourceField.image
158 name: 'rbd_qos_read_bps_limit',
159 value: null, // incorrect type
160 source: RbdConfigurationSourceField.image
163 name: 'rbd_qos_read_bps_limit',
164 value: undefined, // incorrect type
165 source: RbdConfigurationSourceField.image
168 sourceType: RbdConfigurationSourceField.image
171 expect(component.form.getValue('configuration.rbd_qos_iops_limit')).toEqual('22 IOPS');
172 expect(component.form.getValue('configuration.rbd_qos_bps_limit')).toEqual('55 B/s');
173 expect(component.form.getValue('configuration.rbd_qos_read_bps_limit')).toEqual('0 B/s');
174 expect(component.form.getValue('configuration.rbd_qos_read_bps_limit')).toEqual('0 B/s');
178 it('should reset the corresponding form field correctly', () => {
179 const fieldName = 'rbd_qos_bps_limit';
180 const getValue = () => component.form.get(`configuration.${fieldName}`).value;
183 fh.setValue(`configuration.${fieldName}`, 418, true);
184 expect(getValue()).toBe(418);
187 component.reset(fieldName);
188 expect(getValue()).toBe(null);
191 component.reset(fieldName);
192 expect(getValue()).toBe(418);
195 component.reset(fieldName);
196 expect(getValue()).toBe(null);
199 component.reset(fieldName);
200 expect(getValue()).toBe(418);
203 describe('should verify that getDirtyValues() returns correctly', () => {
207 component.initializeData = new EventEmitter<any>();
208 fixture.detectChanges();
209 component.ngOnInit();
213 name: 'rbd_qos_bps_limit',
215 source: RbdConfigurationSourceField.image
218 name: 'rbd_qos_iops_limit',
220 source: RbdConfigurationSourceField.image
223 name: 'rbd_qos_read_bps_limit',
225 source: RbdConfigurationSourceField.image
228 name: 'rbd_qos_read_iops_limit',
230 source: RbdConfigurationSourceField.image
233 name: 'rbd_qos_read_iops_burst',
235 source: RbdConfigurationSourceField.image
238 name: 'rbd_qos_write_bps_burst',
240 source: RbdConfigurationSourceField.global
243 name: 'rbd_qos_write_iops_burst',
245 source: RbdConfigurationSourceField.global
248 sourceType: RbdConfigurationSourceField.image
250 component.initializeData.emit(data);
253 it('should return an empty object', () => {
254 expect(component.getDirtyValues()).toEqual({});
255 expect(component.getDirtyValues(true, RbdConfigurationSourceField.image)).toEqual({});
258 it('should return dirty values', () => {
259 component.form.get('configuration.rbd_qos_write_bps_burst').markAsDirty();
260 expect(component.getDirtyValues()).toEqual({ rbd_qos_write_bps_burst: 0 });
262 component.form.get('configuration.rbd_qos_write_iops_burst').markAsDirty();
263 expect(component.getDirtyValues()).toEqual({
264 rbd_qos_write_iops_burst: 0,
265 rbd_qos_write_bps_burst: 0
269 it('should also return all local values if they do not contain their initial values', () => {
270 // Change value for all options
271 data.initialData = data.initialData.map((o: Record<string, any>) => {
277 ['rbd_qos_read_iops_limit', 'rbd_qos_write_bps_burst'].forEach((option) => {
278 component.form.get(`configuration.${option}`).markAsDirty();
281 expect(component.getDirtyValues(true, RbdConfigurationSourceField.image)).toEqual({
282 rbd_qos_read_iops_limit: 0,
283 rbd_qos_write_bps_burst: 0
287 it('should throw an error if used incorrectly', () => {
288 expect(() => component.getDirtyValues(true)).toThrowError(
289 /^ProgrammingError: If local values shall be included/