blob: decc0599eb042dbc048727559e4aa0ee87953fcd [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: '',
87 verbose_name: ''
Matteo Scandolocb466ed2017-01-04 17:16:24 -080088};
89
Matteo Scandolod58d5042016-12-16 16:59:21 -080090describe('The ConfigHelpers service', () => {
91
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -080092 beforeEach(() => {
93 angular
94 .module('test', ['toastr'])
95 .service('ConfigHelpers', ConfigHelpers)
96 .value('AuthService', {
97 getUser: () => {
98 return {id: 1};
99 }
Matteo Scandolo04964232017-01-07 12:53:46 -0800100 })
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800101 .value('XosModelStore', {
Matteo Scandolo04964232017-01-07 12:53:46 -0800102
Matteo Scandoloa242c872017-01-12 15:13:00 -0800103 })
Matteo Scandolo63498472017-09-26 17:21:41 -0700104 .service('XosFormHelpers', XosFormHelpers)
Matteo Scandoloa242c872017-01-12 15:13:00 -0800105 .value('$state', {
106 get: () => {
107 return [
108 {
109 name: 'xos.core.tests',
110 data: {model: 'Test'}
111 },
112 {
113 name: 'xos.core.slices',
114 data: {model: 'Slices'}
115 }
116 ];
117 }
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -0800118 });
119 angular.mock.module('test');
120 });
Matteo Scandolod58d5042016-12-16 16:59:21 -0800121
122 beforeEach(angular.mock.inject((
123 ConfigHelpers: IXosConfigHelpersService,
124 ) => {
125 service = ConfigHelpers;
126 }));
127
128 describe('The pluralize function', () => {
129 it('should pluralize string', () => {
130 expect(service.pluralize('test')).toEqual('tests');
131 expect(service.pluralize('test', 1)).toEqual('test');
Matteo Scandolo08464e52017-01-17 13:35:27 -0800132 expect(service.pluralize('xos')).toEqual('xoses');
Matteo Scandolod58d5042016-12-16 16:59:21 -0800133 expect(service.pluralize('slice')).toEqual('slices');
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800134 expect(service.pluralize('Slice', 1)).toEqual('Slice');
Matteo Scandolo8bd01ad2018-05-14 13:26:35 -0700135 expect(service.pluralize('kubernetesdata')).toEqual('kubernetesdatas');
Matteo Scandolod58d5042016-12-16 16:59:21 -0800136 });
137
138 it('should preprend count to string', () => {
139 expect(service.pluralize('test', 6, true)).toEqual('6 tests');
140 expect(service.pluralize('test', 1, true)).toEqual('1 test');
141 });
142 });
143
144 describe('the label formatter', () => {
145 it('should format a camel case string', () => {
146 expect(service.toLabel('camelCase')).toEqual('Camel case');
147 });
148
149 it('should format a snake case string', () => {
150 expect(service.toLabel('snake_case')).toEqual('Snake case');
151 });
152
153 it('should format a kebab case string', () => {
154 expect(service.toLabel('kebab-case')).toEqual('Kebab case');
155 });
156
157 it('should set plural', () => {
158 expect(service.toLabel('kebab-case', true)).toEqual('Kebab cases');
159 });
160
161 it('should format an array of strings', () => {
162 let strings: string[] = ['camelCase', 'snake_case', 'kebab-case'];
163 let labels = ['Camel case', 'Snake case', 'Kebab case'];
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -0800164 expect(service.toLabels(strings)).toEqual(labels);
Matteo Scandolod58d5042016-12-16 16:59:21 -0800165 });
166
167 it('should set plural on an array of strings', () => {
168 let strings: string[] = ['camelCase', 'snake_case', 'kebab-case'];
169 let labels = ['Camel cases', 'Snake cases', 'Kebab cases'];
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -0800170 expect(service.toLabels(strings, true)).toEqual(labels);
Matteo Scandolod58d5042016-12-16 16:59:21 -0800171 });
172 });
173
Matteo Scandoloa242c872017-01-12 15:13:00 -0800174 describe('the navigation methods', () => {
Matteo Scandoloa242c872017-01-12 15:13:00 -0800175 describe('stateFromCoreModels', () => {
176
177 let state: ng.ui.IStateService;
178
179 beforeEach(angular.mock.inject(($state) => {
180 state = $state;
181 }));
182
183 it('should return the state for a given model', () => {
184 expect(service.stateFromCoreModel('Test')).toBe('xos.core.tests');
185 });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700186 });
187 describe('stateWithParams', () => {
Matteo Scandoloa242c872017-01-12 15:13:00 -0800188 it('should return the state with params for a given model', () => {
189 expect(service.stateWithParams('Test', {id: 1})).toBe('xos.core.tests({id: 1})');
190 });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700191 it('should return the state with params for a given relation', () => {
192 expect(service.relatedStateWithParams('Test', '1')).toBe('xos.core.tests({id: 1})');
193 });
194
195 it('should return the state with params for usage in js', () => {
Matteo Scandolo1888b2a2018-01-08 16:49:06 -0800196 expect(service.stateWithParamsForJs('Test', 1)).toEqual({ name: 'xos.core.tests', params: Object({ id: 1 }) });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700197 });
Matteo Scandoloa242c872017-01-12 15:13:00 -0800198 });
199 });
200
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800201 describe('the modelFieldsToColumnsCfg method', () => {
202 it('should return an array of columns', () => {
Matteo Scandolo580033a2017-08-17 11:16:39 -0700203 const cols = service.modelFieldsToColumnsCfg({fields: model.fields, name: 'testUrl', app: 'test', description: '', verbose_name: ''});
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800204 expect(cols[0].label).toBe('Id');
205 expect(cols[0].prop).toBe('id');
206 expect(cols[0].link).toBeDefined();
207
208 expect(cols[1].label).toBe('Name');
209 expect(cols[1].prop).toBe('name');
210 expect(cols[1].link).toBeDefined();
211
212 expect(cols[2].label).toBe('Something');
213 expect(cols[2].prop).toBe('something');
214 expect(cols[2].link).not.toBeDefined();
215
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800216 expect(cols[3].label).toBe('Else');
217 expect(cols[3].prop).toBe('else');
218 expect(cols[3].link).not.toBeDefined();
219
220 expect(cols[4]).not.toBeDefined();
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800221 });
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700222
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800223 });
224
225 describe('the modelToTableCfg method', () => {
226 it('should return a table config', () => {
227 const cfg: IXosTableCfg = service.modelToTableCfg(model, 'testUrl/:id?');
228 expect(cfg.columns).toBeDefined();
229 expect(cfg.filter).toBe('fulltext');
230 expect(cfg.order).toEqual({field: 'id', reverse: false});
Matteo Scandolocc4bce82017-08-07 13:11:47 -0700231 expect(cfg.actions.length).toBe(2);
232 expect(cfg.actions[0].label).toEqual('details');
233 expect(cfg.actions[1].label).toEqual('delete');
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800234 });
235 });
Matteo Scandolo1c5905f2017-01-04 17:41:15 -0800236
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800237 describe('the modelFieldToInputConfig', () => {
238 it('should return an array of inputs', () => {
239 const inputs: IXosFormInput[] = service.modelFieldToInputCfg(model.fields);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800240
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700241 expect(inputs[0].name).toBe('name');
242 expect(inputs[0].type).toBe('string');
243 expect(inputs[0].label).toBe('Name');
244 expect(inputs[0].validators.required).toBe(true);
245
246 expect(inputs[1].name).toBe('something');
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800247 expect(inputs[1].type).toBe('string');
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700248 expect(inputs[1].label).toBe('Something');
249 expect(inputs[1].validators.maxlength).toBe(30);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800250
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700251 expect(inputs[2].name).toBe('else');
252 expect(inputs[2].type).toBe('number');
253 expect(inputs[2].label).toBe('Else');
254 expect(inputs[2].validators.min).toBe(20);
255 expect(inputs[2].validators.max).toBe(40);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800256 });
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700257
258 it('should convert boolean defaults to real booleans', () => {
259 const fields: IXosModelDefsField[] = [
260 {
261 type: 'boolean',
262 name: 'active',
263 default: '"True"',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800264 validators: [],
265 read_only: false
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700266 },
267 {
268 type: 'boolean',
269 name: 'disabled',
270 default: '"False"',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800271 validators: [],
272 read_only: false
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700273 },
274 ];
275 const form_fields = service.modelFieldToInputCfg(fields);
276 expect(form_fields[0].default).toBe(true);
277 expect(form_fields[1].default).toBe(false);
278 });
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800279 });
280
281 describe('the modelToFormCfg method', () => {
282 it('should return a form config', () => {
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800283 const config: IXosFormCfg = service.modelToFormCfg(model);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800284 expect(config.formName).toBe('TestForm');
285 expect(config.actions.length).toBe(1);
286 expect(config.actions[0].label).toBe('Save');
287 expect(config.actions[0].class).toBe('success');
288 expect(config.actions[0].icon).toBe('ok');
289 expect(config.actions[0].cb).toBeDefined();
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700290 // NOTE 'id' and 'updated' are hidden fields
291 expect(config.inputs.length).toBe(3);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800292 });
293 });
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800294
295 describe('the private methods', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700296 let modelStoreMock, q, toastr, auth, stateMock, XosFormHelpersMock;
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800297
Matteo Scandolo63498472017-09-26 17:21:41 -0700298 beforeEach(angular.mock.inject(($q, _toastr_, AuthService, XosFormHelpers) => {
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800299 modelStoreMock = {
300 query: () => {
301 const subject = new BehaviorSubject([
302 {id: 1, humanReadableName: 'test'},
303 {id: 2, humanReadableName: 'second'}
304 ]);
305 return subject.asObservable();
306 }
307 };
308 toastr = _toastr_;
309 auth = AuthService;
Matteo Scandolo63498472017-09-26 17:21:41 -0700310 XosFormHelpersMock = XosFormHelpers;
Matteo Scandoloa242c872017-01-12 15:13:00 -0800311 stateMock = {
312 get: ''
313 };
Matteo Scandolo63498472017-09-26 17:21:41 -0700314 q = $q;
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800315 }));
316
317 const field: IXosModelDefsField = {
318 name: 'test',
319 type: 'number',
320 relation: {
321 model: 'Test',
322 type: 'many_to_one'
Matteo Scandolod67adee2018-03-08 16:27:05 -0800323 },
324 read_only: false
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800325 };
326
327 describe('the populateRelated method', () => {
328 const item = {
329 test: 2
330 };
331 it('should add the formatted data to the column definition', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700332 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800333 service['populateRelated'](item, item.test, field);
334 expect(item['test-formatted']).toBe('second');
335 });
336 });
337
338 describe('the populateSelectField', () => {
339
340 const input: IXosFormInput = {
341 name: 'test',
342 label: 'Test',
343 type: 'select',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800344 validators: {},
345 read_only: false
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800346 };
347
348 it('should add the available choice to the select', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700349 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800350 service['populateSelectField'](field, input);
351 expect(input.options).toEqual([
352 {id: 1, label: 'test'},
353 {id: 2, label: 'second'}
354 ]);
355 });
356 });
Matteo Scandolo620cb492017-11-27 16:56:36 -0800357
358 describe('the removeExtraFields method', () => {
359 beforeEach(() => {
360 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
361 });
362
363 it('should remove properties not defined in xProto', () => {
364 const model: IXosModeldef = {
365 name: 'Test',
366 app: 'test',
367 fields: [
368 {
369 type: 'number',
370 name: 'foo',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800371 validators: [],
372 read_only: false
Matteo Scandolo620cb492017-11-27 16:56:36 -0800373 },
374 {
375 type: 'string',
376 name: 'bar',
Matteo Scandolo6f64a742018-06-22 14:25:59 -0700377 validators: [],
Matteo Scandolod67adee2018-03-08 16:27:05 -0800378 read_only: false
Matteo Scandolo620cb492017-11-27 16:56:36 -0800379 }
380 ],
381 description: '',
382 verbose_name: ''
383 };
384
385 const item: any = {
386 foo: 1,
387 bar: 'existing',
388 baz: 'remove me'
389 };
390
391 const res = service['removeExtraFields'](item, model);
392
393 expect(res).not.toHaveProp('baz');
394 });
Matteo Scandolo6f64a742018-06-22 14:25:59 -0700395
396 it('should remove properties marked as read_only', () => {
397 const model: IXosModeldef = {
398 name: 'Test',
399 app: 'test',
400 fields: [
401 {
402 type: 'number',
403 name: 'write_allowed',
404 validators: [],
405 read_only: false
406 },
407 {
408 type: 'string',
409 name: 'read_only',
410 validators: [],
411 read_only: true
412 }
413 ],
414 description: '',
415 verbose_name: ''
416 };
417
418 const item: any = {
419 write_allowed: 'existing',
420 read_only: 'remove me'
421 };
422
423 const res = service['removeExtraFields'](item, model);
424
425 expect(res).not.toHaveProp('read_only');
426 });
Matteo Scandolo620cb492017-11-27 16:56:36 -0800427 });
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800428 });
Matteo Scandolod58d5042016-12-16 16:59:21 -0800429});
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800430