blob: 35e626813f85b729c7d8e8f9f8f6838ef5049fa0 [file] [log] [blame]
Matteo Scandolofb46ae62017-08-08 09:10:50 -07001
2/*
3 * Copyright 2017-present Open Networking Foundation
4
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8
9 * http://www.apache.org/licenses/LICENSE-2.0
10
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
Matteo Scandolod58d5042016-12-16 16:59:21 -080019import * as angular from 'angular';
20import 'angular-mocks';
21import 'angular-ui-router';
22
Matteo Scandolo07e2f622017-01-09 10:54:13 -080023import {IXosConfigHelpersService, ConfigHelpers, IXosModelDefsField} from './config.helpers';
Matteo Scandolo1aee1982017-02-17 08:33:23 -080024import {IXosModeldef} from '../../../datasources/rest/modeldefs.rest';
Matteo Scandolocb466ed2017-01-04 17:16:24 -080025import {IXosTableCfg} from '../../table/table';
Matteo Scandolo1aee1982017-02-17 08:33:23 -080026import {IXosFormInput, IXosFormCfg} from '../../form/form';
Matteo Scandolo07e2f622017-01-09 10:54:13 -080027import {BehaviorSubject} from 'rxjs';
Matteo Scandolo63498472017-09-26 17:21:41 -070028import {XosFormHelpers} from '../../form/form-helpers';
Matteo Scandolod58d5042016-12-16 16:59:21 -080029
30let service: IXosConfigHelpersService;
Matteo Scandolocb466ed2017-01-04 17:16:24 -080031
Matteo Scandolo1aee1982017-02-17 08:33:23 -080032const model: IXosModeldef = {
Matteo Scandolocb466ed2017-01-04 17:16:24 -080033 name: 'Test',
Matteo Scandolo1aee1982017-02-17 08:33:23 -080034 app: 'test',
Matteo Scandolocb466ed2017-01-04 17:16:24 -080035 fields: [
36 {
37 type: 'number',
38 name: 'id',
Matteo Scandolod67adee2018-03-08 16:27:05 -080039 validators: [],
40 read_only: false
Matteo Scandolocb466ed2017-01-04 17:16:24 -080041 },
42 {
43 type: 'string',
44 name: 'name',
Matteo Scandolo1aee1982017-02-17 08:33:23 -080045 validators: [
46 {
47 bool_value: true,
48 name: 'required'
49 }
Matteo Scandolod67adee2018-03-08 16:27:05 -080050 ],
51 read_only: false
Matteo Scandolocb466ed2017-01-04 17:16:24 -080052 },
53 {
54 type: 'string',
55 name: 'something',
Matteo Scandolo1aee1982017-02-17 08:33:23 -080056 validators: [
57 {
58 int_value: 30,
59 name: 'maxlength'
60 }
Matteo Scandolod67adee2018-03-08 16:27:05 -080061 ],
62 read_only: false
Matteo Scandolo80c3a652017-01-06 10:48:31 -080063 },
64 {
65 type: 'number',
66 name: 'else',
Matteo Scandolo1aee1982017-02-17 08:33:23 -080067 validators: [
68 {
69 int_value: 20,
70 name: 'min'
71 },
72 {
73 int_value: 40,
74 name: 'max'
75 }
Matteo Scandolod67adee2018-03-08 16:27:05 -080076 ],
77 read_only: false
Matteo Scandolocb466ed2017-01-04 17:16:24 -080078 },
79 {
80 type: 'date',
81 name: 'updated',
Matteo Scandolod67adee2018-03-08 16:27:05 -080082 validators: [],
83 read_only: false
Matteo Scandolocb466ed2017-01-04 17:16:24 -080084 },
Matteo Scandolo580033a2017-08-17 11:16:39 -070085 ],
86 description: '',
Matteo Scandolob310f3c2019-06-20 13:50:31 -070087 verbose_name: '',
88 policy_implemented: 'True',
89 sync_implemented: 'True'
Matteo Scandolocb466ed2017-01-04 17:16:24 -080090};
91
Matteo Scandolod58d5042016-12-16 16:59:21 -080092describe('The ConfigHelpers service', () => {
93
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -080094 beforeEach(() => {
95 angular
96 .module('test', ['toastr'])
97 .service('ConfigHelpers', ConfigHelpers)
98 .value('AuthService', {
99 getUser: () => {
100 return {id: 1};
101 }
Matteo Scandolo04964232017-01-07 12:53:46 -0800102 })
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800103 .value('XosModelStore', {
Matteo Scandolo04964232017-01-07 12:53:46 -0800104
Matteo Scandoloa242c872017-01-12 15:13:00 -0800105 })
Matteo Scandolo63498472017-09-26 17:21:41 -0700106 .service('XosFormHelpers', XosFormHelpers)
Matteo Scandoloa242c872017-01-12 15:13:00 -0800107 .value('$state', {
108 get: () => {
109 return [
110 {
111 name: 'xos.core.tests',
112 data: {model: 'Test'}
113 },
114 {
115 name: 'xos.core.slices',
116 data: {model: 'Slices'}
117 }
118 ];
119 }
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -0800120 });
121 angular.mock.module('test');
122 });
Matteo Scandolod58d5042016-12-16 16:59:21 -0800123
124 beforeEach(angular.mock.inject((
125 ConfigHelpers: IXosConfigHelpersService,
126 ) => {
127 service = ConfigHelpers;
128 }));
129
130 describe('The pluralize function', () => {
131 it('should pluralize string', () => {
132 expect(service.pluralize('test')).toEqual('tests');
133 expect(service.pluralize('test', 1)).toEqual('test');
Matteo Scandolo08464e52017-01-17 13:35:27 -0800134 expect(service.pluralize('xos')).toEqual('xoses');
Matteo Scandolod58d5042016-12-16 16:59:21 -0800135 expect(service.pluralize('slice')).toEqual('slices');
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800136 expect(service.pluralize('Slice', 1)).toEqual('Slice');
Matteo Scandolo8bd01ad2018-05-14 13:26:35 -0700137 expect(service.pluralize('kubernetesdata')).toEqual('kubernetesdatas');
Matteo Scandolod58d5042016-12-16 16:59:21 -0800138 });
139
140 it('should preprend count to string', () => {
141 expect(service.pluralize('test', 6, true)).toEqual('6 tests');
142 expect(service.pluralize('test', 1, true)).toEqual('1 test');
143 });
144 });
145
146 describe('the label formatter', () => {
147 it('should format a camel case string', () => {
148 expect(service.toLabel('camelCase')).toEqual('Camel case');
149 });
150
151 it('should format a snake case string', () => {
152 expect(service.toLabel('snake_case')).toEqual('Snake case');
153 });
154
155 it('should format a kebab case string', () => {
156 expect(service.toLabel('kebab-case')).toEqual('Kebab case');
157 });
158
159 it('should set plural', () => {
160 expect(service.toLabel('kebab-case', true)).toEqual('Kebab cases');
161 });
162
163 it('should format an array of strings', () => {
164 let strings: string[] = ['camelCase', 'snake_case', 'kebab-case'];
165 let labels = ['Camel case', 'Snake case', 'Kebab case'];
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -0800166 expect(service.toLabels(strings)).toEqual(labels);
Matteo Scandolod58d5042016-12-16 16:59:21 -0800167 });
168
169 it('should set plural on an array of strings', () => {
170 let strings: string[] = ['camelCase', 'snake_case', 'kebab-case'];
171 let labels = ['Camel cases', 'Snake cases', 'Kebab cases'];
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -0800172 expect(service.toLabels(strings, true)).toEqual(labels);
Matteo Scandolod58d5042016-12-16 16:59:21 -0800173 });
174 });
175
Matteo Scandoloa242c872017-01-12 15:13:00 -0800176 describe('the navigation methods', () => {
Matteo Scandoloa242c872017-01-12 15:13:00 -0800177 describe('stateFromCoreModels', () => {
178
179 let state: ng.ui.IStateService;
180
181 beforeEach(angular.mock.inject(($state) => {
182 state = $state;
183 }));
184
185 it('should return the state for a given model', () => {
186 expect(service.stateFromCoreModel('Test')).toBe('xos.core.tests');
187 });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700188 });
189 describe('stateWithParams', () => {
Matteo Scandoloa242c872017-01-12 15:13:00 -0800190 it('should return the state with params for a given model', () => {
191 expect(service.stateWithParams('Test', {id: 1})).toBe('xos.core.tests({id: 1})');
192 });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700193 it('should return the state with params for a given relation', () => {
194 expect(service.relatedStateWithParams('Test', '1')).toBe('xos.core.tests({id: 1})');
195 });
196
197 it('should return the state with params for usage in js', () => {
Matteo Scandolo1888b2a2018-01-08 16:49:06 -0800198 expect(service.stateWithParamsForJs('Test', 1)).toEqual({ name: 'xos.core.tests', params: Object({ id: 1 }) });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700199 });
Matteo Scandoloa242c872017-01-12 15:13:00 -0800200 });
201 });
202
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800203 describe('the modelFieldsToColumnsCfg method', () => {
204 it('should return an array of columns', () => {
Matteo Scandolob310f3c2019-06-20 13:50:31 -0700205
Matteo Scandolo580033a2017-08-17 11:16:39 -0700206 const cols = service.modelFieldsToColumnsCfg({fields: model.fields, name: 'testUrl', app: 'test', description: '', verbose_name: ''});
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800207 expect(cols[0].label).toBe('Id');
208 expect(cols[0].prop).toBe('id');
209 expect(cols[0].link).toBeDefined();
210
211 expect(cols[1].label).toBe('Name');
212 expect(cols[1].prop).toBe('name');
213 expect(cols[1].link).toBeDefined();
214
215 expect(cols[2].label).toBe('Something');
216 expect(cols[2].prop).toBe('something');
217 expect(cols[2].link).not.toBeDefined();
218
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800219 expect(cols[3].label).toBe('Else');
220 expect(cols[3].prop).toBe('else');
221 expect(cols[3].link).not.toBeDefined();
222
223 expect(cols[4]).not.toBeDefined();
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800224 });
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700225
Matteo Scandolob310f3c2019-06-20 13:50:31 -0700226 describe('it should handle sync_implemented and policy_implemented options', () => {
227
228 const modelFields: IXosModelDefsField[] = [
229 {
230 type: 'string',
231 name: 'policy_status',
232 validators: [],
233 read_only: false
234 },
235 {
236 type: 'string',
237 name: 'name',
238 validators: [],
239 read_only: false
240 },
241 {
242 type: 'string',
243 name: 'backend_status',
244 validators: [],
245 read_only: false
246 }
247 ];
248
249 const model: IXosModeldef = {
250 fields: modelFields,
251 name: 'testUrl',
252 app: 'test',
253 description: '',
254 verbose_name: ''
255 };
256
257 it('should not create columns for policy and sync status unless defined', () => {
258 model.policy_implemented = '';
259 model.sync_implemented = '';
260 const cols = service.modelFieldsToColumnsCfg(model);
261 expect(cols.length).toBe(1); // we strip backend_status and policy_status
262 });
263
264 it('should create columns for policy and sync status unless defined', () => {
265 model.policy_implemented = 'True';
266 model.sync_implemented = 'True';
267 const cols = service.modelFieldsToColumnsCfg(model);
268 expect(cols.length).toBe(3); // we DO NOT strip backend_status and policy_status
269 });
270
271 });
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800272 });
273
274 describe('the modelToTableCfg method', () => {
275 it('should return a table config', () => {
Matteo Scandolob310f3c2019-06-20 13:50:31 -0700276 service.excluded_fields = ['foo', 'bar'];
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800277 const cfg: IXosTableCfg = service.modelToTableCfg(model, 'testUrl/:id?');
278 expect(cfg.columns).toBeDefined();
279 expect(cfg.filter).toBe('fulltext');
280 expect(cfg.order).toEqual({field: 'id', reverse: false});
Matteo Scandolocc4bce82017-08-07 13:11:47 -0700281 expect(cfg.actions.length).toBe(2);
282 expect(cfg.actions[0].label).toEqual('details');
283 expect(cfg.actions[1].label).toEqual('delete');
Matteo Scandolob310f3c2019-06-20 13:50:31 -0700284 expect(service.excluded_fields).toEqual(service.base_excluded_fields);
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800285 });
286 });
Matteo Scandolo1c5905f2017-01-04 17:41:15 -0800287
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800288 describe('the modelFieldToInputConfig', () => {
289 it('should return an array of inputs', () => {
290 const inputs: IXosFormInput[] = service.modelFieldToInputCfg(model.fields);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800291
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700292 expect(inputs[0].name).toBe('name');
293 expect(inputs[0].type).toBe('string');
294 expect(inputs[0].label).toBe('Name');
295 expect(inputs[0].validators.required).toBe(true);
296
297 expect(inputs[1].name).toBe('something');
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800298 expect(inputs[1].type).toBe('string');
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700299 expect(inputs[1].label).toBe('Something');
300 expect(inputs[1].validators.maxlength).toBe(30);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800301
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700302 expect(inputs[2].name).toBe('else');
303 expect(inputs[2].type).toBe('number');
304 expect(inputs[2].label).toBe('Else');
305 expect(inputs[2].validators.min).toBe(20);
306 expect(inputs[2].validators.max).toBe(40);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800307 });
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700308
309 it('should convert boolean defaults to real booleans', () => {
310 const fields: IXosModelDefsField[] = [
311 {
312 type: 'boolean',
313 name: 'active',
314 default: '"True"',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800315 validators: [],
316 read_only: false
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700317 },
318 {
319 type: 'boolean',
320 name: 'disabled',
321 default: '"False"',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800322 validators: [],
323 read_only: false
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700324 },
325 ];
326 const form_fields = service.modelFieldToInputCfg(fields);
327 expect(form_fields[0].default).toBe(true);
328 expect(form_fields[1].default).toBe(false);
329 });
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800330 });
331
332 describe('the modelToFormCfg method', () => {
333 it('should return a form config', () => {
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800334 const config: IXosFormCfg = service.modelToFormCfg(model);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800335 expect(config.formName).toBe('TestForm');
336 expect(config.actions.length).toBe(1);
337 expect(config.actions[0].label).toBe('Save');
338 expect(config.actions[0].class).toBe('success');
339 expect(config.actions[0].icon).toBe('ok');
340 expect(config.actions[0].cb).toBeDefined();
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700341 // NOTE 'id' and 'updated' are hidden fields
342 expect(config.inputs.length).toBe(3);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800343 });
344 });
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800345
346 describe('the private methods', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700347 let modelStoreMock, q, toastr, auth, stateMock, XosFormHelpersMock;
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800348
Matteo Scandolo63498472017-09-26 17:21:41 -0700349 beforeEach(angular.mock.inject(($q, _toastr_, AuthService, XosFormHelpers) => {
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800350 modelStoreMock = {
351 query: () => {
352 const subject = new BehaviorSubject([
353 {id: 1, humanReadableName: 'test'},
354 {id: 2, humanReadableName: 'second'}
355 ]);
356 return subject.asObservable();
357 }
358 };
359 toastr = _toastr_;
360 auth = AuthService;
Matteo Scandolo63498472017-09-26 17:21:41 -0700361 XosFormHelpersMock = XosFormHelpers;
Matteo Scandoloa242c872017-01-12 15:13:00 -0800362 stateMock = {
363 get: ''
364 };
Matteo Scandolo63498472017-09-26 17:21:41 -0700365 q = $q;
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800366 }));
367
368 const field: IXosModelDefsField = {
369 name: 'test',
370 type: 'number',
371 relation: {
372 model: 'Test',
373 type: 'many_to_one'
Matteo Scandolod67adee2018-03-08 16:27:05 -0800374 },
375 read_only: false
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800376 };
377
378 describe('the populateRelated method', () => {
379 const item = {
380 test: 2
381 };
382 it('should add the formatted data to the column definition', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700383 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800384 service['populateRelated'](item, item.test, field);
385 expect(item['test-formatted']).toBe('second');
386 });
387 });
388
389 describe('the populateSelectField', () => {
390
391 const input: IXosFormInput = {
392 name: 'test',
393 label: 'Test',
394 type: 'select',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800395 validators: {},
396 read_only: false
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800397 };
398
399 it('should add the available choice to the select', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700400 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800401 service['populateSelectField'](field, input);
402 expect(input.options).toEqual([
403 {id: 1, label: 'test'},
404 {id: 2, label: 'second'}
405 ]);
406 });
407 });
Matteo Scandolo620cb492017-11-27 16:56:36 -0800408
409 describe('the removeExtraFields method', () => {
410 beforeEach(() => {
411 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
412 });
413
414 it('should remove properties not defined in xProto', () => {
415 const model: IXosModeldef = {
416 name: 'Test',
417 app: 'test',
418 fields: [
419 {
420 type: 'number',
421 name: 'foo',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800422 validators: [],
423 read_only: false
Matteo Scandolo620cb492017-11-27 16:56:36 -0800424 },
425 {
426 type: 'string',
427 name: 'bar',
Matteo Scandolo6f64a742018-06-22 14:25:59 -0700428 validators: [],
Matteo Scandolod67adee2018-03-08 16:27:05 -0800429 read_only: false
Matteo Scandolo620cb492017-11-27 16:56:36 -0800430 }
431 ],
432 description: '',
433 verbose_name: ''
434 };
435
436 const item: any = {
437 foo: 1,
438 bar: 'existing',
439 baz: 'remove me'
440 };
441
442 const res = service['removeExtraFields'](item, model);
443
444 expect(res).not.toHaveProp('baz');
445 });
Matteo Scandolo6f64a742018-06-22 14:25:59 -0700446
447 it('should remove properties marked as read_only', () => {
448 const model: IXosModeldef = {
449 name: 'Test',
450 app: 'test',
451 fields: [
452 {
453 type: 'number',
454 name: 'write_allowed',
455 validators: [],
456 read_only: false
457 },
458 {
459 type: 'string',
460 name: 'read_only',
461 validators: [],
462 read_only: true
463 }
464 ],
465 description: '',
466 verbose_name: ''
467 };
468
469 const item: any = {
470 write_allowed: 'existing',
471 read_only: 'remove me'
472 };
473
474 const res = service['removeExtraFields'](item, model);
475
476 expect(res).not.toHaveProp('read_only');
477 });
Matteo Scandolo620cb492017-11-27 16:56:36 -0800478 });
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800479 });
Matteo Scandolod58d5042016-12-16 16:59:21 -0800480});
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800481