blob: e9b7c2a1da96c9a71e43ec3103e3912e7afd8a7f [file] [log] [blame]
Sean Condon83fc39f2018-04-19 18:56:13 +01001/*
Sean Condon5ca00262018-09-06 17:55:25 +01002 * Copyright 2018-present Open Networking Foundation
Sean Condon83fc39f2018-04-19 18:56:13 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import { TestBed, inject } from '@angular/core/testing';
17
Sean Condon5ca00262018-09-06 17:55:25 +010018import { LogService } from '../log.service';
19import { ConsoleLoggerService } from '../consolelogger.service';
20import { FnService } from './fn.service';
Sean Condon49e15be2018-05-16 16:58:29 +010021import { ActivatedRoute, Params } from '@angular/router';
22import { of } from 'rxjs';
Sean Condonfd6d11b2018-06-02 20:29:49 +010023import * as d3 from 'd3';
Sean Condon49e15be2018-05-16 16:58:29 +010024
25class MockActivatedRoute extends ActivatedRoute {
26 constructor(params: Params) {
27 super();
28 this.queryParams = of(params);
29 }
30}
Sean Condon83fc39f2018-04-19 18:56:13 +010031
32/**
33 * ONOS GUI -- Util -- General Purpose Functions - Unit Tests
34 */
35describe('FnService', () => {
Sean Condon49e15be2018-05-16 16:58:29 +010036 let ar: ActivatedRoute;
Sean Condonfd6d11b2018-06-02 20:29:49 +010037 let fs: FnService;
38 let mockWindow: Window;
39 let logServiceSpy: jasmine.SpyObj<LogService>;
40
41 const someFunction = () => {};
42 const someArray = [1, 2, 3];
43 const someObject = { foo: 'bar'};
44 const someNumber = 42;
45 const someString = 'xyyzy';
46 const someDate = new Date();
47 const stringArray = ['foo', 'bar'];
Sean Condon83fc39f2018-04-19 18:56:13 +010048
Sean Condon49e15be2018-05-16 16:58:29 +010049 beforeEach(() => {
Sean Condonfd6d11b2018-06-02 20:29:49 +010050 const logSpy = jasmine.createSpyObj('LogService', ['debug', 'warn']);
Sean Condon49e15be2018-05-16 16:58:29 +010051 ar = new MockActivatedRoute({'debug': 'TestService'});
Sean Condonfd6d11b2018-06-02 20:29:49 +010052 mockWindow = <any>{
53 innerWidth: 400,
54 innerHeight: 200,
55 navigator: {
56 userAgent: 'defaultUA'
57 }
58 };
59
Sean Condon49e15be2018-05-16 16:58:29 +010060
61 TestBed.configureTestingModule({
62 providers: [FnService,
Sean Condonfd6d11b2018-06-02 20:29:49 +010063 { provide: LogService, useValue: logSpy },
Sean Condon49e15be2018-05-16 16:58:29 +010064 { provide: ActivatedRoute, useValue: ar },
Sean Condona00bf382018-06-23 07:54:01 +010065 { provide: 'Window', useFactory: (() => mockWindow ) }
Sean Condon49e15be2018-05-16 16:58:29 +010066 ]
67 });
Sean Condonfd6d11b2018-06-02 20:29:49 +010068
69 fs = TestBed.get(FnService);
70 logServiceSpy = TestBed.get(LogService);
Sean Condon49e15be2018-05-16 16:58:29 +010071 });
72
Sean Condonfd6d11b2018-06-02 20:29:49 +010073 it('should be created', () => {
74 expect(fs).toBeTruthy();
75 });
76
77 // === Tests for isF()
78 it('isF(): null for undefined', () => {
79 expect(fs.isF(undefined)).toBeNull();
80 });
81
82 it('isF(): null for null', () => {
83 expect(fs.isF(null)).toBeNull();
84 });
85 it('isF(): the reference for function', () => {
86 expect(fs.isF(someFunction)).toBe(someFunction);
87 });
88 it('isF(): null for string', () => {
89 expect(fs.isF(someString)).toBeNull();
90 });
91 it('isF(): null for number', () => {
92 expect(fs.isF(someNumber)).toBeNull();
93 });
94 it('isF(): null for Date', () => {
95 expect(fs.isF(someDate)).toBeNull();
96 });
97 it('isF(): null for array', () => {
98 expect(fs.isF(someArray)).toBeNull();
99 });
100 it('isF(): null for object', () => {
101 expect(fs.isF(someObject)).toBeNull();
102 });
103
104 // === Tests for isA()
105 it('isA(): null for undefined', () => {
106 expect(fs.isA(undefined)).toBeNull();
107 });
108 it('isA(): null for null', () => {
109 expect(fs.isA(null)).toBeNull();
110 });
111 it('isA(): null for function', () => {
112 expect(fs.isA(someFunction)).toBeNull();
113 });
114 it('isA(): null for string', () => {
115 expect(fs.isA(someString)).toBeNull();
116 });
117 it('isA(): null for number', () => {
118 expect(fs.isA(someNumber)).toBeNull();
119 });
120 it('isA(): null for Date', () => {
121 expect(fs.isA(someDate)).toBeNull();
122 });
123 it('isA(): the reference for array', () => {
124 expect(fs.isA(someArray)).toBe(someArray);
125 });
126 it('isA(): null for object', () => {
127 expect(fs.isA(someObject)).toBeNull();
128 });
129
130 // === Tests for isS()
131 it('isS(): null for undefined', () => {
132 expect(fs.isS(undefined)).toBeNull();
133 });
134 it('isS(): null for null', () => {
135 expect(fs.isS(null)).toBeNull();
136 });
137 it('isS(): null for function', () => {
138 expect(fs.isS(someFunction)).toBeNull();
139 });
140 it('isS(): the reference for string', () => {
141 expect(fs.isS(someString)).toBe(someString);
142 });
143 it('isS(): null for number', () => {
144 expect(fs.isS(someNumber)).toBeNull();
145 });
146 it('isS(): null for Date', () => {
147 expect(fs.isS(someDate)).toBeNull();
148 });
149 it('isS(): null for array', () => {
150 expect(fs.isS(someArray)).toBeNull();
151 });
152 it('isS(): null for object', () => {
153 expect(fs.isS(someObject)).toBeNull();
154 });
155
156 // === Tests for isO()
157 it('isO(): null for undefined', () => {
158 expect(fs.isO(undefined)).toBeNull();
159 });
160 it('isO(): null for null', () => {
161 expect(fs.isO(null)).toBeNull();
162 });
163 it('isO(): null for function', () => {
164 expect(fs.isO(someFunction)).toBeNull();
165 });
166 it('isO(): null for string', () => {
167 expect(fs.isO(someString)).toBeNull();
168 });
169 it('isO(): null for number', () => {
170 expect(fs.isO(someNumber)).toBeNull();
171 });
172 it('isO(): null for Date', () => {
173 expect(fs.isO(someDate)).toBeNull();
174 });
175 it('isO(): null for array', () => {
176 expect(fs.isO(someArray)).toBeNull();
177 });
178 it('isO(): the reference for object', () => {
179 expect(fs.isO(someObject)).toBe(someObject);
180 });
181
182
183 // === Tests for contains()
184 it('contains(): false for non-array', () => {
185 expect(fs.contains(null, 1)).toBeFalsy();
186 });
187 it('contains(): true for contained item', () => {
188 expect(fs.contains(someArray, 1)).toBeTruthy();
189 expect(fs.contains(stringArray, 'bar')).toBeTruthy();
190 });
191 it('contains(): false for non-contained item', () => {
192 expect(fs.contains(someArray, 109)).toBeFalsy();
193 expect(fs.contains(stringArray, 'zonko')).toBeFalsy();
194 });
195
196 // === Tests for areFunctions()
197 it('areFunctions(): true for empty-array', () => {
198 expect(fs.areFunctions({}, [])).toBeTruthy();
199 });
200 it('areFunctions(): true for some api', () => {
201 expect(fs.areFunctions({
202 a: () => {},
203 b: () => {}
204 }, ['b', 'a'])).toBeTruthy();
205 });
206 it('areFunctions(): false for some other api', () => {
207 expect(fs.areFunctions({
208 a: () => {},
209 b: 'not-a-function'
210 }, ['b', 'a'])).toBeFalsy();
211 });
212 it('areFunctions(): extraneous stuff NOT ignored', () => {
213 expect(fs.areFunctions({
214 a: () => {},
215 b: () => {},
216 c: 1,
217 d: 'foo'
218 }, ['a', 'b'])).toBeFalsy();
219 });
220 it('areFunctions(): extraneous stuff ignored (alternate fn)', () => {
221 expect(fs.areFunctionsNonStrict({
222 a: () => {},
223 b: () => {},
224 c: 1,
225 d: 'foo'
226 }, ['a', 'b'])).toBeTruthy();
227 });
228
229 // == use the now-tested areFunctions() on our own api:
230 it('should define api functions', () => {
231 expect(fs.areFunctions(fs, [
232 'isF', 'isA', 'isS', 'isO', 'contains',
233 'areFunctions', 'areFunctionsNonStrict', 'windowSize',
234 'isMobile', 'isChrome', 'isChromeHeadless', 'isSafari',
235 'isFirefox', 'parseDebugFlags',
236 'debugOn', 'debug', 'find', 'inArray', 'removeFromArray',
237 'isEmptyObject', 'cap', 'noPx', 'noPxStyle', 'endsWith',
Sean Condonf4f54a12018-10-10 23:25:46 +0100238 'inEvilList', 'analyze', 'sanitize', 'sameObjProps', 'containsObj',
239 'addToTrie', 'removeFromTrie', 'trieLookup'
Sean Condonfd6d11b2018-06-02 20:29:49 +0100240// 'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'sameObjProps', 'containsObj', 'cap',
241// 'eecode', 'noPx', 'noPxStyle', 'endsWith', 'addToTrie', 'removeFromTrie', 'trieLookup',
242// 'classNames', 'extend', 'sanitize'
243 ])).toBeTruthy();
244 });
245
246
247 // === Tests for windowSize()
248 it('windowSize(): adjust height', () => {
249 const dim = fs.windowSize(50);
250 expect(dim.width).toEqual(400);
251 expect(dim.height).toEqual(150);
252 });
253
254 it('windowSize(): adjust width', () => {
255 const dim = fs.windowSize(0, 50);
256 expect(dim.width).toEqual(350);
257 expect(dim.height).toEqual(200);
258 });
259
260 it('windowSize(): adjust width and height', () => {
261 const dim = fs.windowSize(101, 201);
262 expect(dim.width).toEqual(199);
263 expect(dim.height).toEqual(99);
264 });
265
266 // === Tests for isMobile()
267 const uaMap = {
268 chrome: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) ' +
269 'AppleWebKit/537.36 (KHTML, like Gecko) ' +
270 'Chrome/41.0.2272.89 Safari/537.36',
271
272 iPad: 'Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) ' +
273 'AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 ' +
274 'Mobile/11A465 Safari/9537.53',
275
276 iPhone: 'Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) ' +
277 'AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 ' +
278 'Mobile/11A465 Safari/9537.53'
279 };
280
281 function setUa(key) {
282 const str = uaMap[key];
283 expect(str).toBeTruthy();
284 (<any>mockWindow.navigator).userAgent = str;
285 }
286
287 it('isMobile(): should be false for Chrome on Mac OS X', () => {
288 setUa('chrome');
289 expect(fs.isMobile()).toBe(false);
290 });
291 it('isMobile(): should be true for Safari on iPad', () => {
292 setUa('iPad');
293 expect(fs.isMobile()).toBe(true);
294 });
295 it('isMobile(): should be true for Safari on iPhone', () => {
296 setUa('iPhone');
297 expect(fs.isMobile()).toBe(true);
298 });
299
300 // === Tests for find()
301 const dataset = [
302 { id: 'foo', name: 'Furby'},
303 { id: 'bar', name: 'Barbi'},
304 { id: 'baz', name: 'Basil'},
305 { id: 'goo', name: 'Gabby'},
306 { id: 'zoo', name: 'Zevvv'}
307 ];
308
309 it('should not find ooo', () => {
310 expect(fs.find('ooo', dataset)).toEqual(-1);
311 });
312 it('should find foo', () => {
313 expect(fs.find('foo', dataset)).toEqual(0);
314 });
315 it('should find zoo', () => {
316 expect(fs.find('zoo', dataset)).toEqual(4);
317 });
318
319 it('should not find Simon', () => {
320 expect(fs.find('Simon', dataset, 'name')).toEqual(-1);
321 });
322 it('should find Furby', () => {
323 expect(fs.find('Furby', dataset, 'name')).toEqual(0);
324 });
325 it('should find Zevvv', () => {
326 expect(fs.find('Zevvv', dataset, 'name')).toEqual(4);
327 });
328
329
330 // === Tests for inArray()
331 const objRef = { x: 1, y: 2 };
332 const array = [1, 3.14, 'hey', objRef, 'there', true];
333 const array2 = ['b', 'a', 'd', 'a', 's', 's'];
334
335 it('should not find HOO', () => {
336 expect(fs.inArray('HOO', array)).toEqual(-1);
337 });
338 it('should find 1', () => {
339 expect(fs.inArray(1, array)).toEqual(0);
340 });
341 it('should find pi', () => {
342 expect(fs.inArray(3.14, array)).toEqual(1);
343 });
344 it('should find hey', () => {
345 expect(fs.inArray('hey', array)).toEqual(2);
346 });
347 it('should find the object', () => {
348 expect(fs.inArray(objRef, array)).toEqual(3);
349 });
350 it('should find there', () => {
351 expect(fs.inArray('there', array)).toEqual(4);
352 });
353 it('should find true', () => {
354 expect(fs.inArray(true, array)).toEqual(5);
355 });
356
357 it('should find the first occurrence A', () => {
358 expect(fs.inArray('a', array2)).toEqual(1);
359 });
360 it('should find the first occurrence S', () => {
361 expect(fs.inArray('s', array2)).toEqual(4);
362 });
363 it('should not find X', () => {
364 expect(fs.inArray('x', array2)).toEqual(-1);
365 });
366
367 // === Tests for removeFromArray()
368 it('should keep the array the same, for non-match', () => {
369 const array1 = [1, 2, 3];
370 expect(fs.removeFromArray(4, array1)).toBe(false);
371 expect(array1).toEqual([1, 2, 3]);
372 });
373 it('should remove a value', () => {
374 const array1a = [1, 2, 3];
375 expect(fs.removeFromArray(2, array1a)).toBe(true);
376 expect(array1a).toEqual([1, 3]);
377 });
378 it('should remove the first occurrence', () => {
379 const array1b = ['x', 'y', 'z', 'z', 'y'];
380 expect(fs.removeFromArray('y', array1b)).toBe(true);
381 expect(array1b).toEqual(['x', 'z', 'z', 'y']);
382 expect(fs.removeFromArray('x', array1b)).toBe(true);
383 expect(array1b).toEqual(['z', 'z', 'y']);
384 });
385
386 // === Tests for isEmptyObject()
387 it('should return true if an object is empty', () => {
388 expect(fs.isEmptyObject({})).toBe(true);
389 });
390 it('should return false if an object is not empty', () => {
391 expect(fs.isEmptyObject({foo: 'bar'})).toBe(false);
392 });
393
394 // === Tests for cap()
395 it('should ignore non-alpha', () => {
396 expect(fs.cap('123')).toEqual('123');
397 });
398 it('should capitalize first char', () => {
399 expect(fs.cap('Foo')).toEqual('Foo');
400 expect(fs.cap('foo')).toEqual('Foo');
401 expect(fs.cap('foo bar')).toEqual('Foo bar');
402 expect(fs.cap('FOO BAR')).toEqual('Foo bar');
403 expect(fs.cap('foo Bar')).toEqual('Foo bar');
404 });
405
406 // === Tests for noPx()
407 it('should return the value without px suffix', () => {
408 expect(fs.noPx('10px')).toBe(10);
409 expect(fs.noPx('500px')).toBe(500);
410 expect(fs.noPx('-80px')).toBe(-80);
411 });
412
413 // === Tests for noPxStyle()
414 it('should give a style\'s property without px suffix', () => {
415 const d3Elem = d3.select('body')
416 .append('div')
417 .attr('id', 'fooElem')
418 .style('width', '500px')
419 .style('height', '200px')
420 .style('font-size', '12px');
421 expect(fs.noPxStyle(d3Elem, 'width')).toBe(500);
422 expect(fs.noPxStyle(d3Elem, 'height')).toBe(200);
423 expect(fs.noPxStyle(d3Elem, 'font-size')).toBe(12);
424 d3.select('#fooElem').remove();
425 });
426
427 // === Tests for endsWith()
428 it('should return true if string ends with foo', () => {
429 expect(fs.endsWith('barfoo', 'foo')).toBe(true);
430 });
431
432 it('should return false if string doesnt end with foo', () => {
433 expect(fs.endsWith('barfood', 'foo')).toBe(false);
434 });
435
436 // === Tests for sanitize()
437 it('should return foo', () => {
438 expect(fs.sanitize('foo')).toEqual('foo');
439 });
440 it('should retain < b > tags', () => {
441 const str = 'foo <b>bar</b> baz';
442 expect(fs.sanitize(str)).toEqual(str);
443 });
444 it('should retain < i > tags', () => {
445 const str = 'foo <i>bar</i> baz';
446 expect(fs.sanitize(str)).toEqual(str);
447 });
448 it('should retain < p > tags', () => {
449 const str = 'foo <p>bar</p> baz';
450 expect(fs.sanitize(str)).toEqual(str);
451 });
452 it('should retain < em > tags', () => {
453 const str = 'foo <em>bar</em> baz';
454 expect(fs.sanitize(str)).toEqual(str);
455 });
456 it('should retain < strong > tags', () => {
457 const str = 'foo <strong>bar</strong> baz';
458 expect(fs.sanitize(str)).toEqual(str);
459 });
460
461 it('should reject < a > tags', () => {
462 expect(fs.sanitize('test <a href="hah">something</a> this'))
463 .toEqual('test something this');
464 });
465
466 it('should log a warning for < script > tags', () => {
467 expect(fs.sanitize('<script>alert("foo");</script>'))
468 .toEqual('');
469 expect(logServiceSpy.warn).toHaveBeenCalledWith(
470 'Unsanitary HTML input -- <script> detected!'
471 );
472 });
473 it('should log a warning for < style > tags', () => {
474 expect(fs.sanitize('<style> h1 {color:red;} </style>'))
475 .toEqual('');
476 expect(logServiceSpy.warn).toHaveBeenCalledWith(
477 'Unsanitary HTML input -- <style> detected!'
478 );
479 });
480
481 it('should log a warning for < iframe > tags', () => {
482 expect(fs.sanitize('Foo<iframe><body><h1>fake</h1></body></iframe>Bar'))
483 .toEqual('FooBar');
484 expect(logServiceSpy.warn).toHaveBeenCalledWith(
485 'Unsanitary HTML input -- <iframe> detected!'
486 );
487 });
488
489 it('should completely strip < script >, remove < a >, retain < i >', () => {
490 expect(fs.sanitize('Hey <i>this</i> is <script>alert("foo");</script> <a href="meh">cool</a>'))
491 .toEqual('Hey <i>this</i> is cool');
492 });
Sean Condon83fc39f2018-04-19 18:56:13 +0100493});