1 import { ComponentFixture, TestBed } from '@angular/core/testing';
2 import { ReactiveFormsModule } from '@angular/forms';
3 import { By } from '@angular/platform-browser';
5 import { ReplaySubject } from 'rxjs';
7 import { DirectivesModule } from '~/app/shared/directives/directives.module';
8 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
9 import { RbdConfigurationSourceField } from '~/app/shared/models/configuration';
10 import { DimlessBinaryPerSecondPipe } from '~/app/shared/pipes/dimless-binary-per-second.pipe';
11 import { FormatterService } from '~/app/shared/services/formatter.service';
12 import { RbdConfigurationService } from '~/app/shared/services/rbd-configuration.service';
13 import { SharedModule } from '~/app/shared/shared.module';
14 import { configureTestBed, FormHelper } from '~/testing/unit-test-helper';
15 import { RbdConfigurationFormComponent } from './rbd-configuration-form.component';
17 describe('RbdConfigurationFormComponent', () => {
18 let component: RbdConfigurationFormComponent;
19 let fixture: ComponentFixture<RbdConfigurationFormComponent>;
24 imports: [ReactiveFormsModule, DirectivesModule, SharedModule],
25 declarations: [RbdConfigurationFormComponent],
26 providers: [RbdConfigurationService, FormatterService, DimlessBinaryPerSecondPipe]
30 fixture = TestBed.createComponent(RbdConfigurationFormComponent);
31 component = fixture.componentInstance;
32 component.form = new CdFormGroup({}, null);
33 fh = new FormHelper(component.form);
34 fixture.detectChanges();
35 sections = TestBed.inject(RbdConfigurationService).sections;
38 it('should create', () => {
39 expect(component).toBeTruthy();
42 it('should create all form fields mentioned in RbdConfiguration::OPTIONS', () => {
43 /* Test form creation on a TypeScript level */
44 const actual = Object.keys((component.form.get('configuration') as CdFormGroup).controls);
45 const expected = sections
46 .map((section) => section.options)
47 .reduce((a, b) => a.concat(b))
48 .map((option: Record<string, any>) => option.name);
49 expect(actual).toEqual(expected);
51 /* Test form creation on a template level */
52 const controlDebugElements = fixture.debugElement.queryAll(By.css('input.form-control'));
53 expect(controlDebugElements.length).toBe(expected.length);
54 controlDebugElements.forEach((element) => expect(element.nativeElement).toBeTruthy());
57 it('should only contain values of changed controls if submitted', () => {
59 component.changes.subscribe((getDirtyValues: Function) => {
60 values = getDirtyValues();
62 fh.setValue('configuration.rbd_qos_bps_limit', 0, true);
63 fixture.detectChanges();
65 expect(values).toEqual({ rbd_qos_bps_limit: 0 });
68 describe('test loading of initial data for editing', () => {
70 component.initializeData = new ReplaySubject<any>(1);
71 fixture.detectChanges();
75 it('should return dirty values without any units', () => {
77 component.changes.subscribe((getDirtyValues: Function) => {
78 dirtyValues = getDirtyValues();
81 fh.setValue('configuration.rbd_qos_bps_limit', 55, true);
82 fh.setValue('configuration.rbd_qos_iops_limit', 22, true);
84 expect(dirtyValues['rbd_qos_bps_limit']).toBe(55);
85 expect(dirtyValues['rbd_qos_iops_limit']).toBe(22);
88 it('should load initial data into forms', () => {
89 component.initializeData.next({
92 name: 'rbd_qos_bps_limit',
97 sourceType: RbdConfigurationSourceField.pool
100 expect(component.form.getValue('configuration.rbd_qos_bps_limit')).toEqual('55 B/s');
103 it('should not load initial data if the source is not the pool itself', () => {
104 component.initializeData.next({
107 name: 'rbd_qos_bps_limit',
109 source: RbdConfigurationSourceField.image
112 name: 'rbd_qos_iops_limit',
114 source: RbdConfigurationSourceField.global
117 sourceType: RbdConfigurationSourceField.pool
120 expect(component.form.getValue('configuration.rbd_qos_iops_limit')).toEqual('0 IOPS');
121 expect(component.form.getValue('configuration.rbd_qos_bps_limit')).toEqual('0 B/s');
124 it('should not load initial data if the source is not the image itself', () => {
125 component.initializeData.next({
128 name: 'rbd_qos_bps_limit',
130 source: RbdConfigurationSourceField.pool
133 name: 'rbd_qos_iops_limit',
135 source: RbdConfigurationSourceField.global
138 sourceType: RbdConfigurationSourceField.image
141 expect(component.form.getValue('configuration.rbd_qos_iops_limit')).toEqual('0 IOPS');
142 expect(component.form.getValue('configuration.rbd_qos_bps_limit')).toEqual('0 B/s');
145 it('should always have formatted results', () => {
146 component.initializeData.next({
149 name: 'rbd_qos_bps_limit',
151 source: RbdConfigurationSourceField.image
154 name: 'rbd_qos_iops_limit',
156 source: RbdConfigurationSourceField.image
159 name: 'rbd_qos_read_bps_limit',
160 value: null, // incorrect type
161 source: RbdConfigurationSourceField.image
164 name: 'rbd_qos_read_bps_limit',
165 value: undefined, // incorrect type
166 source: RbdConfigurationSourceField.image
169 sourceType: RbdConfigurationSourceField.image
172 expect(component.form.getValue('configuration.rbd_qos_iops_limit')).toEqual('22 IOPS');
173 expect(component.form.getValue('configuration.rbd_qos_bps_limit')).toEqual('55 B/s');
174 expect(component.form.getValue('configuration.rbd_qos_read_bps_limit')).toEqual('0 B/s');
175 expect(component.form.getValue('configuration.rbd_qos_read_bps_limit')).toEqual('0 B/s');
179 it('should reset the corresponding form field correctly', () => {
180 const fieldName = 'rbd_qos_bps_limit';
181 const getValue = () => component.form.get(`configuration.${fieldName}`).value;
184 fh.setValue(`configuration.${fieldName}`, 418, true);
185 expect(getValue()).toBe(418);
188 component.reset(fieldName);
189 expect(getValue()).toBe(null);
192 component.reset(fieldName);
193 expect(getValue()).toBe(418);
196 component.reset(fieldName);
197 expect(getValue()).toBe(null);
200 component.reset(fieldName);
201 expect(getValue()).toBe(418);
204 describe('should verify that getDirtyValues() returns correctly', () => {
208 component.initializeData = new ReplaySubject<any>(1);
209 fixture.detectChanges();
210 component.ngOnInit();
214 name: 'rbd_qos_bps_limit',
216 source: RbdConfigurationSourceField.image
219 name: 'rbd_qos_iops_limit',
221 source: RbdConfigurationSourceField.image
224 name: 'rbd_qos_read_bps_limit',
226 source: RbdConfigurationSourceField.image
229 name: 'rbd_qos_read_iops_limit',
231 source: RbdConfigurationSourceField.image
234 name: 'rbd_qos_read_iops_burst',
236 source: RbdConfigurationSourceField.image
239 name: 'rbd_qos_write_bps_burst',
241 source: RbdConfigurationSourceField.global
244 name: 'rbd_qos_write_iops_burst',
246 source: RbdConfigurationSourceField.global
249 sourceType: RbdConfigurationSourceField.image
251 component.initializeData.next(data);
254 it('should return an empty object', () => {
255 expect(component.getDirtyValues()).toEqual({});
256 expect(component.getDirtyValues(true, RbdConfigurationSourceField.image)).toEqual({});
259 it('should return dirty values', () => {
260 component.form.get('configuration.rbd_qos_write_bps_burst').markAsDirty();
261 expect(component.getDirtyValues()).toEqual({ rbd_qos_write_bps_burst: 0 });
263 component.form.get('configuration.rbd_qos_write_iops_burst').markAsDirty();
264 expect(component.getDirtyValues()).toEqual({
265 rbd_qos_write_iops_burst: 0,
266 rbd_qos_write_bps_burst: 0
270 it('should also return all local values if they do not contain their initial values', () => {
271 // Change value for all options
272 data.initialData = data.initialData.map((o: Record<string, any>) => {
278 ['rbd_qos_read_iops_limit', 'rbd_qos_write_bps_burst'].forEach((option) => {
279 component.form.get(`configuration.${option}`).markAsDirty();
282 expect(component.getDirtyValues(true, RbdConfigurationSourceField.image)).toEqual({
283 rbd_qos_read_iops_limit: 0,
284 rbd_qos_write_bps_burst: 0
288 it('should throw an error if used incorrectly', () => {
289 expect(() => component.getDirtyValues(true)).toThrowError(
290 /^ProgrammingError: If local values shall be included/