blob: 9e7318ebaaac2a9d05ceba24db6fbc363ec4362e [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 Scandolod58d5042016-12-16 16:59:21 -0800135 });
136
137 it('should preprend count to string', () => {
138 expect(service.pluralize('test', 6, true)).toEqual('6 tests');
139 expect(service.pluralize('test', 1, true)).toEqual('1 test');
140 });
141 });
142
143 describe('the label formatter', () => {
144 it('should format a camel case string', () => {
145 expect(service.toLabel('camelCase')).toEqual('Camel case');
146 });
147
148 it('should format a snake case string', () => {
149 expect(service.toLabel('snake_case')).toEqual('Snake case');
150 });
151
152 it('should format a kebab case string', () => {
153 expect(service.toLabel('kebab-case')).toEqual('Kebab case');
154 });
155
156 it('should set plural', () => {
157 expect(service.toLabel('kebab-case', true)).toEqual('Kebab cases');
158 });
159
160 it('should format an array of strings', () => {
161 let strings: string[] = ['camelCase', 'snake_case', 'kebab-case'];
162 let labels = ['Camel case', 'Snake case', 'Kebab case'];
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -0800163 expect(service.toLabels(strings)).toEqual(labels);
Matteo Scandolod58d5042016-12-16 16:59:21 -0800164 });
165
166 it('should set plural on an array of strings', () => {
167 let strings: string[] = ['camelCase', 'snake_case', 'kebab-case'];
168 let labels = ['Camel cases', 'Snake cases', 'Kebab cases'];
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -0800169 expect(service.toLabels(strings, true)).toEqual(labels);
Matteo Scandolod58d5042016-12-16 16:59:21 -0800170 });
171 });
172
Matteo Scandoloa242c872017-01-12 15:13:00 -0800173 describe('the navigation methods', () => {
Matteo Scandoloa242c872017-01-12 15:13:00 -0800174 describe('stateFromCoreModels', () => {
175
176 let state: ng.ui.IStateService;
177
178 beforeEach(angular.mock.inject(($state) => {
179 state = $state;
180 }));
181
182 it('should return the state for a given model', () => {
183 expect(service.stateFromCoreModel('Test')).toBe('xos.core.tests');
184 });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700185 });
186 describe('stateWithParams', () => {
Matteo Scandoloa242c872017-01-12 15:13:00 -0800187 it('should return the state with params for a given model', () => {
188 expect(service.stateWithParams('Test', {id: 1})).toBe('xos.core.tests({id: 1})');
189 });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700190 it('should return the state with params for a given relation', () => {
191 expect(service.relatedStateWithParams('Test', '1')).toBe('xos.core.tests({id: 1})');
192 });
193
194 it('should return the state with params for usage in js', () => {
Matteo Scandolo1888b2a2018-01-08 16:49:06 -0800195 expect(service.stateWithParamsForJs('Test', 1)).toEqual({ name: 'xos.core.tests', params: Object({ id: 1 }) });
Matteo Scandolo8248bca2017-08-09 13:46:04 -0700196 });
Matteo Scandoloa242c872017-01-12 15:13:00 -0800197 });
198 });
199
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800200 describe('the modelFieldsToColumnsCfg method', () => {
201 it('should return an array of columns', () => {
Matteo Scandolo580033a2017-08-17 11:16:39 -0700202 const cols = service.modelFieldsToColumnsCfg({fields: model.fields, name: 'testUrl', app: 'test', description: '', verbose_name: ''});
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800203 expect(cols[0].label).toBe('Id');
204 expect(cols[0].prop).toBe('id');
205 expect(cols[0].link).toBeDefined();
206
207 expect(cols[1].label).toBe('Name');
208 expect(cols[1].prop).toBe('name');
209 expect(cols[1].link).toBeDefined();
210
211 expect(cols[2].label).toBe('Something');
212 expect(cols[2].prop).toBe('something');
213 expect(cols[2].link).not.toBeDefined();
214
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800215 expect(cols[3].label).toBe('Else');
216 expect(cols[3].prop).toBe('else');
217 expect(cols[3].link).not.toBeDefined();
218
219 expect(cols[4]).not.toBeDefined();
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800220 });
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700221
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800222 });
223
224 describe('the modelToTableCfg method', () => {
225 it('should return a table config', () => {
226 const cfg: IXosTableCfg = service.modelToTableCfg(model, 'testUrl/:id?');
227 expect(cfg.columns).toBeDefined();
228 expect(cfg.filter).toBe('fulltext');
229 expect(cfg.order).toEqual({field: 'id', reverse: false});
Matteo Scandolocc4bce82017-08-07 13:11:47 -0700230 expect(cfg.actions.length).toBe(2);
231 expect(cfg.actions[0].label).toEqual('details');
232 expect(cfg.actions[1].label).toEqual('delete');
Matteo Scandolocb466ed2017-01-04 17:16:24 -0800233 });
234 });
Matteo Scandolo1c5905f2017-01-04 17:41:15 -0800235
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800236 describe('the modelFieldToInputConfig', () => {
237 it('should return an array of inputs', () => {
238 const inputs: IXosFormInput[] = service.modelFieldToInputCfg(model.fields);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800239
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700240 expect(inputs[0].name).toBe('name');
241 expect(inputs[0].type).toBe('string');
242 expect(inputs[0].label).toBe('Name');
243 expect(inputs[0].validators.required).toBe(true);
244
245 expect(inputs[1].name).toBe('something');
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800246 expect(inputs[1].type).toBe('string');
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700247 expect(inputs[1].label).toBe('Something');
248 expect(inputs[1].validators.maxlength).toBe(30);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800249
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700250 expect(inputs[2].name).toBe('else');
251 expect(inputs[2].type).toBe('number');
252 expect(inputs[2].label).toBe('Else');
253 expect(inputs[2].validators.min).toBe(20);
254 expect(inputs[2].validators.max).toBe(40);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800255 });
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700256
257 it('should convert boolean defaults to real booleans', () => {
258 const fields: IXosModelDefsField[] = [
259 {
260 type: 'boolean',
261 name: 'active',
262 default: '"True"',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800263 validators: [],
264 read_only: false
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700265 },
266 {
267 type: 'boolean',
268 name: 'disabled',
269 default: '"False"',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800270 validators: [],
271 read_only: false
Matteo Scandolof1e68cd2017-09-05 17:30:34 -0700272 },
273 ];
274 const form_fields = service.modelFieldToInputCfg(fields);
275 expect(form_fields[0].default).toBe(true);
276 expect(form_fields[1].default).toBe(false);
277 });
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800278 });
279
280 describe('the modelToFormCfg method', () => {
281 it('should return a form config', () => {
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800282 const config: IXosFormCfg = service.modelToFormCfg(model);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800283 expect(config.formName).toBe('TestForm');
284 expect(config.actions.length).toBe(1);
285 expect(config.actions[0].label).toBe('Save');
286 expect(config.actions[0].class).toBe('success');
287 expect(config.actions[0].icon).toBe('ok');
288 expect(config.actions[0].cb).toBeDefined();
Matteo Scandolod53ac1d2017-08-01 15:06:09 -0700289 // NOTE 'id' and 'updated' are hidden fields
290 expect(config.inputs.length).toBe(3);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800291 });
292 });
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800293
294 describe('the private methods', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700295 let modelStoreMock, q, toastr, auth, stateMock, XosFormHelpersMock;
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800296
Matteo Scandolo63498472017-09-26 17:21:41 -0700297 beforeEach(angular.mock.inject(($q, _toastr_, AuthService, XosFormHelpers) => {
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800298 modelStoreMock = {
299 query: () => {
300 const subject = new BehaviorSubject([
301 {id: 1, humanReadableName: 'test'},
302 {id: 2, humanReadableName: 'second'}
303 ]);
304 return subject.asObservable();
305 }
306 };
307 toastr = _toastr_;
308 auth = AuthService;
Matteo Scandolo63498472017-09-26 17:21:41 -0700309 XosFormHelpersMock = XosFormHelpers;
Matteo Scandoloa242c872017-01-12 15:13:00 -0800310 stateMock = {
311 get: ''
312 };
Matteo Scandolo63498472017-09-26 17:21:41 -0700313 q = $q;
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800314 }));
315
316 const field: IXosModelDefsField = {
317 name: 'test',
318 type: 'number',
319 relation: {
320 model: 'Test',
321 type: 'many_to_one'
Matteo Scandolod67adee2018-03-08 16:27:05 -0800322 },
323 read_only: false
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800324 };
325
326 describe('the populateRelated method', () => {
327 const item = {
328 test: 2
329 };
330 it('should add the formatted data to the column definition', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700331 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800332 service['populateRelated'](item, item.test, field);
333 expect(item['test-formatted']).toBe('second');
334 });
335 });
336
337 describe('the populateSelectField', () => {
338
339 const input: IXosFormInput = {
340 name: 'test',
341 label: 'Test',
342 type: 'select',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800343 validators: {},
344 read_only: false
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800345 };
346
347 it('should add the available choice to the select', () => {
Matteo Scandolo63498472017-09-26 17:21:41 -0700348 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800349 service['populateSelectField'](field, input);
350 expect(input.options).toEqual([
351 {id: 1, label: 'test'},
352 {id: 2, label: 'second'}
353 ]);
354 });
355 });
Matteo Scandolo620cb492017-11-27 16:56:36 -0800356
357 describe('the removeExtraFields method', () => {
358 beforeEach(() => {
359 service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
360 });
361
362 it('should remove properties not defined in xProto', () => {
363 const model: IXosModeldef = {
364 name: 'Test',
365 app: 'test',
366 fields: [
367 {
368 type: 'number',
369 name: 'foo',
Matteo Scandolod67adee2018-03-08 16:27:05 -0800370 validators: [],
371 read_only: false
Matteo Scandolo620cb492017-11-27 16:56:36 -0800372 },
373 {
374 type: 'string',
375 name: 'bar',
376 validators: [
377 {
378 bool_value: true,
379 name: 'required'
380 }
Matteo Scandolod67adee2018-03-08 16:27:05 -0800381 ],
382 read_only: false
Matteo Scandolo620cb492017-11-27 16:56:36 -0800383 }
384 ],
385 description: '',
386 verbose_name: ''
387 };
388
389 const item: any = {
390 foo: 1,
391 bar: 'existing',
392 baz: 'remove me'
393 };
394
395 const res = service['removeExtraFields'](item, model);
396
397 expect(res).not.toHaveProp('baz');
398 });
399 });
Matteo Scandolo07e2f622017-01-09 10:54:13 -0800400 });
Matteo Scandolod58d5042016-12-16 16:59:21 -0800401});
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800402