cloudinary-video-player 1.5.5 → 1.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +140 -21
  2. package/dist/cld-video-player.css +229 -2079
  3. package/dist/cld-video-player.js +200 -483
  4. package/dist/cld-video-player.light.css +231 -2081
  5. package/dist/cld-video-player.light.js +71 -23
  6. package/dist/cld-video-player.light.min.css +1 -1
  7. package/dist/cld-video-player.light.min.js +2 -23
  8. package/dist/cld-video-player.light.min.js.LICENSE.txt +23 -0
  9. package/dist/cld-video-player.min.css +1 -1
  10. package/dist/cld-video-player.min.js +2 -27
  11. package/dist/cld-video-player.min.js.LICENSE.txt +26 -0
  12. package/docs/interaction-area.html +104 -64
  13. package/package.json +23 -23
  14. package/src/assets/styles/components/interaction-areas.scss +165 -0
  15. package/src/assets/styles/components/themedButton.scss +48 -0
  16. package/src/assets/styles/icons.scss +149 -217
  17. package/src/assets/styles/main.scss +8 -30
  18. package/src/components/interaction-area/interaction-area.const.js +30 -0
  19. package/src/components/interaction-area/interaction-area.service.js +225 -0
  20. package/src/components/interaction-area/interaction-area.utils.js +236 -0
  21. package/src/components/logoButton/logo-button.js +3 -6
  22. package/src/components/themeButton/themedButton.const.js +3 -0
  23. package/src/components/themeButton/themedButton.js +25 -0
  24. package/src/config/defaults.js +3 -2
  25. package/src/extended-events.js +3 -0
  26. package/src/plugins/cloudinary/common.js +14 -6
  27. package/src/plugins/cloudinary/models/video-source/video-source.js +16 -12
  28. package/src/plugins/cloudinary/models/video-source/video-source.utils.js +28 -1
  29. package/src/plugins/colors/index.js +6 -1
  30. package/src/plugins/dash/videojs-dash.js +1 -1
  31. package/src/plugins/floating-player/index.js +3 -1
  32. package/src/utils/array.js +21 -0
  33. package/src/utils/dom.js +41 -2
  34. package/src/utils/object.js +26 -0
  35. package/src/utils/time.js +28 -1
  36. package/src/utils/type-inference.js +5 -1
  37. package/src/validators/validators-functions.js +48 -0
  38. package/src/validators/validators-types.js +78 -0
  39. package/src/validators/validators.js +110 -0
  40. package/src/video-player.const.js +23 -16
  41. package/src/video-player.js +47 -127
  42. package/src/video-player.utils.js +9 -70
  43. package/test/isValidConfig.test.js +224 -0
  44. package/test/unit/utils.test.js +27 -0
  45. package/test/unit/videoSource.test.js +155 -0
  46. package/types/video-player.d.ts +1 -1
@@ -10,23 +10,18 @@ import PlaylistWidget from './components/playlist/playlist-widget';
10
10
  import qualitySelector from './components/qualitySelector/qualitySelector.js';
11
11
  // #endif
12
12
  import VideoSource from './plugins/cloudinary/models/video-source/video-source';
13
- import { createElement } from './utils/dom';
14
- import { isFunction, isString, noop, isPlainObject } from './utils/type-inference';
13
+ import { isFunction, isString, isPlainObject } from './utils/type-inference';
15
14
  import {
16
- addMetadataTrack,
17
15
  extractOptions,
18
- getMetaDataTracker,
19
16
  getResolveVideoElement,
20
- getInteractionAreaItem,
21
- getZoomTransformation,
22
- overrideDefaultVideojsComponents,
23
- setInteractionAreasContainer
17
+ overrideDefaultVideojsComponents
24
18
  } from './video-player.utils';
25
- import {
26
- FLUID_CLASS_NAME,
27
- TEMPLATE_INTERACTION_AREAS_VTT,
28
- INTERACTION_AREAS_CONTAINER_CLASS_NAME
29
- } from './video-player.const';
19
+ import { FLOATING_TO, FLUID_CLASS_NAME } from './video-player.const';
20
+ // #if (!process.env.WEBPACK_BUILD_LIGHT)
21
+ import { interactionAreaService } from './components/interaction-area/interaction-area.service';
22
+ // #endif
23
+ import { isValidConfig } from './validators/validators-functions';
24
+ import { playerValidators, sourceValidators } from './validators/validators';
30
25
 
31
26
 
32
27
  // Register all plugins
@@ -73,11 +68,9 @@ class VideoPlayer extends Utils.mixin(Eventable) {
73
68
  constructor(elem, initOptions, ready) {
74
69
  super();
75
70
 
76
- this._isZoomed = false;
77
- this.unZoom = noop;
78
- this._setStaticInteractionAreas = noop;
79
71
  this._playlistWidget = null;
80
72
  this.nbCalls = 0;
73
+ this._firstPlayed = false;
81
74
 
82
75
  this.videoElement = getResolveVideoElement(elem);
83
76
 
@@ -101,6 +94,16 @@ class VideoPlayer extends Utils.mixin(Eventable) {
101
94
 
102
95
  this.videojs = videojs(this.videoElement, this._videojsOptions);
103
96
 
97
+ // to do, should be change by isValidConfig
98
+ this._isPlayerConfigValid = true;
99
+
100
+ isValidConfig(this.options, playerValidators);
101
+
102
+ if (!this._isPlayerConfigValid) {
103
+ this.videojs.error('invalid player configuration');
104
+ return;
105
+ }
106
+
104
107
  if (this._videojsOptions.muted) {
105
108
  this.videojs.volume(0.4);
106
109
  }
@@ -115,6 +118,10 @@ class VideoPlayer extends Utils.mixin(Eventable) {
115
118
  imaAdsLoaded: (typeof google === 'object' && typeof google.ima === 'object')
116
119
  };
117
120
 
121
+ // #if (!process.env.WEBPACK_BUILD_LIGHT)
122
+ this.interactionArea = interactionAreaService(this, this.playerOptions, this._videojsOptions);
123
+ // #endif
124
+
118
125
  this._setCssClasses();
119
126
  this._initPlugins(loaded);
120
127
  this._initPlaylistWidget();
@@ -127,7 +134,7 @@ class VideoPlayer extends Utils.mixin(Eventable) {
127
134
  this.videojs.on('error', () => {
128
135
  const error = this.videojs.error();
129
136
  if (error) {
130
- const type = this.videojs.cloudinary.currentSourceType();
137
+ const type = this._isPlayerConfigValid && this.videojs.cloudinary.currentSourceType();
131
138
  if (error.code === 4 && (type === 'VideoSource' || type === 'AudioSource')) {
132
139
  this.videojs.error(null);
133
140
  Utils.handleCldError(this, this.playerOptions);
@@ -152,13 +159,9 @@ class VideoPlayer extends Utils.mixin(Eventable) {
152
159
  ready(this);
153
160
  }
154
161
 
155
- this.videojs.on('sourcechanged', () => {
156
- this._updateInteractionAreasTrack();
157
- });
158
-
159
- this.videojs.on('ended', () => {
160
- this.unZoom();
161
- });
162
+ // #if (!process.env.WEBPACK_BUILD_LIGHT)
163
+ this.interactionArea.init();
164
+ // #endif
162
165
  });
163
166
 
164
167
  if (this.adsEnabled && Object.keys(this.playerOptions.ads).length > 0 && typeof this.videojs.ima === 'object') {
@@ -191,22 +194,8 @@ class VideoPlayer extends Utils.mixin(Eventable) {
191
194
  this._initSeekThumbs();
192
195
  }
193
196
 
194
- _updateInteractionAreasTrack() {
195
- const interactionAreasConfig = this.getInteractionAreasConfig();
196
- this._currentTrack && this.videojs.removeRemoteTextTrack(this._currentTrack);
197
-
198
- if (!interactionAreasConfig) {
199
- return;
200
- }
201
-
202
- const vttUrl = interactionAreasConfig.vttUrl || TEMPLATE_INTERACTION_AREAS_VTT[interactionAreasConfig.template];
203
-
204
- this.videojs.removeRemoteTextTrack(this._currentTrack);
205
-
206
- if (!this._isZoomed && interactionAreasConfig.enable && vttUrl) {
207
- this._currentTrack = addMetadataTrack(this.videojs, vttUrl);
208
- this.addCueListener(interactionAreasConfig);
209
- }
197
+ _isFullScreen() {
198
+ return this.videojs.player().isFullscreen();
210
199
  }
211
200
 
212
201
  _initIma (loaded) {
@@ -367,6 +356,11 @@ class VideoPlayer extends Utils.mixin(Eventable) {
367
356
  _initAnalytics() {
368
357
  const analyticsOpts = this.playerOptions.analytics;
369
358
 
359
+ if (!window.ga && analyticsOpts) {
360
+ console.error('Google Analytics script is missing');
361
+ return;
362
+ }
363
+
370
364
  if (analyticsOpts) {
371
365
  const opts = typeof analyticsOpts === 'object' ? analyticsOpts : {};
372
366
  this.videojs.analytics(opts);
@@ -377,7 +371,7 @@ class VideoPlayer extends Utils.mixin(Eventable) {
377
371
  if (!this.isVideoReady()) {
378
372
  if (this.nbCalls < maxNumberOfCalls) {
379
373
  this.nbCalls++;
380
- this.reTryId = this.videojs.setTimeout(this.reTryVideo, timeout);
374
+ this.reTryId = this.videojs.setTimeout(() => this.reTryVideo(maxNumberOfCalls, timeout), timeout);
381
375
  } else {
382
376
  let e = new Error('Video is not ready please try later');
383
377
  this.videojs.trigger('error', e);
@@ -446,7 +440,7 @@ class VideoPlayer extends Utils.mixin(Eventable) {
446
440
  }
447
441
 
448
442
  _initFloatingPlayer() {
449
- if (this.playerOptions.floatingWhenNotVisible) {
443
+ if (this.playerOptions.floatingWhenNotVisible !== FLOATING_TO.NONE) {
450
444
  this.videojs.floatingPlayer({ 'floatTo': this.playerOptions.floatingWhenNotVisible });
451
445
  }
452
446
  }
@@ -509,91 +503,6 @@ class VideoPlayer extends Utils.mixin(Eventable) {
509
503
  return this.options.playerOptions;
510
504
  }
511
505
 
512
- _setGoBackButton() {
513
- const button = createElement('div', { 'class': 'go-back-button' });
514
-
515
- button.addEventListener('click', () => {
516
- this.unZoom();
517
- }, false);
518
-
519
- const tracksContainer = createElement('div', { 'class': INTERACTION_AREAS_CONTAINER_CLASS_NAME }, button);
520
- setInteractionAreasContainer(this.videojs, tracksContainer);
521
- }
522
-
523
- addInteractionAreas(interactionAreas, interactionAreasOptions) {
524
- this._setStaticInteractionAreas = () => {
525
- this._addInteractionAreasItems(interactionAreas, interactionAreasOptions);
526
- };
527
-
528
- this._addInteractionAreasItems(interactionAreas, interactionAreasOptions);
529
- }
530
-
531
- getInteractionAreasConfig() {
532
- return this.videojs.currentSource().cldSrc.getInteractionAreas();
533
- }
534
-
535
- _onZoom(src, newOption, item) {
536
- const currentSource = this.videojs.currentSource();
537
- const { cldSrc } = currentSource;
538
- const currentSrcOptions = cldSrc.getInitOptions();
539
- const option = newOption || { transformation: currentSrcOptions.transformation.toOptions() };
540
- const transformation = !src && getZoomTransformation(this.videoElement, item);
541
- const sourceOptions = transformation ? videojs.mergeOptions({ transformation }, option) : option;
542
-
543
- const newSource = cldSrc.isRawUrl ? currentSource.src : { publicId: cldSrc.publicId() };
544
- this.source(transformation ? { publicId: cldSrc.publicId() } : src, sourceOptions).play();
545
-
546
- this._isZoomed = true;
547
-
548
- this._setGoBackButton();
549
-
550
- this.unZoom = () => {
551
- if (this._isZoomed) {
552
- this._isZoomed = false;
553
- this._setStaticInteractionAreas();
554
- this.source(newSource, currentSrcOptions).play();
555
- }
556
- };
557
- }
558
-
559
- _addInteractionAreasItems(interactionAreasData, interactionAreasOptions = {}) {
560
- const interactionAreasItems = interactionAreasData.map((item, index) => {
561
- return getInteractionAreaItem(item, (event) => {
562
- interactionAreasOptions.onClick && interactionAreasOptions.onClick({
563
- item,
564
- index,
565
- event,
566
- zoom: (source, option) => {
567
- this._onZoom(source, option, item);
568
- }
569
- });
570
- });
571
- });
572
-
573
- const interactionAreasContainer = createElement('div', { 'class': INTERACTION_AREAS_CONTAINER_CLASS_NAME }, interactionAreasItems);
574
- setInteractionAreasContainer(this.videojs, interactionAreasContainer);
575
- }
576
-
577
- addCueListener(interactionAreasConfig) {
578
- const textTracks = this.videojs.textTracks();
579
- if (!textTracks.length) {
580
- return;
581
- }
582
-
583
- const track = getMetaDataTracker(textTracks);
584
-
585
- if (!track) {
586
- return;
587
- }
588
-
589
- track.mode = 'hidden';
590
-
591
- track.addEventListener('cuechange', () => {
592
- const tracksData = JSON.parse(track.activeCues[0].text);
593
- this._addInteractionAreasItems(tracksData, interactionAreasConfig);
594
- });
595
- }
596
-
597
506
  currentPublicId() {
598
507
  return this.videojs.cloudinary.currentPublicId();
599
508
  }
@@ -607,6 +516,17 @@ class VideoPlayer extends Utils.mixin(Eventable) {
607
516
  }
608
517
 
609
518
  source(publicId, options = {}) {
519
+ if (!this._isPlayerConfigValid) {
520
+ return;
521
+ }
522
+
523
+ const isSourceConfigValid = isValidConfig(options, sourceValidators);
524
+
525
+ if (!isSourceConfigValid) {
526
+ this.videojs.error('invalid source configuration');
527
+ return;
528
+ }
529
+
610
530
  if (publicId instanceof VideoSource) {
611
531
  return this.videojs.cloudinary.source(publicId, options);
612
532
  }
@@ -1,4 +1,3 @@
1
- import { createElement } from './utils/dom';
2
1
  import videojs from 'video.js';
3
2
  import Utils from './utils';
4
3
  import defaults from './config/defaults';
@@ -6,74 +5,11 @@ import {
6
5
  CLOUDINARY_PARAMS,
7
6
  DEFAULT_HLS_OPTIONS,
8
7
  PLAYER_PARAMS,
9
- INTERACTION_AREAS_CONTAINER_CLASS_NAME,
10
- FLUID_CLASS_NAME
8
+ FLUID_CLASS_NAME,
9
+ AUTO_PLAY_MODE
11
10
  } from './video-player.const';
12
11
  import { isString } from './utils/type-inference';
13
12
 
14
- export const getMetaDataTracker = (textTracks) => {
15
- for (let i = 0; textTracks.length; i++) {
16
- const value = textTracks[i];
17
- if (value.kind === 'metadata') {
18
- return value;
19
- }
20
- }
21
- };
22
-
23
-
24
- export const setInteractionAreasContainer = (videojs, newInteractionAreasContainer) => {
25
- const videoContainer = videojs.el();
26
- const currentInteractionAreasContainer = videoContainer.querySelector(`.${INTERACTION_AREAS_CONTAINER_CLASS_NAME}`);
27
-
28
- if (currentInteractionAreasContainer) {
29
- currentInteractionAreasContainer.replaceWith(newInteractionAreasContainer);
30
- } else {
31
- videoContainer.append(newInteractionAreasContainer);
32
- }
33
- };
34
-
35
- export const percentageToFixedValue = (outOf, value) => (outOf / (100 / +value));
36
-
37
- export const getZoomTransformation = (videoElement, interactionAreaItem) => {
38
-
39
- const { videoHeight, videoWidth } = videoElement;
40
-
41
- const itemX = percentageToFixedValue(videoWidth, interactionAreaItem.left);
42
- const itemY = percentageToFixedValue(videoHeight, interactionAreaItem.top);
43
- const itemWidth = percentageToFixedValue(videoWidth, interactionAreaItem.width);
44
- const itemHeight = percentageToFixedValue(videoHeight, interactionAreaItem.height);
45
-
46
- const videoAspectRatio = videoWidth / videoHeight;
47
- const itemAspectRatio = itemWidth / itemHeight;
48
-
49
- const width = Math.round(itemAspectRatio > 1 || videoAspectRatio > 1 ? itemHeight * itemAspectRatio : itemWidth);
50
- const height = Math.round(width / videoAspectRatio);
51
-
52
- const x = Math.round(itemX - (width - itemWidth) / 2);
53
- const y = Math.round(itemY - (height - itemHeight) / 2);
54
-
55
- return {
56
- width,
57
- height,
58
- x: Math.min(Math.max(x, 0), videoWidth - width),
59
- y: Math.min(Math.max(y, 0), videoHeight - height),
60
- crop: 'crop'
61
- };
62
- };
63
-
64
- export const getInteractionAreaItem = (item, onClick) => {
65
- const element = createElement('div', { class: 'video-player-interaction-area' });
66
- element.style.left = `${item.left}%`;
67
- element.style.top = `${item.top}%`;
68
- element.style.width = `${item.width}%`;
69
- element.style.height = `${item.height}%`;
70
-
71
- element.addEventListener('click', onClick, false);
72
-
73
- return element;
74
- };
75
-
76
-
77
13
  export const addMetadataTrack = (videoJs, vttSource) => {
78
14
  return videoJs.addRemoteTextTrack({
79
15
  kind: 'metadata',
@@ -83,6 +19,10 @@ export const addMetadataTrack = (videoJs, vttSource) => {
83
19
  }, true).track;
84
20
  };
85
21
 
22
+ export const isLight = (opts) => {
23
+ return opts.class.indexOf('cld-video-player-skin-light') > -1 || opts.skin === 'light';
24
+ };
25
+
86
26
  export const getResolveVideoElement = (elem) => {
87
27
  if (isString(elem)) {
88
28
  let id = elem;
@@ -117,11 +57,11 @@ export const normalizeAutoplay = (options) => {
117
57
  const autoplayMode = options.autoplayMode;
118
58
  if (autoplayMode) {
119
59
  switch (autoplayMode) {
120
- case 'always':
60
+ case AUTO_PLAY_MODE.ALWAYS:
121
61
  options.autoplay = true;
122
62
  break;
123
- case 'on-scroll':
124
- case 'never':
63
+ case AUTO_PLAY_MODE.ON_SCROLL:
64
+ case AUTO_PLAY_MODE.NEVER:
125
65
  default:
126
66
  options.autoplay = false;
127
67
  }
@@ -134,7 +74,6 @@ export const extractOptions = (elem, options) => {
134
74
  if (videojs.dom.hasClass(elem, FLUID_CLASS_NAME) || videojs.dom.hasClass(elem, 'vjs-fluid')) {
135
75
  options.fluid = true;
136
76
  }
137
-
138
77
  // Default HLS options < Default options < Markup options < Player options
139
78
  options = Utils.assign({}, DEFAULT_HLS_OPTIONS, defaults, elemOptions, options);
140
79
 
@@ -0,0 +1,224 @@
1
+ import { isValidConfig } from '../src/validators/validators-functions';
2
+ import { validator } from '../src/validators/validators-types';
3
+ import { noop } from '../src/utils/type-inference';
4
+
5
+ describe('test isValidConfig method', () => {
6
+
7
+ describe('should be a number', () => {
8
+
9
+ const validators = {
10
+ test: validator.isNumber
11
+ };
12
+
13
+ it('is valid number', () => {
14
+ const isValid = isValidConfig({
15
+ test: 1
16
+ }, validators);
17
+
18
+ expect(isValid).toBe(true);
19
+ });
20
+
21
+ it('is invalid number', () => {
22
+ const isValid = isValidConfig({
23
+ test: 'a'
24
+ }, validators);
25
+
26
+ expect(isValid).toBe(false);
27
+ });
28
+ });
29
+
30
+ describe('should be a string', () => {
31
+
32
+ const validators = {
33
+ test: validator.isString
34
+ };
35
+
36
+ it('is valid string', () => {
37
+ const isValid = isValidConfig({
38
+ test: 'a'
39
+ }, validators);
40
+
41
+ expect(isValid).toBe(true);
42
+ });
43
+
44
+ it('is invalid string', () => {
45
+ const isValid = isValidConfig({
46
+ test: 1
47
+ }, validators);
48
+
49
+ expect(isValid).toBe(false);
50
+ });
51
+ });
52
+
53
+ describe('should be a boolean', () => {
54
+
55
+ const validators = {
56
+ test: validator.isBoolean
57
+ };
58
+
59
+ it('is valid boolean', () => {
60
+ const isValid = isValidConfig({
61
+ test: true
62
+ }, validators);
63
+
64
+ expect(isValid).toBe(true);
65
+ });
66
+
67
+ it('is invalid boolean', () => {
68
+ const isValid = isValidConfig({
69
+ test: 1
70
+ }, validators);
71
+
72
+ expect(isValid).toBe(false);
73
+ });
74
+ });
75
+
76
+ describe('should be a function', () => {
77
+
78
+ const validators = {
79
+ test: validator.isFunction
80
+ };
81
+
82
+ it('is valid function', () => {
83
+ const isValid = isValidConfig({
84
+ test: noop
85
+ }, validators);
86
+
87
+ expect(isValid).toBe(true);
88
+ });
89
+
90
+ it('is invalid function', () => {
91
+ const isValid = isValidConfig({
92
+ test: 1
93
+ }, validators);
94
+
95
+ expect(isValid).toBe(false);
96
+ });
97
+ });
98
+
99
+ describe('should be an array Of strings', () => {
100
+
101
+ const validators = {
102
+ test: validator.isArrayOfStrings
103
+ };
104
+
105
+ it('is valid array of strings', () => {
106
+ const isValid = isValidConfig({
107
+ test: ['a', 'b', 'c']
108
+ }, validators);
109
+
110
+ expect(isValid).toBe(true);
111
+ });
112
+
113
+ it('is invalid array of strings', () => {
114
+ const isValid = isValidConfig({
115
+ test: 1
116
+ }, validators);
117
+
118
+ expect(isValid).toBe(false);
119
+ });
120
+ });
121
+
122
+ describe('should be an array Of numbers', () => {
123
+
124
+ const validators = {
125
+ test: validator.isArrayOfNumbers
126
+ };
127
+
128
+ it('is valid array of numbers', () => {
129
+ const isValid = isValidConfig({
130
+ test: [1, 1, 1]
131
+ }, validators);
132
+
133
+ expect(isValid).toBe(true);
134
+ });
135
+
136
+ it('is invalid array of numbers', () => {
137
+ const isValid = isValidConfig({
138
+ test: 1
139
+ }, validators);
140
+
141
+ expect(isValid).toBe(false);
142
+ });
143
+ });
144
+
145
+ describe('should be an object', () => {
146
+ const validators = {
147
+ test: validator.isPlainObject
148
+ };
149
+
150
+ it('is valid object', () => {
151
+ const isValid = isValidConfig({
152
+ test: { a: 1 }
153
+ }, validators);
154
+
155
+ expect(isValid).toBe(true);
156
+ });
157
+
158
+ it('is invalid object', () => {
159
+ const isValid = isValidConfig({
160
+ test: 1
161
+ }, validators);
162
+
163
+ expect(isValid).toBe(false);
164
+ });
165
+ });
166
+
167
+ describe('should be an array of object', () => {
168
+ const validators = {
169
+ test: validator.isArrayOfObjects({
170
+ a: validator.isString,
171
+ b: validator.isNumber,
172
+ c: validator.isBoolean
173
+ })
174
+ };
175
+ it('is valid array of object', () => {
176
+ const isValid = isValidConfig({
177
+ test: [{ a: 'a', b: 1, c: true }]
178
+ }, validators);
179
+
180
+ expect(isValid).toBe(true);
181
+ });
182
+
183
+ it('is invalid object', () => {
184
+ const isValid = isValidConfig({
185
+ test: [{ a: 'a', b: 1, c: 1 }]
186
+ }, validators);
187
+
188
+ expect(isValid).toBe(false);
189
+ });
190
+ });
191
+
192
+
193
+ describe('or validator', () => {
194
+ const validators = {
195
+ test: validator.or(validator.isNumber, validator.isString)
196
+ };
197
+
198
+ it('could be a number', () => {
199
+ const isValid = isValidConfig({
200
+ test: 1
201
+ }, validators);
202
+
203
+ expect(isValid).toBe(true);
204
+ });
205
+
206
+ it('could be a string', () => {
207
+ const isValid = isValidConfig({
208
+ test: 'a'
209
+ }, validators);
210
+
211
+ expect(isValid).toBe(true);
212
+ });
213
+
214
+ it('is invalid or', () => {
215
+ const isValid = isValidConfig({
216
+ test: true
217
+ }, validators);
218
+
219
+ expect(isValid).toBe(false);
220
+ });
221
+ });
222
+
223
+
224
+ });
@@ -0,0 +1,27 @@
1
+ import { get } from '../../src/utils/object.js';
2
+
3
+ describe('test get utils function', () => {
4
+ const value = { a: { b: [{ c: 2 }], d: undefined } };
5
+
6
+ it('should get primitive value', () => {
7
+ expect(get(value, 'a.b.0.c')).toBe(value.a.b[0].c);
8
+ });
9
+
10
+ it('should get object value', () => {
11
+ expect(get(value, 'a.b')).toBe(value.a.b);
12
+ });
13
+
14
+ it('should get default value', () => {
15
+ const defaultValue = 10;
16
+ expect(get(value, 'a.c', defaultValue)).toBe(defaultValue);
17
+ });
18
+
19
+ it('should get default value if value is undefined', () => {
20
+ expect(get(value, 'a.d', 10)).toBe(10);
21
+ });
22
+
23
+ it('should get default value if the value is not an object', () => {
24
+ expect(get(1, 'a.b', 12)).toBe(12);
25
+ });
26
+
27
+ });