mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-05 02:12:26 +03:00
Merge branch 'v2.0.0' of https://github.com/KingSora/OverlayScrollbars into v2.0.0
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
style,
|
||||
appendChildren,
|
||||
clientSize,
|
||||
offset,
|
||||
absoluteCoordinates,
|
||||
offsetSize,
|
||||
scrollLeft,
|
||||
jsAPI,
|
||||
@@ -45,10 +45,10 @@ const rtlScrollBehavior = (parentElm: HTMLElement, childElm: HTMLElement): { i:
|
||||
style(parentElm, { overflowX: strHidden, overflowY: strHidden });
|
||||
scrollLeft(parentElm, 0);
|
||||
|
||||
const parentOffset = offset(parentElm);
|
||||
const childOffset = offset(childElm);
|
||||
const parentOffset = absoluteCoordinates(parentElm);
|
||||
const childOffset = absoluteCoordinates(childElm);
|
||||
scrollLeft(parentElm, -999); // https://github.com/KingSora/OverlayScrollbars/issues/187
|
||||
const childOffsetAfterScroll = offset(childElm);
|
||||
const childOffsetAfterScroll = absoluteCoordinates(childElm);
|
||||
return {
|
||||
/**
|
||||
* origin direction = determines if the zero scroll position is on the left or right side
|
||||
@@ -143,7 +143,7 @@ export class Environment {
|
||||
removeAttr(envElm, 'style');
|
||||
removeElements(envElm);
|
||||
|
||||
if (nativeScrollbarIsOverlaid.x && nativeScrollbarIsOverlaid.y) {
|
||||
if (!nativeScrollbarIsOverlaid.x || !nativeScrollbarIsOverlaid.y) {
|
||||
let size = windowSize();
|
||||
let dpr = windowDPR();
|
||||
const onChangedListener = this.#onChangedListener;
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
import { Environment } from 'environment/environment';
|
||||
|
||||
export * from 'environment/environment';
|
||||
export type OSEnvironment = Omit<Environment, 'addListener' | 'removeListener'>;
|
||||
|
||||
@@ -81,7 +81,7 @@ export const cssPropertyValue = (property: string, values: string, suffix?: stri
|
||||
* Get the requested JS function, object or constructor with vendor prefix if it isn't supported without or undefined if unsupported.
|
||||
* @param name The name of the JS function, object or constructor.
|
||||
*/
|
||||
export const jsAPI = (name: string): any => {
|
||||
export const jsAPI = <T = any>(name: string): T | undefined => {
|
||||
let result: any = jsCache[name] || window[name];
|
||||
|
||||
if (hasOwnProperty(jsCache, name)) {
|
||||
|
||||
@@ -2,8 +2,15 @@ import { each } from 'support/utils/array';
|
||||
import { contents } from 'support/dom/traversal';
|
||||
import { removeElements } from 'support/dom/manipulation';
|
||||
|
||||
/**
|
||||
* Creates a div DOM node.
|
||||
*/
|
||||
export const createDiv = (): HTMLDivElement => document.createElement('div');
|
||||
|
||||
/**
|
||||
* Creates DOM nodes modeled after the passed html string and returns the root dom nodes as a array.
|
||||
* @param html The html string after which the DOM nodes shall be created.
|
||||
*/
|
||||
export const createDOM = (html: string): ReadonlyArray<Node> => {
|
||||
const createdDiv = createDiv();
|
||||
createdDiv.innerHTML = html.trim();
|
||||
|
||||
@@ -5,11 +5,18 @@ const zeroObj: WH = {
|
||||
h: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the window inner- width and height.
|
||||
*/
|
||||
export const windowSize = (): WH => ({
|
||||
w: window.innerWidth,
|
||||
h: window.innerHeight,
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the offset- width and height of the passed element. If the element is null the width and height values are 0.
|
||||
* @param elm The element of which the offset- width and height shall be returned.
|
||||
*/
|
||||
export const offsetSize = (elm: HTMLElement | null): WH =>
|
||||
elm
|
||||
? {
|
||||
@@ -18,6 +25,10 @@ export const offsetSize = (elm: HTMLElement | null): WH =>
|
||||
}
|
||||
: zeroObj;
|
||||
|
||||
/**
|
||||
* Returns the client- width and height of the passed element. If the element is null the width and height values are 0.
|
||||
* @param elm The element of which the client- width and height shall be returned.
|
||||
*/
|
||||
export const clientSize = (elm: HTMLElement | null): WH =>
|
||||
elm
|
||||
? {
|
||||
@@ -26,4 +37,8 @@ export const clientSize = (elm: HTMLElement | null): WH =>
|
||||
}
|
||||
: zeroObj;
|
||||
|
||||
/**
|
||||
* Returns the BoundingClientRect of the passed element.
|
||||
* @param elm The element of which the BoundingClientRect shall be returned.
|
||||
*/
|
||||
export const getBoundingClientRect = (elm: HTMLElement): DOMRect => elm.getBoundingClientRect();
|
||||
|
||||
@@ -89,9 +89,9 @@ export const removeElements = (nodes: NodeCollection): void => {
|
||||
if (isArrayLike(nodes)) {
|
||||
each(from(nodes), (e) => removeElements(e));
|
||||
} else if (nodes) {
|
||||
const { parentNode } = nodes;
|
||||
if (parentNode) {
|
||||
parentNode.removeChild(nodes);
|
||||
const parentElm = parent(nodes);
|
||||
if (parentElm) {
|
||||
parentElm.removeChild(nodes);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,7 +6,11 @@ const zeroObj: XY = {
|
||||
y: 0,
|
||||
};
|
||||
|
||||
export const offset = (elm: HTMLElement | null): XY => {
|
||||
/**
|
||||
* Returns the offset- left and top coordinates of the passed element relative to the document. If the element is null the top and left values are 0.
|
||||
* @param elm The element of which the offset- top and left coordinates shall be returned.
|
||||
*/
|
||||
export const absoluteCoordinates = (elm: HTMLElement | null): XY => {
|
||||
const rect = elm ? getBoundingClientRect(elm) : 0;
|
||||
return rect
|
||||
? {
|
||||
@@ -16,7 +20,11 @@ export const offset = (elm: HTMLElement | null): XY => {
|
||||
: zeroObj;
|
||||
};
|
||||
|
||||
export const position = (elm: HTMLElement | null): XY =>
|
||||
/**
|
||||
* Returns the offset- left and top coordinates of the passed element. If the element is null the top and left values are 0.
|
||||
* @param elm The element of which the offset- top and left coordinates shall be returned.
|
||||
*/
|
||||
export const offsetCoordinates = (elm: HTMLElement | null): XY =>
|
||||
elm
|
||||
? {
|
||||
x: elm.offsetLeft,
|
||||
|
||||
@@ -31,6 +31,11 @@ const setCSSVal = (elm: HTMLElement | null, prop: string, val: string | number):
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets or sets the passed styles to the passed element.
|
||||
* @param elm The element to which the styles shall be applied to / be read from.
|
||||
* @param styles The styles which shall be set or read.
|
||||
*/
|
||||
export function style(elm: HTMLElement | null, styles: CssStyles): void;
|
||||
export function style(elm: HTMLElement | null, styles: string): string;
|
||||
export function style(elm: HTMLElement | null, styles: Array<string> | string): { [key: string]: string };
|
||||
@@ -54,10 +59,18 @@ export function style(elm: HTMLElement | null, styles: CssStyles | Array<string>
|
||||
each(keys(styles), (key) => setCSSVal(elm, key, styles[key]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the passed element (display: none).
|
||||
* @param elm The element which shall be hidden.
|
||||
*/
|
||||
export const hide = (elm: HTMLElement | null): void => {
|
||||
style(elm, { display: 'none' });
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the passed element (display: block).
|
||||
* @param elm The element which shall be shown.
|
||||
*/
|
||||
export const show = (elm: HTMLElement | null): void => {
|
||||
style(elm, { display: 'block' });
|
||||
};
|
||||
|
||||
@@ -2,6 +2,11 @@ import { each, from } from 'support/utils/array';
|
||||
|
||||
const elementIsVisible = (elm: HTMLElement): boolean => !!(elm.offsetWidth || elm.offsetHeight || elm.getClientRects().length);
|
||||
|
||||
/**
|
||||
* Find all elements with the passed selector, outgoing (and including) the passed element or the document if no element was provided.
|
||||
* @param selector The selector which has to be searched by.
|
||||
* @param elm The element from which the search shall be outgoing.
|
||||
*/
|
||||
export const find = (selector: string, elm?: Element | null): ReadonlyArray<Element> => {
|
||||
const arr: Array<Element> = [];
|
||||
|
||||
@@ -12,8 +17,18 @@ export const find = (selector: string, elm?: Element | null): ReadonlyArray<Elem
|
||||
return arr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the first element with the passed selector, outgoing (and including) the passed element or the document if no element was provided.
|
||||
* @param selector The selector which has to be searched by.
|
||||
* @param elm The element from which the search shall be outgoing.
|
||||
*/
|
||||
export const findFirst = (selector: string, elm?: Element | null): Element | null => (elm || document).querySelector(selector);
|
||||
|
||||
/**
|
||||
* Determines whether the passed element is matching with the passed selector.
|
||||
* @param elm The element which has to be compared with the passed selector.
|
||||
* @param selector The selector which has to be compared with the passed element. Additional selectors: ':visible' and ':hidden'.
|
||||
*/
|
||||
export const is = (elm: Element | null, selector: string): boolean => {
|
||||
if (elm) {
|
||||
if (selector === ':visible') {
|
||||
@@ -29,6 +44,11 @@ export const is = (elm: Element | null, selector: string): boolean => {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the children (no text-nodes or comments) of the passed element which are matching the passed selector. An empty array is returned if the passed element is null.
|
||||
* @param elm The element of which the children shall be returned.
|
||||
* @param selector The selector which must match with the children elements.
|
||||
*/
|
||||
export const children = (elm: Element | null, selector?: string): ReadonlyArray<Element> => {
|
||||
const childs: Array<Element> = [];
|
||||
|
||||
@@ -45,6 +65,14 @@ export const children = (elm: Element | null, selector?: string): ReadonlyArray<
|
||||
return childs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the childNodes (incl. text-nodes or comments etc.) of the passed element. An empty array is returned if the passed element is null.
|
||||
* @param elm The element of which the childNodes shall be returned.
|
||||
*/
|
||||
export const contents = (elm: Element | null): ReadonlyArray<ChildNode> => (elm ? from(elm.childNodes) : []);
|
||||
|
||||
/**
|
||||
* Returns the parent element of the passed element, or null if the passed element is null.
|
||||
* @param elm The element of which the parent element shall be returned.
|
||||
*/
|
||||
export const parent = (elm: Node | null): Node | null => (elm ? elm.parentElement : null);
|
||||
|
||||
@@ -51,6 +51,10 @@ export function each<T>(
|
||||
*/
|
||||
export const indexOf = <T = any>(arr: Array<T>, item: T, fromIndex?: number): number => arr.indexOf(item, fromIndex);
|
||||
|
||||
/**
|
||||
* Creates a shallow-copied Array instance from an array-like or iterable object.
|
||||
* @param arr The object from which the array instance shall be created.
|
||||
*/
|
||||
export const from = <T = any>(arr: ArrayLike<T>) => {
|
||||
if (Array.from) {
|
||||
return Array.from(arr);
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import { isArray, isFunction, isPlainObject, isNull } from 'support/utils/types';
|
||||
import { each } from 'support/utils/array';
|
||||
import { keys } from 'support/utils/object';
|
||||
|
||||
// https://github.com/jquery/jquery/blob/master/src/core.js#L116
|
||||
export function extend<T, U>(target: T, object1: U): T & U;
|
||||
export function extend<T, U, V>(target: T, object1: U, object2: V): T & U & V;
|
||||
export function extend<T, U, V, W>(target: T, object1: U, object2: V, object3: W): T & U & V & W;
|
||||
export function extend<T, U, V, W, X>(target: T, object1: U, object2: V, object3: W, object4: X): T & U & V & W & X;
|
||||
export function extend<T, U, V, W, X, Y>(target: T, object1: U, object2: V, object3: W, object4: X, object5: Y): T & U & V & W & X & Y;
|
||||
export function extend<T, U, V, W, X, Y, Z>(
|
||||
target: T,
|
||||
object1?: U,
|
||||
object2?: V,
|
||||
object3?: W,
|
||||
object4?: X,
|
||||
object5?: Y,
|
||||
object6?: Z
|
||||
): T & U & V & W & X & Y & Z {
|
||||
const sources: Array<any> = [object1, object2, object3, object4, object5, object6];
|
||||
|
||||
// Handle case when target is a string or something (possible in deep copy)
|
||||
if ((typeof target !== 'object' || isNull(target)) && !isFunction(target)) {
|
||||
target = {} as T;
|
||||
}
|
||||
|
||||
each(sources, (source) => {
|
||||
// Extend the base object
|
||||
each(keys(source), (key) => {
|
||||
const copy: any = source[key];
|
||||
|
||||
// Prevent Object.prototype pollution
|
||||
// Prevent never-ending loop
|
||||
if (target === copy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const copyIsArray = isArray(copy);
|
||||
|
||||
// Recurse if we're merging plain objects or arrays
|
||||
if (copy && (isPlainObject(copy) || copyIsArray)) {
|
||||
const src = target[key];
|
||||
let clone: any = src;
|
||||
|
||||
// Ensure proper type for the source value
|
||||
if (copyIsArray && !isArray(src)) {
|
||||
clone = [];
|
||||
} else if (!copyIsArray && !isPlainObject(src)) {
|
||||
clone = {};
|
||||
}
|
||||
|
||||
// Never move original objects, clone them
|
||||
target[key] = extend(clone, copy) as any;
|
||||
|
||||
// Don't bring in undefined values
|
||||
} else if (copy !== undefined) {
|
||||
target[key] = copy;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Return the modified object
|
||||
return target as any;
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from 'support/utils/array';
|
||||
export * from 'support/utils/object';
|
||||
export * from 'support/utils/extend';
|
||||
export * from 'support/utils/types';
|
||||
|
||||
@@ -1,13 +1,74 @@
|
||||
import { isArray, isFunction, isPlainObject, isNull } from 'support/utils/types';
|
||||
import { each } from 'support/utils/array';
|
||||
|
||||
/**
|
||||
* Determines whether the passed object has a property with the passed name.
|
||||
* @param obj The object.
|
||||
* @param prop The name of the property.
|
||||
*/
|
||||
export const hasOwnProperty = (obj: any, prop: string | number | symbol): boolean =>
|
||||
Object.prototype.hasOwnProperty.call(obj, prop);
|
||||
export const hasOwnProperty = (obj: any, prop: string | number | symbol): boolean => Object.prototype.hasOwnProperty.call(obj, prop);
|
||||
|
||||
/**
|
||||
* Returns the names of the enumerable string properties and methods of an object.
|
||||
* @param obj The object of which the properties shall be returned.
|
||||
*/
|
||||
export const keys = (obj: any): Array<string> => (obj ? Object.keys(obj) : []);
|
||||
|
||||
// https://github.com/jquery/jquery/blob/master/src/core.js#L116
|
||||
export function assignDeep<T, U>(target: T, object1: U): T & U;
|
||||
export function assignDeep<T, U, V>(target: T, object1: U, object2: V): T & U & V;
|
||||
export function assignDeep<T, U, V, W>(target: T, object1: U, object2: V, object3: W): T & U & V & W;
|
||||
export function assignDeep<T, U, V, W, X>(target: T, object1: U, object2: V, object3: W, object4: X): T & U & V & W & X;
|
||||
export function assignDeep<T, U, V, W, X, Y>(target: T, object1: U, object2: V, object3: W, object4: X, object5: Y): T & U & V & W & X & Y;
|
||||
export function assignDeep<T, U, V, W, X, Y, Z>(
|
||||
target: T,
|
||||
object1?: U,
|
||||
object2?: V,
|
||||
object3?: W,
|
||||
object4?: X,
|
||||
object5?: Y,
|
||||
object6?: Z
|
||||
): T & U & V & W & X & Y & Z {
|
||||
const sources: Array<any> = [object1, object2, object3, object4, object5, object6];
|
||||
|
||||
// Handle case when target is a string or something (possible in deep copy)
|
||||
if ((typeof target !== 'object' || isNull(target)) && !isFunction(target)) {
|
||||
target = {} as T;
|
||||
}
|
||||
|
||||
each(sources, (source) => {
|
||||
// Extend the base object
|
||||
each(keys(source), (key) => {
|
||||
const copy: any = source[key];
|
||||
|
||||
// Prevent Object.prototype pollution
|
||||
// Prevent never-ending loop
|
||||
if (target === copy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const copyIsArray = isArray(copy);
|
||||
|
||||
// Recurse if we're merging plain objects or arrays
|
||||
if (copy && (isPlainObject(copy) || copyIsArray)) {
|
||||
const src = target[key];
|
||||
let clone: any = src;
|
||||
|
||||
// Ensure proper type for the source value
|
||||
if (copyIsArray && !isArray(src)) {
|
||||
clone = [];
|
||||
} else if (!copyIsArray && !isPlainObject(src)) {
|
||||
clone = {};
|
||||
}
|
||||
|
||||
// Never move original objects, clone them
|
||||
target[key] = assignDeep(clone, copy) as any;
|
||||
} else {
|
||||
target[key] = copy;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Return the modified object
|
||||
return target as any;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { isNumber, isPlainObject } from 'support/utils/types';
|
||||
import { windowSize, offsetSize, clientSize, getBoundingClientRect } from 'support/dom/dimensions';
|
||||
|
||||
describe('dom dimensions', () => {
|
||||
describe('offsetSize', () => {
|
||||
test('DOM element', () => {
|
||||
const result = offsetSize(document.body);
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(isNumber(result.w)).toBe(true);
|
||||
expect(isNumber(result.h)).toBe(true);
|
||||
});
|
||||
|
||||
test('null', () => {
|
||||
const result = offsetSize(null);
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(isNumber(result.w)).toBe(true);
|
||||
expect(isNumber(result.h)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clientSize', () => {
|
||||
test('DOM element', () => {
|
||||
const result = clientSize(document.body);
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(isNumber(result.w)).toBe(true);
|
||||
expect(isNumber(result.h)).toBe(true);
|
||||
});
|
||||
|
||||
test('null', () => {
|
||||
const result = clientSize(null);
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(isNumber(result.w)).toBe(true);
|
||||
expect(isNumber(result.h)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('windowSize', () => {
|
||||
const result = windowSize();
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(isNumber(result.w)).toBe(true);
|
||||
expect(isNumber(result.h)).toBe(true);
|
||||
});
|
||||
|
||||
test('getBoundingClientRect', () => {
|
||||
expect(getBoundingClientRect(document.body)).toEqual(document.body.getBoundingClientRect());
|
||||
});
|
||||
});
|
||||
@@ -1,33 +1,33 @@
|
||||
import { isNumber, isPlainObject } from 'support/utils/types';
|
||||
import { offset, position } from 'support/dom/offset';
|
||||
import { absoluteCoordinates, offsetCoordinates } from 'support/dom/offset';
|
||||
|
||||
describe('dom offset', () => {
|
||||
describe('offset', () => {
|
||||
test('returns correct object with DOM element', () => {
|
||||
const result = offset(document.body);
|
||||
describe('absoluteCoordinates', () => {
|
||||
test('DOM element', () => {
|
||||
const result = absoluteCoordinates(document.body);
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(isNumber(result.x)).toBe(true);
|
||||
expect(isNumber(result.y)).toBe(true);
|
||||
});
|
||||
|
||||
test('returns correct object with null', () => {
|
||||
const result = offset(null);
|
||||
test('null', () => {
|
||||
const result = absoluteCoordinates(null);
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(result.x).toBe(0);
|
||||
expect(result.y).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('position', () => {
|
||||
test('returns correct object with DOM element', () => {
|
||||
const result = position(document.body);
|
||||
describe('offsetCoordinates', () => {
|
||||
test('DOM element', () => {
|
||||
const result = offsetCoordinates(document.body);
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(isNumber(result.x)).toBe(true);
|
||||
expect(isNumber(result.y)).toBe(true);
|
||||
});
|
||||
|
||||
test('returns correct object with null', () => {
|
||||
const result = position(null);
|
||||
test('null', () => {
|
||||
const result = offsetCoordinates(null);
|
||||
expect(isPlainObject(result)).toBe(true);
|
||||
expect(result.x).toBe(0);
|
||||
expect(result.y).toBe(0);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { validate, optionsTemplateTypes as oTypes, OptionsTemplate } from 'support/options';
|
||||
import { extend, isEmptyObject } from 'support/utils';
|
||||
import { assignDeep, isEmptyObject } from 'support/utils';
|
||||
|
||||
type TestOptionsObj = { propA: 'propA'; null: null };
|
||||
type TestOptionsEnum = 'A' | 'B' | 'C';
|
||||
@@ -55,7 +55,7 @@ describe('options validation', () => {
|
||||
foreignProp: 'foreign',
|
||||
foreignDeep: { a: 'A', b: 'B' },
|
||||
};
|
||||
const modifiedOptions = extend({}, options, { nested: foreignObj }, foreignObj);
|
||||
const modifiedOptions = assignDeep({}, options, { nested: foreignObj }, foreignObj);
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { validated } = result;
|
||||
|
||||
@@ -63,14 +63,14 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('passed objects arent mutated', () => {
|
||||
const clonedOptions = extend({}, options);
|
||||
const clonedOptions = assignDeep({}, options);
|
||||
validate(clonedOptions, template, clonedOptions);
|
||||
|
||||
expect(clonedOptions).toEqual(options);
|
||||
});
|
||||
|
||||
test('passed object isnt returned object', () => {
|
||||
const clonedOptions = extend({}, options);
|
||||
const clonedOptions = assignDeep({}, options);
|
||||
const result = validate(clonedOptions, template);
|
||||
|
||||
expect(result.validated).not.toBe(clonedOptions);
|
||||
@@ -86,7 +86,7 @@ describe('options validation', () => {
|
||||
|
||||
test('return signle non-object foreign property', () => {
|
||||
const foreignObj = { foreignProp: 'foreign' };
|
||||
const modifiedOptions = extend({}, options, foreignObj);
|
||||
const modifiedOptions = assignDeep({}, options, foreignObj);
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { foreign } = result;
|
||||
|
||||
@@ -98,7 +98,7 @@ describe('options validation', () => {
|
||||
foreignProp: 'foreign',
|
||||
foreignDeep: { a: 'A', b: 'B' },
|
||||
};
|
||||
const modifiedOptions = extend({}, options, foreignObj);
|
||||
const modifiedOptions = assignDeep({}, options, foreignObj);
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { foreign } = result;
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('options validation', () => {
|
||||
foreignProp: 'foreign',
|
||||
foreignDeep: { a: 'A', b: 'B' },
|
||||
};
|
||||
const modifiedOptions = extend({}, options, { nested: foreignObj }, foreignObj);
|
||||
const modifiedOptions = assignDeep({}, options, { nested: foreignObj }, foreignObj);
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { foreign } = result;
|
||||
|
||||
@@ -122,7 +122,7 @@ describe('options validation', () => {
|
||||
|
||||
describe('diff property return', () => {
|
||||
test('one value changed', () => {
|
||||
const modifiedOptions = extend({}, options, { str: 'newvaluetest' });
|
||||
const modifiedOptions = assignDeep({}, options, { str: 'newvaluetest' });
|
||||
const result = validate(modifiedOptions, template, options);
|
||||
const { validated } = result;
|
||||
|
||||
@@ -132,7 +132,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('multiple values changed', () => {
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
str: 'newvaluetest',
|
||||
nullbool: null,
|
||||
});
|
||||
@@ -147,7 +147,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('one nested value changed', () => {
|
||||
const modifiedOptions = extend({}, options, { nested: { num: -1293 } });
|
||||
const modifiedOptions = assignDeep({}, options, { nested: { num: -1293 } });
|
||||
const result = validate(modifiedOptions, template, options);
|
||||
const { validated } = result;
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('multiple nested values changed', () => {
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
nested: { num: -1293, abc: 'C' },
|
||||
});
|
||||
const result = validate(modifiedOptions, template, options);
|
||||
@@ -176,7 +176,7 @@ describe('options validation', () => {
|
||||
|
||||
test('various values changed', () => {
|
||||
const newFunc = () => {};
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
str: 'newstrvalue',
|
||||
func: newFunc,
|
||||
abc: 'C',
|
||||
@@ -206,7 +206,7 @@ describe('options validation', () => {
|
||||
foreignDeep: { a: 'A', b: 'B' },
|
||||
};
|
||||
const newFunc = () => {};
|
||||
const modifiedOptions = extend(
|
||||
const modifiedOptions = assignDeep(
|
||||
{},
|
||||
options,
|
||||
{
|
||||
@@ -239,7 +239,7 @@ describe('options validation', () => {
|
||||
|
||||
describe('value validity', () => {
|
||||
test('single value doesnt match template', () => {
|
||||
const modifiedOptions = extend({}, options, { str: 1 });
|
||||
const modifiedOptions = assignDeep({}, options, { str: 1 });
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { validated } = result;
|
||||
|
||||
@@ -247,7 +247,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('single enum value doesnt match template', () => {
|
||||
const modifiedOptions = extend({}, options, { abc: 'testval' });
|
||||
const modifiedOptions = assignDeep({}, options, { abc: 'testval' });
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { validated } = result;
|
||||
|
||||
@@ -255,7 +255,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('multiple values dont match template', () => {
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
str: 1,
|
||||
abc: 'testval',
|
||||
nullbool: 'string',
|
||||
@@ -269,7 +269,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('single nested value dont match template', () => {
|
||||
const modifiedOptions = extend({}, options, { nested: { num: 'hi' } });
|
||||
const modifiedOptions = assignDeep({}, options, { nested: { num: 'hi' } });
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { validated } = result;
|
||||
|
||||
@@ -277,7 +277,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('single nested enum value dont match template', () => {
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
nested: { abc: 'testabc' },
|
||||
});
|
||||
const result = validate(modifiedOptions, template);
|
||||
@@ -287,7 +287,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('multiple nested values dont match template', () => {
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
nested: { num: 'hi', abc: 'testabc' },
|
||||
});
|
||||
const result = validate(modifiedOptions, template);
|
||||
@@ -298,7 +298,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('all nested values dont match template', () => {
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
nested: { num: 'hi', abc: 'testabc', switch: 1 },
|
||||
});
|
||||
const result = validate(modifiedOptions, template);
|
||||
@@ -308,7 +308,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('all nested values dont match template with foreign property', () => {
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
nested: {
|
||||
foreign: 'foreign',
|
||||
num: 'hi',
|
||||
@@ -323,7 +323,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('various values dont match template', () => {
|
||||
const modifiedOptions = extend({}, options, {
|
||||
const modifiedOptions = assignDeep({}, options, {
|
||||
nested: { switch: null },
|
||||
obj: 1,
|
||||
abc: 'testest',
|
||||
@@ -343,7 +343,7 @@ describe('options validation', () => {
|
||||
foreignProp: 'foreign',
|
||||
foreignDeep: { a: 'A', b: 'B' },
|
||||
};
|
||||
const modifiedOptions = extend(
|
||||
const modifiedOptions = assignDeep(
|
||||
{},
|
||||
options,
|
||||
{
|
||||
@@ -369,7 +369,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('nested object is string', () => {
|
||||
const modifiedOptions = extend({}, options, { nested: 'string' });
|
||||
const modifiedOptions = assignDeep({}, options, { nested: 'string' });
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { validated } = result;
|
||||
|
||||
@@ -377,7 +377,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('nested object is null', () => {
|
||||
const modifiedOptions = extend({}, options, { nested: null });
|
||||
const modifiedOptions = assignDeep({}, options, { nested: null });
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { validated } = result;
|
||||
|
||||
@@ -385,7 +385,7 @@ describe('options validation', () => {
|
||||
});
|
||||
|
||||
test('nested object is undefined', () => {
|
||||
const modifiedOptions = extend({}, options);
|
||||
const modifiedOptions = assignDeep({}, options);
|
||||
modifiedOptions.nested = undefined;
|
||||
const result = validate(modifiedOptions, template);
|
||||
const { validated } = result;
|
||||
@@ -409,7 +409,7 @@ describe('options validation', () => {
|
||||
const { warn } = console;
|
||||
console.warn = jest.fn();
|
||||
|
||||
const modifiedOptions = extend({}, options, { str: 1 });
|
||||
const modifiedOptions = assignDeep({}, options, { str: 1 });
|
||||
validate(modifiedOptions, template, {}, false);
|
||||
expect(console.warn).not.toBeCalled();
|
||||
|
||||
@@ -421,15 +421,15 @@ describe('options validation', () => {
|
||||
console.warn = jest.fn();
|
||||
|
||||
// str must be string
|
||||
validate(extend({}, options, { str: 1 }), template, {}, true);
|
||||
validate(assignDeep({}, options, { str: 1 }), template, {}, true);
|
||||
expect(console.warn).toBeCalledTimes(1);
|
||||
|
||||
// abc must be A | B | C
|
||||
validate(extend({}, options, { abc: 'some string' }), template, {}, true);
|
||||
validate(assignDeep({}, options, { abc: 'some string' }), template, {}, true);
|
||||
expect(console.warn).toBeCalledTimes(2);
|
||||
|
||||
// everthing OK
|
||||
validate(extend({}, options, { abc: 'C' }), template, {}, true);
|
||||
validate(assignDeep({}, options, { abc: 'C' }), template, {}, true);
|
||||
expect(console.warn).toBeCalledTimes(2);
|
||||
|
||||
console.warn = warn;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { each, indexOf } from 'support/utils/array';
|
||||
import { each, from, indexOf } from 'support/utils/array';
|
||||
|
||||
describe('array utilities', () => {
|
||||
describe('each', () => {
|
||||
@@ -172,6 +172,26 @@ describe('array utilities', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('from', () => {
|
||||
test('Array.from', () => {
|
||||
document.body.innerHTML = '<div></div><div></div><div></div>';
|
||||
const fromChildNodes = from(document.body.childNodes);
|
||||
expect(fromChildNodes).toEqual(Array.from(document.body.childNodes));
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
test('fallback', () => {
|
||||
document.body.innerHTML = '<div></div><div></div><div></div>';
|
||||
const arrFrom = Array.from;
|
||||
// @ts-ignore
|
||||
Array.from = undefined;
|
||||
const fromChildNodes = from(document.body.childNodes);
|
||||
Array.from = arrFrom;
|
||||
expect(fromChildNodes).toEqual(Array.from(document.body.childNodes));
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
});
|
||||
|
||||
test('indexOf', () => {
|
||||
const idx = indexOf([1, 2, 3], 2);
|
||||
expect(idx).toBe(1);
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
import { extend } from 'support/utils/extend';
|
||||
import { isPlainObject } from 'support/utils/types';
|
||||
|
||||
// type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T
|
||||
type Deep = {
|
||||
foo?: {
|
||||
bar?: boolean;
|
||||
baz?: boolean;
|
||||
};
|
||||
foo2?: Document;
|
||||
};
|
||||
type Settings = {
|
||||
xnumber0?: null;
|
||||
xnumber1?: number | null;
|
||||
xnumber2?: number | null;
|
||||
xstring1?: string;
|
||||
xstring2?: string;
|
||||
xxx?: string;
|
||||
};
|
||||
type NestedArray = {
|
||||
arr: Array<any> | object;
|
||||
};
|
||||
|
||||
// https://github.com/jquery/jquery/blob/master/test/unit/core.js#L965
|
||||
describe('extend', () => {
|
||||
test('equals object assign', () => {
|
||||
let settings: Settings = { xnumber1: 5, xnumber2: 7, xstring1: 'peter', xstring2: 'pan' };
|
||||
const options: Settings = { xnumber2: 1, xstring2: 'x', xxx: 'newstring' };
|
||||
const optionsCopy: Settings = { xnumber2: 1, xstring2: 'x', xxx: 'newstring' };
|
||||
const merged: Settings = { xnumber1: 5, xnumber2: 1, xstring1: 'peter', xstring2: 'x', xxx: 'newstring' };
|
||||
|
||||
extend(settings, options);
|
||||
expect(settings).toEqual(merged);
|
||||
expect(options).toEqual(optionsCopy);
|
||||
|
||||
extend(settings, null, options);
|
||||
expect(settings).toEqual(merged);
|
||||
expect(options).toEqual(optionsCopy);
|
||||
|
||||
const deep1: Deep = { foo: { bar: true } };
|
||||
const deep2: Deep = { foo: { baz: true }, foo2: document };
|
||||
const deep2copy: Deep = { foo: { baz: true }, foo2: document };
|
||||
const deepmerged: Deep = { foo: { bar: true, baz: true }, foo2: document };
|
||||
|
||||
extend(deep1, deep2);
|
||||
expect(deep1.foo).toEqual(deepmerged.foo);
|
||||
expect(deep2.foo).toEqual(deep2copy.foo);
|
||||
expect(deep1.foo2).toBe(document);
|
||||
|
||||
const arr = [1, 2, 3];
|
||||
const nestedArray: NestedArray = { arr };
|
||||
|
||||
expect(extend({}, nestedArray).arr).not.toBe(arr);
|
||||
expect(Array.isArray(extend({ arr: {} }, nestedArray).arr)).toBeTruthy();
|
||||
expect(Array.isArray(extend({ arr: {} }, nestedArray).arr)).toBeTruthy();
|
||||
expect(isPlainObject(extend({ arr }, { arr: {} }).arr)).toBeTruthy();
|
||||
|
||||
let empty: { foo?: any } = {};
|
||||
const optionsWithLength = { foo: { length: -1 } };
|
||||
|
||||
extend(empty, optionsWithLength);
|
||||
expect(empty.foo).toEqual(optionsWithLength.foo);
|
||||
|
||||
empty = {};
|
||||
const optionsWithDate = { foo: { date: new Date() } };
|
||||
|
||||
extend(empty, optionsWithDate);
|
||||
expect(empty.foo).toEqual(optionsWithDate.foo);
|
||||
|
||||
/** @constructor */
|
||||
const MyKlass = function () {};
|
||||
// @ts-ignore
|
||||
const customObject = new MyKlass();
|
||||
const optionsWithCustomObject = { foo: { date: customObject } };
|
||||
empty = {};
|
||||
|
||||
extend(empty, optionsWithCustomObject);
|
||||
expect(empty.foo && empty.foo.date === customObject).toBeTruthy();
|
||||
|
||||
// Makes the class a little more realistic
|
||||
MyKlass.prototype = { someMethod() {} };
|
||||
empty = {};
|
||||
|
||||
extend(empty, optionsWithCustomObject);
|
||||
expect(empty.foo && empty.foo.date === customObject).toBeTruthy();
|
||||
|
||||
const MyNumber = Number;
|
||||
|
||||
let ret: any = extend({ foo: 4 }, { foo: new MyNumber(5) });
|
||||
expect(parseInt(ret.foo?.toString() as string, 10) === 5).toBeTruthy();
|
||||
|
||||
let nullUndef = extend({}, options, { xnumber2: null });
|
||||
expect(nullUndef.xnumber2).toBe(null);
|
||||
|
||||
// @ts-ignore
|
||||
nullUndef = extend({}, options, { xnumber2: undefined });
|
||||
expect(nullUndef.xnumber2).toBe(options.xnumber2);
|
||||
|
||||
// @ts-ignore
|
||||
nullUndef = extend({}, options, { xnumber0: null });
|
||||
expect(nullUndef.xnumber0).toBe(null);
|
||||
|
||||
const target = {};
|
||||
const recursive = { foo: target, bar: 5 };
|
||||
|
||||
extend(target, recursive);
|
||||
expect(target).toEqual({ bar: 5 });
|
||||
|
||||
ret = extend({ foo: [] }, { foo: [0] });
|
||||
expect(ret.foo?.length).toBe(1);
|
||||
|
||||
ret = extend({ foo: '1,2,3' }, { foo: [1, 2, 3] });
|
||||
expect(typeof ret.foo !== 'string').toBeTruthy();
|
||||
|
||||
ret = extend({ foo: 'bar' }, { foo: null });
|
||||
expect(typeof ret.foo !== 'undefined').toBeTruthy();
|
||||
|
||||
const obj = { foo: null };
|
||||
extend(obj, { foo: 'notnull' });
|
||||
expect(obj.foo).toBe('notnull');
|
||||
|
||||
const func: { (): void; key?: string } = () => {};
|
||||
extend(func, { key: 'value' });
|
||||
expect(func.key).toBe('value');
|
||||
|
||||
const defaults = { xnumber1: 5, xnumber2: 7, xstring1: 'peter', xstring2: 'pan' };
|
||||
const defaultsCopy = { xnumber1: 5, xnumber2: 7, xstring1: 'peter', xstring2: 'pan' };
|
||||
const options1 = { xnumber2: 1, xstring2: 'x' };
|
||||
const options1Copy = { xnumber2: 1, xstring2: 'x' };
|
||||
const options2 = { xstring2: 'xx', xxx: 'newstringx' };
|
||||
const options2Copy = { xstring2: 'xx', xxx: 'newstringx' };
|
||||
const merged2 = { xnumber1: 5, xnumber2: 1, xstring1: 'peter', xstring2: 'xx', xxx: 'newstringx' };
|
||||
|
||||
settings = extend({}, defaults, options1, options2);
|
||||
expect(settings).toEqual(merged2);
|
||||
expect(defaults).toEqual(defaultsCopy);
|
||||
expect(options1).toEqual(options1Copy);
|
||||
expect(options2).toEqual(options2Copy);
|
||||
|
||||
expect(extend('', { foo: 1 })).toEqual({ foo: 1 });
|
||||
expect(extend(null, { foo: null, deep: { foo: null } })).toEqual({ foo: null, deep: { foo: null } });
|
||||
expect(extend(12, { foo: 1, deep: { foo: null, text: '' } })).toEqual({ foo: 1, deep: { foo: null, text: '' } });
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,153 @@
|
||||
import { keys, hasOwnProperty } from 'support/utils/object';
|
||||
import { assignDeep, keys, hasOwnProperty } from 'support/utils/object';
|
||||
import { isPlainObject } from 'support/utils/types';
|
||||
|
||||
describe('object utilities', () => {
|
||||
// https://github.com/jquery/jquery/blob/master/test/unit/core.js#L965
|
||||
describe('assignDeep', () => {
|
||||
// type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T
|
||||
type Deep = {
|
||||
foo?: {
|
||||
bar?: boolean;
|
||||
baz?: boolean;
|
||||
};
|
||||
foo2?: Document;
|
||||
};
|
||||
type Settings = {
|
||||
xnumber0?: null;
|
||||
xnumber1?: number | null;
|
||||
xnumber2?: number | null;
|
||||
xstring1?: string;
|
||||
xstring2?: string;
|
||||
xxx?: string;
|
||||
};
|
||||
type NestedArray = {
|
||||
arr: Array<any> | object;
|
||||
};
|
||||
|
||||
test('equals object assign', () => {
|
||||
let settings: Settings = { xnumber1: 5, xnumber2: 7, xstring1: 'peter', xstring2: 'pan' };
|
||||
const options: Settings = { xnumber2: 1, xstring2: 'x', xxx: 'newstring' };
|
||||
const optionsCopy: Settings = { xnumber2: 1, xstring2: 'x', xxx: 'newstring' };
|
||||
const merged: Settings = { xnumber1: 5, xnumber2: 1, xstring1: 'peter', xstring2: 'x', xxx: 'newstring' };
|
||||
|
||||
assignDeep(settings, options);
|
||||
expect(settings).toEqual(merged);
|
||||
expect(options).toEqual(optionsCopy);
|
||||
|
||||
assignDeep(settings, null, options);
|
||||
expect(settings).toEqual(merged);
|
||||
expect(options).toEqual(optionsCopy);
|
||||
|
||||
const deep1: Deep = { foo: { bar: true } };
|
||||
const deep2: Deep = { foo: { baz: true }, foo2: document };
|
||||
const deep2copy: Deep = { foo: { baz: true }, foo2: document };
|
||||
const deepmerged: Deep = { foo: { bar: true, baz: true }, foo2: document };
|
||||
|
||||
assignDeep(deep1, deep2);
|
||||
expect(deep1.foo).toEqual(deepmerged.foo);
|
||||
expect(deep2.foo).toEqual(deep2copy.foo);
|
||||
expect(deep1.foo2).toBe(document);
|
||||
|
||||
const arr = [1, 2, 3];
|
||||
const nestedArray: NestedArray = { arr };
|
||||
|
||||
expect(assignDeep({}, nestedArray).arr).not.toBe(arr);
|
||||
expect(Array.isArray(assignDeep({ arr: {} }, nestedArray).arr)).toBeTruthy();
|
||||
expect(Array.isArray(assignDeep({ arr: {} }, nestedArray).arr)).toBeTruthy();
|
||||
expect(isPlainObject(assignDeep({ arr }, { arr: {} }).arr)).toBeTruthy();
|
||||
|
||||
let empty: { foo?: any } = {};
|
||||
const optionsWithLength = { foo: { length: -1 } };
|
||||
|
||||
assignDeep(empty, optionsWithLength);
|
||||
expect(empty.foo).toEqual(optionsWithLength.foo);
|
||||
|
||||
empty = {};
|
||||
const optionsWithDate = { foo: { date: new Date() } };
|
||||
|
||||
assignDeep(empty, optionsWithDate);
|
||||
expect(empty.foo).toEqual(optionsWithDate.foo);
|
||||
|
||||
/** @constructor */
|
||||
const MyKlass = function () {};
|
||||
// @ts-ignore
|
||||
const customObject = new MyKlass();
|
||||
const optionsWithCustomObject = { foo: { date: customObject } };
|
||||
empty = {};
|
||||
|
||||
assignDeep(empty, optionsWithCustomObject);
|
||||
expect(empty.foo && empty.foo.date === customObject).toBeTruthy();
|
||||
|
||||
// Makes the class a little more realistic
|
||||
MyKlass.prototype = { someMethod() {} };
|
||||
empty = {};
|
||||
|
||||
assignDeep(empty, optionsWithCustomObject);
|
||||
expect(empty.foo && empty.foo.date === customObject).toBeTruthy();
|
||||
|
||||
const MyNumber = Number;
|
||||
|
||||
let ret: any = assignDeep({ foo: 4 }, { foo: new MyNumber(5) });
|
||||
expect(parseInt(ret.foo?.toString() as string, 10) === 5).toBeTruthy();
|
||||
|
||||
let nullUndef = assignDeep({}, options, { xnumber2: null });
|
||||
expect(nullUndef.xnumber2).toBe(null);
|
||||
|
||||
// @ts-ignore
|
||||
nullUndef = assignDeep({}, options, {});
|
||||
expect(nullUndef.xnumber2).toBe(options.xnumber2);
|
||||
|
||||
// @ts-ignore
|
||||
nullUndef = assignDeep({}, options, { xnumber2: undefined });
|
||||
expect(nullUndef.xnumber2).toBe(undefined);
|
||||
|
||||
// @ts-ignore
|
||||
nullUndef = assignDeep({}, options, { xnumber0: null });
|
||||
expect(nullUndef.xnumber0).toBe(null);
|
||||
|
||||
const target = {};
|
||||
const recursive = { foo: target, bar: 5 };
|
||||
|
||||
assignDeep(target, recursive);
|
||||
expect(target).toEqual({ bar: 5 });
|
||||
|
||||
ret = assignDeep({ foo: [] }, { foo: [0] });
|
||||
expect(ret.foo?.length).toBe(1);
|
||||
|
||||
ret = assignDeep({ foo: '1,2,3' }, { foo: [1, 2, 3] });
|
||||
expect(typeof ret.foo !== 'string').toBeTruthy();
|
||||
|
||||
ret = assignDeep({ foo: 'bar' }, { foo: null });
|
||||
expect(typeof ret.foo !== 'undefined').toBeTruthy();
|
||||
|
||||
const obj = { foo: null };
|
||||
assignDeep(obj, { foo: 'notnull' });
|
||||
expect(obj.foo).toBe('notnull');
|
||||
|
||||
const func: { (): void; key?: string } = () => {};
|
||||
assignDeep(func, { key: 'value' });
|
||||
expect(func.key).toBe('value');
|
||||
|
||||
const defaults = { xnumber1: 5, xnumber2: 7, xstring1: 'peter', xstring2: 'pan' };
|
||||
const defaultsCopy = { xnumber1: 5, xnumber2: 7, xstring1: 'peter', xstring2: 'pan' };
|
||||
const options1 = { xnumber2: 1, xstring2: 'x' };
|
||||
const options1Copy = { xnumber2: 1, xstring2: 'x' };
|
||||
const options2 = { xstring2: 'xx', xxx: 'newstringx' };
|
||||
const options2Copy = { xstring2: 'xx', xxx: 'newstringx' };
|
||||
const merged2 = { xnumber1: 5, xnumber2: 1, xstring1: 'peter', xstring2: 'xx', xxx: 'newstringx' };
|
||||
|
||||
settings = assignDeep({}, defaults, options1, options2);
|
||||
expect(settings).toEqual(merged2);
|
||||
expect(defaults).toEqual(defaultsCopy);
|
||||
expect(options1).toEqual(options1Copy);
|
||||
expect(options2).toEqual(options2Copy);
|
||||
|
||||
expect(assignDeep('', { foo: 1 })).toEqual({ foo: 1 });
|
||||
expect(assignDeep(null, { foo: null, deep: { foo: null } })).toEqual({ foo: null, deep: { foo: null } });
|
||||
expect(assignDeep(12, { foo: 1, deep: { foo: null, text: '' } })).toEqual({ foo: 1, deep: { foo: null, text: '' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('keys', () => {
|
||||
test('correct amount of keys', () => {
|
||||
const obj = {
|
||||
|
||||
Reference in New Issue
Block a user