Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

core-functions

Package Overview
Dependencies
Maintainers
1
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

core-functions - npm Package Compare versions

Comparing version 2.0.14 to 3.0.0

copy.sh

3

booleans.js

@@ -35,6 +35,5 @@ 'use strict';

function isTrueOrFalse(value) {
//TODO redundant (effectively an alias for isBoolean, since end results are same), but maybe do a micro-benchmark on the two to decide which to keep
//return value === true || value === false || (value instanceof Boolean && isTrueOrFalse(value.valueOf()));
//Redundant - effectively an alias for isBoolean, since end results are same, but maybe do a micro-benchmark on the two to decide which to keep
return value === true || value === false || value instanceof Boolean;
}
'use strict';
/** The smallest positive normal value of type number, 2-1022. It is equal to the hexadecimal floating-point literal 0x1.0p-1022. */
// const MIN_NORMAL = 2.2250738585072014E-308;
// const MAX_FIXED_DECIMAL_PLACES = 20;
const integerRegex = /^[+-]?\d+(\.0*)?$/;
const numberRegex = /^[+-]?(\d+\.?|\d*\.\d+)([eE][+-]?\d+)?$/;
const zeroRegex = /^[+-]?0+(\.0*)?([eE][+-]?\d+)?$/;
const leadingZeroesRegex = /^([+-]?)0+([1-9].*|0(\..*)?$)/;
const trailingZeroesRegex = /^([^.]*\.(?:\d+?))0+(([eE][+-]?\d+)?)$/;
/**

@@ -16,3 +28,29 @@ * Module containing utilities for working with numbers.

/** Returns true if the number is NaN or if it is an instance of Number and its value is NaN; false otherwise. */
isNaN: isNaN
isNaN: isNaN,
isInteger: isInteger,
isSafeInteger: isSafeInteger,
integerRegex: integerRegex,
numberRegex: numberRegex,
isNumberLike: isNumberLike,
isIntegerLike: isIntegerLike,
isZeroLike: isZeroLike,
toNumberLike: toNumberLike,
toDecimalLike: toDecimalLike,
toDecimalLikeOrNaN: toDecimalLikeOrNaN,
toIntegerLike: toIntegerLike,
toIntegerLikeOrNaN: toIntegerLikeOrNaN,
toNumberOrIntegerLike: toNumberOrIntegerLike,
removeLeadingZeroes: removeLeadingZeroes,
removeTrailingZeroes: removeTrailingZeroes,
zeroPadLeft: zeroPadLeft,
removeSignIfZero: removeSignIfZeroLike,
nearlyEqual: nearlyEqual
};

@@ -50,4 +88,4 @@

function isSpecialNumber(value) {
return value === +Infinity || value === -Infinity || (typeof value === 'number' && Number.isNaN(value)) ||
(value instanceof Number && isSpecialNumber(value.valueOf()));
if (value instanceof Number) value = value.valueOf();
return value === +Infinity || value === -Infinity || (typeof value === 'number' && Number.isNaN(value));
}

@@ -62,4 +100,303 @@

function isNaN(value) {
return (typeof value === 'number' && Number.isNaN(value)) || (value instanceof Number && isNaN(value.valueOf()));
return (typeof value === 'number' && Number.isNaN(value)) ||
(value instanceof Number && Number.isNaN(value.valueOf()));
}
/**
* Returns true if the given value is an integer.
* @see Number.isInteger
* @param {number|Number} value - the value to test
* @returns {boolean} true if the given value is an integer; false otherwise
*/
function isInteger(value) {
return (typeof value === 'number' && Number.isInteger(value)) ||
(value instanceof Number && Number.isInteger(value.valueOf()));
}
/**
* Returns true if the given value is a safe integer.
* @see Number.isSafeInteger
* @param {number|Number} value - the value to test
* @returns {boolean} true if the given value is a safe integer; false otherwise
*/
function isSafeInteger(value) {
return (typeof value === 'number' && Number.isSafeInteger(value)) ||
(value instanceof Number && Number.isSafeInteger(value.valueOf()));
}
/**
* Returns true if the given value is a number-like string containing a number of any precision.
* @param {string} value - the value to test
* @returns {boolean} true if the given value is a number string; false otherwise
*/
function isNumberLike(value) {
return typeof value === 'string' && numberRegex.test(value);
}
/**
* Returns true if the given value is a integer-like string containing an integer of any precision.
* @param {string} value - the value to test
* @returns {boolean} true if the given value is an integer string; false otherwise
*/
function isIntegerLike(value) {
return typeof value === 'string' && (integerRegex.test(value) ||
(numberRegex.test(value) && integerRegex.test(toDecimalLike(value))));
}
/**
* Returns true if the given value is a zero number-like string; false otherwise.
* @param {string} value - the value to test
* @returns {boolean} true if the given value is a zero number-like string; false otherwise
*/
function isZeroLike(value) {
return typeof value === 'string' && zeroRegex.test(value);
}
/**
* Converts the given number into a number-like string.
* @param {number|Number} number - the number to convert
* @returns {string} a number-like string
*/
function toNumberLike(number) {
// Unbox if Number object
if (number instanceof Number) {
number = number.valueOf()
}
return Number.isInteger(number) ? number.toFixed(0) : number.toPrecision(); //removeTrailingZeroes(number.toFixed(MAX_FIXED_DECIMAL_PLACES));
}
/**
* Converts the given number-like into a decimal-like string by replacing any exponent part with its decimal equivalent.
* Precondition: number or isNumberLike(numberLike)
* @param {string|number|Number} numberLike - a number-like string or number
* @returns {string|undefined} a decimal-like string (if number-like); otherwise undefined
*/
function toDecimalLike(numberLike) {
if (typeof numberLike === 'number' || numberLike instanceof Number) {
numberLike = toNumberLike(numberLike); // Convert any number into a number-like
} else if (numberLike instanceof String) {
numberLike = numberLike.valueOf(); // unbox String object
}
// Ensure that the given numberLike is actually number-like
if (!isNumberLike(numberLike)) return undefined;
// Convert number-like string into decimal-like string
const signed = numberLike.length > 0 && (numberLike[0] === '-' || numberLike[0] === '+');
const sign = signed ? numberLike.substring(0, 1) : '';
let n = signed ? numberLike.substring(1) : numberLike;
const decPos = n.indexOf('.');
const hasDecPoint = decPos !== -1;
const ePos = n.indexOf('e');
const hasExponent = ePos !== -1;
const intPart = removeLeadingZeroes(hasDecPoint || hasExponent ? n.substring(0, hasDecPoint ? decPos : ePos) : n);
const fractionalPart = hasDecPoint ? n.substring(decPos + 1, hasExponent ? ePos : n.length) : '';
const decPlaces = fractionalPart.length;
if (hasExponent) {
// Convert exponent into decimal format
let e = Number.parseInt(n.substring(ePos + 1));
if (e >= 0) {
if (hasDecPoint) {
if (e < decPlaces) {
n = `${intPart}${fractionalPart.substring(0, e)}.${fractionalPart.substring(e)}`;
} else if (e === decPlaces) {
n = `${intPart}${fractionalPart}`;
} else { // e > decPlaces
n = `${intPart}${fractionalPart}${'0'.repeat(e - decPlaces)}`;
}
n = removeLeadingZeroes(n);
} else {
// has no decimal point
n = `${e > 0 ? removeLeadingZeroes(`${intPart}${'0'.repeat(e)}`) : intPart}`;
}
} else {
// e < 0
const intLen = intPart.length;
e = -e;
if (e < intLen) {
n = `${intPart.substring(0, e)}.${intPart.substring(e)}${fractionalPart}`;
} else if (e === intLen) {
n = `0.${intPart}${fractionalPart}`;
} else { // e > intLen
n = `0.${'0'.repeat(e - intLen)}${intPart}${fractionalPart}`;
}
}
} else {
// has no exponent
n = `${intPart}${decPlaces > 0 ? `.${fractionalPart}` : ''}`;
}
return !isZeroLike(n) ? `${sign}${n}` : n;
}
/**
* A convenience version of {@linkcode toDecimalLike} function that returns NaN instead of undefined.
* Precondition: number or isNumberLike(numberLike)
* @param {number|string|Number|String} numberLike - the number or number-like string to convert
* @returns {string|NaN} a decimal-like string (if number-like); otherwise NaN
*/
function toDecimalLikeOrNaN(numberLike) {
const decimalLike = toDecimalLike(numberLike);
return decimalLike ? decimalLike : NaN;
}
/**
* Converts the given number or number-like string into an integer-like string by first converting it into a decimal-
* like string and then removing any fractional part.
* Precondition: number or isNumberLike(numberLike) (or IDEALLY isIntegerLike(numberLike))
* @param {number|string|Number|String} numberLike - the number or number-like string to convert
* @returns {string|undefined} an integer-like string (if number-like); otherwise undefined
*/
function toIntegerLike(numberLike) {
// First convert number or number-like string into a decimal-like string
const decimalLike = toDecimalLike(numberLike);
if (!decimalLike) return undefined;
// and then remove any fractional part by truncating the number at the decimal point
const decPos = decimalLike.indexOf('.');
return decPos !== -1 ? removeSignIfZeroLike(decimalLike.substring(0, decPos)) : decimalLike;
}
/**
* A convenience version of {@linkcode toIntegerLike} function that returns NaN instead of undefined.
* Precondition: number or isNumberLike(numberLike) (or IDEALLY isIntegerLike(numberLike))
* @param {number|string|Number|String} numberLike - the number or number-like string to convert
* @returns {string|NaN} an integer-like string (if number-like); otherwise NaN
*/
function toIntegerLikeOrNaN(numberLike) {
const integerLike = toIntegerLike(numberLike);
return integerLike ? integerLike : NaN;
}
/**
* Converts the given value into a number (if its a number or a Number or a safe integer-like string or a number-like
* string); or returns the given value (if its a big integer-like string that cannot be safely converted to an integer
* without losing precision); otherwise returns NaN.
* @param {string|String|number|Number} value - the value to convert
* @returns {number|string|NaN} a number or big integer-like string or NaN
*/
function toNumberOrIntegerLike(value) {
// Unbox the given value if it is a Number or String object
if (value instanceof Number || value instanceof String) {
value = value.valueOf();
}
const type = typeof value;
if (type === 'number') {
return value;
} else if (type === 'string') {
// Check if the string contains an integer
if (isIntegerLike(value)) {
// Integer-like string, so attempt to parse to an integer
const n = Number.parseInt(value);
// Check if have enough precision to hold the given integer value ... otherwise return the given value excluding
// its fractional part (if any)
return Number.isSafeInteger(n) ? n : Number.isNaN(n) ? NaN : toIntegerLikeOrNaN(value);
}
// Check if the string contains a number
else if (isNumberLike(value)) {
// Number-like string, so attempt to parse to a number
return Number.parseFloat(value);
}
}
return NaN;
}
/**
* Returns the given number string without superfluous leading zeroes.
* @param {string} numberString - the number string
* @returns {string} the number string without superfluous leading zeroes
*/
function removeLeadingZeroes(numberString) {
return typeof numberString === 'string' ? numberString.replace(leadingZeroesRegex, '$1$2') :
!numberString ? numberString : undefined;
}
/**
* Returns the given number string without superfluous trailing zeroes.
* @param {string} numberString - the number string
* @returns {string} the number string without superfluous trailing zeroes
*/
function removeTrailingZeroes(numberString) {
return typeof numberString === 'string' ? numberString.replace(trailingZeroesRegex, '$1$2') :
!numberString ? numberString : undefined;
}
/**
* Pads the given number string with leading zeroes to create a number string with a number of digits equal to the given
* number of digits (excluding any +/- sign). NB: If the given number string is signed then it must have a leading plus
* or minus sign.
* @param {string} numberString - the number string to pad
* @param {number|undefined} [digits] - the optional number of digits up to which to pad the given number with leading
* zeroes if necessary (if digits > 0); otherwise (if digits <= 0 or undefined) then returns the given number (without
* any zero-padding)
* @returns {string} the zero-padded number string
* @throws an Error if the given number has more digits than the given number of digits
*/
function zeroPadLeft(numberString, digits) {
if (digits && digits > 0) {
const signed = numberString.length > 0 && (numberString[0] === '-' || numberString[0] === '+');
const unsignedNumber = signed ? numberString.substring(1) : numberString;
const len = unsignedNumber.length;
if (len < digits) {
return (signed ? numberString[0] : '') + '0'.repeat(digits - len) + unsignedNumber;
} else if (len > digits) {
throw new Error(`${numberString} has more than ${digits} digits`);
}
}
return numberString;
}
/**
* Returns the given number-like string either with its sign (if non-zero) or without its sign (if zero).
* @param {string} numberLike - a number-like string
* @returns {*} the given number-like string either with its sign (if non-zero) or without its sign (if zero)
*/
function removeSignIfZeroLike(numberLike) {
if (isZeroLike(numberLike)) {
const signed = numberLike.length > 0 && (numberLike[0] === '-' || numberLike[0] === '+');
return signed ? numberLike.substring(1) : numberLike;
}
return numberLike;
}
/**
* Compares two given floating-point numbers for approximate equality.
* Algorithm below was inspired by http://floating-point-gui.de/errors/comparison/
* with an extra case personally added for comparing safe integers
* @param {number} a - the first number to compare
* @param {number} b - the second number to compare
* @returns {boolean} true if x is approximately equal to y
*/
function nearlyEqual(a, b) {
if (a === b) { // shortcut that also handles case if both Infinity, MAX_VALUE or MIN_VALUE
return true;
}
// Use an absolute epsilon check for safe integer comparisons
if (Number.isSafeInteger(a) && Number.isSafeInteger(b)) {
return Math.abs(a - b) < Number.EPSILON;
}
// Use a relative epsilon check for floating-point comparisons
//let m = Math.abs(a) + Math.abs(b);
//let m = Math.max(Math.abs(a), Math.abs(b));
let m = Math.abs(a)/2 + Math.abs(b)/2; // average magnitude, seems to work better than sum
// Constrain m to be within the range MIN_VALUE to MAX_VALUE to avoid divide/times by zero & +/- infinities
m = Math.min(Math.max(m, Number.MIN_VALUE), Number.MAX_VALUE);
//return Math.abs((a - b) / m) < Number.EPSILON;
return Math.abs(a - b) < Number.EPSILON * m;
}
// // Algorithm translated from http://floating-point-gui.de/errors/comparison/
// function nearlyEqual_0(a, b) {
// const absA = Math.abs(a);
// const absB = Math.abs(b);
// const diff = Math.abs(a - b);
//
// if (a === b) { // shortcut, handles infinities
// return true;
// } else if (a === 0 || b === 0 || diff < MIN_NORMAL) {
// // a or b is zero or both are extremely close to it
// // relative error is less meaningful here
// return diff < Number.EPSILON * MIN_NORMAL;
// } else { // use relative error
// return diff / Math.min(absA + absB, Number.MAX_VALUE) < Number.EPSILON;
// }
// }

@@ -18,3 +18,4 @@ 'use strict';

getPropertyValue: getPropertyValue,
copyNamedProperties: copyNamedProperties
copyNamedProperties: copyNamedProperties,
toKeyValuePairs: toKeyValuePairs
};

@@ -33,11 +34,25 @@

/**
* Merges the enumerable properties of the given 'from' object into the given 'to' object, only replacing same named
* properties in the 'to' object if the given replace flag is true. Executes a deep merge if the given deep flag is true,
* otherwise only does a shallow merge. Returns the updated 'to' object.
* Merges the properties of the given 'from' object into the given 'to' object, only replacing same named properties in
* the 'to' object if opts.replace is true. Executes a deep merge if opts.deep is true, otherwise only does a shallow
* merge. Returns the updated 'to' object.
*
* Legacy parameters were (from, to, replace, deep), so for backward compatibility, convert any boolean opts or
* (undefined opts and boolean 4th argument) into an appropriate object opts.
*
* @param {Object} from - the 'from' object from which to get enumerable properties to be merged into the 'to' object
* @param {Object} to - the 'to' object to which to add or deep merge (or optionally replace) properties from the 'from' object
* @param {boolean|undefined} [replace] - whether to replace properties in the 'to' object with same named properties in the from object or not
* @param {boolean|undefined} [deep] - Executes a deep merge if the given deep flag is true, otherwise only does a shallow merge
* @param {Object|undefined} [opts] - optional opts to use
* @param {boolean|undefined} [opts.replace] - whether to replace properties in the 'to' object with same named properties in the from object or not (defaults to not)
* @param {boolean|undefined} [opts.deep] - Executes a deep merge if true, otherwise only does a shallow merge (defaults to shallow)
*/
function merge(from, to, replace, deep) {
function merge(from, to, opts) {
// Legacy parameters were (from, to, replace, deep), so for backward compatibility, convert any boolean opts or
// (undefined opts and boolean 4th argument) into an appropriate object opts
if (typeof opts === "boolean" || (!opts && typeof arguments[3] === "boolean")) {
opts = {replace: !!opts, deep: !!arguments[3]};
}
// Resolve the options from opts
const replace = !!opts && opts.replace === true;
const deep = !!opts && opts.deep === true;
if (!from || typeof from !== 'object') throw new TypeError(`from must be a non-null object`);

@@ -69,3 +84,3 @@ if (!to || typeof to !== 'object') throw new TypeError(`to must be a non-null object`);

} else if (replace) {
dest[i] = srcElementIsObject ? copy(srcElement, true) : srcElement;
dest[i] = srcElementIsObject ? copy(srcElement, {deep: true}) : srcElement;
}

@@ -77,3 +92,3 @@ }

const srcElementIsObject = srcElement && typeof srcElement === 'object';
dest.push(srcElementIsObject ? copy(srcElement, true) : srcElement);
dest.push(srcElementIsObject ? copy(srcElement, {deep: true}) : srcElement);
}

@@ -102,6 +117,6 @@ }

} else if (replace) {
dest[name] = srcPropertyIsObject ? copy(srcProperty, true) : srcProperty;
dest[name] = srcPropertyIsObject ? copy(srcProperty, {deep: true}) : srcProperty;
}
} else {
dest[name] = srcPropertyIsObject ? copy(srcProperty, true) : srcProperty;
dest[name] = srcPropertyIsObject ? copy(srcProperty, {deep: true}) : srcProperty;
}

@@ -116,12 +131,20 @@ }

/**
* Copies the enumerable properties of the given object into a new object. Executes a deep copy if the given deep flag
* is true, otherwise only does a shallow copy. Returns the new copy of the original object or the original object if it
* was NOT a non-null instance of Object.
* Copies the properties of the given object into a new object. Executes a deep copy if opts.deep is true, otherwise
* only does a shallow copy. Returns the new copy of the original object or the original "object" if it was NOT a non-
* null instance of Object.
*
* Legacy parameters were (object, deep), so for backward compatibility, use any true opts as if it was true opts.deep
*
* @param {Object} object - the object from which to copy enumerable properties into a new object
* @param {boolean|undefined} [deep] - Executes a deep copy if the given deep flag is true, otherwise only does a shallow copy
* @param {Object|boolean|undefined} [opts] - optional opts to use (if opts is true, handles it as if opts.deep were true & does a deep copy)
* @param {boolean|undefined} [opts.deep] - Executes a deep copy if opts.deep is true, otherwise only does a shallow copy (defaults to shallow)
*/
function copy(object, deep) {
function copy(object, opts) {
if (!object || typeof object !== 'object') {
return object;
}
// Resolve the options from opts
// Legacy parameters were (object, deep), so for backward compatibility, use any true opts as if it was true opts.deep
const deep = (!!opts && opts.deep === true) || opts === true;
const history = new WeakMap();

@@ -191,15 +214,31 @@

* property name if compact is true.
*
* Legacy parameters were (src, propertyNames, compact, deep, omitPropertyIfUndefined), so for backward compatibility,
* convert any boolean opts or (no opts, but boolean 4th or 5th arg) into an appropriate object opts.
*
* @param {Object} src - the source object from which to copy the named properties
* @param {string[]} propertyNames - the list of named properties to be copied
* @param {boolean|undefined} [compact] - whether to create a flatter, more-compact destination object, which will use
* @param {Object|undefined} [opts] - optional opts to use
* @param {boolean|undefined} [opts.compact] - whether to create a flatter, more-compact destination object, which will use
* any compound property names as is and eliminate any unnecessary intermediate objects or rather create a more-
* conventional, less-compact destination object, which will only have simple property names and all necessary intermediate objects
* @param {boolean|undefined} [deep] - executes a deep copy of each property value if the given deep flag is true, otherwise only does a shallow copy
* @param {boolean|undefined} [omitPropertyIfUndefined] - whether or not to omit any named property that has an undefined value from the destination object
* @param {boolean|undefined} [opts.deep] - executes a deep copy of each property value if the given deep flag is true, otherwise only does a shallow copy
* @param {boolean|undefined} [opts.omitIfUndefined] - whether or not to omit any named property that has an undefined value from the destination object
* @returns {Object} a new object containing copies of only the named properties from the source object
*/
function copyNamedProperties(src, propertyNames, compact, deep, omitPropertyIfUndefined) {
function copyNamedProperties(src, propertyNames, opts) {
if (!src || typeof src !== 'object') {
return src === undefined ? undefined : src === null ? null : {};
}
// Legacy parameters were (src, propertyNames, compact, deep, omitPropertyIfUndefined), so for backward compatibility,
// convert any boolean opts or (no opts, but boolean 4th or 5th arg) into an appropriate object opts
if (typeof opts === "boolean" || (!opts && (typeof arguments[3] === "boolean" || typeof arguments[4] === "boolean"))) {
opts = {compact: !!opts, deep: !!arguments[3], omitIfUndefined: !!arguments[4]};
}
// Resolve the options from opts
const compact = !!opts && opts.compact === true;
const deep = !!opts && opts.deep === true;
const deepOpts = {deep: deep};
const omitIfUndefined = !!opts && opts.omitIfUndefined === true;
const dest = {};

@@ -209,5 +248,5 @@ for (let i = 0; i < propertyNames.length; ++i) {

const propertyValue = getPropertyValue(src, propertyName);
if (!omitPropertyIfUndefined || propertyValue !== undefined) {
if (!omitIfUndefined || propertyValue !== undefined) {
if (compact) {
dest[propertyName] = copy(propertyValue, deep);
dest[propertyName] = copy(propertyValue, deepOpts);
} else {

@@ -224,3 +263,3 @@ const names = propertyName.split(".").map(n => trim(n)).filter(name => isNotBlank(name));

}
d[names[names.length - 1]] = copy(propertyValue, deep);
d[names[names.length - 1]] = copy(propertyValue, deepOpts);
}

@@ -231,2 +270,12 @@ }

return dest;
}
/**
* Extracts an array of key value pairs from the given object. Each key value pair is represented as an array containing
* a key property name followed by its associated value.
* @param {Object} object - an object
* @returns {KeyValuePair[]} an array of key value pairs
*/
function toKeyValuePairs(object) {
return object && typeof object === 'object' ? Object.getOwnPropertyNames(object).map(key => [key, object[key]]) : [];
}
{
"name": "core-functions",
"version": "2.0.14",
"version": "3.0.0",
"description": "Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including strings, booleans, Promises, base 64, Arrays, Objects, standard AppErrors, etc.",

@@ -5,0 +5,0 @@ "author": "Byron du Preez",

'use strict';
const tries = require('./tries');
//const Try = tries.Try;
const Success = tries.Success;
const Failure = tries.Failure;
/**
* Module containing native Promise utility functions, which are installed directly onto the native {@linkcode Promise}
* class.
* An Error subclass thrown to cancel/short-circuit a promise that is waiting for a list of promises to resolve.
* @property (Success|Failure)[]} resolvedOutcomes - a list of resolved outcomes
* @property {Promise[]} unresolvedPromises - a list of unresolved promises
* @property {boolean} completed - whether all of the promises resolved prior to cancellation or not
*/
class CancelledError extends Error {
/**
* Constructs a new CancelledError.
* @param {(Success|Failure)[]} resolvedOutcomes - the list of resolved outcomes
* @param {Promise[]} unresolvedPromises - the list of unresolved promises
*/
constructor(resolvedOutcomes, unresolvedPromises) {
const doneCount = resolvedOutcomes ? resolvedOutcomes.length : 0;
const totalCount = doneCount + (unresolvedPromises ? unresolvedPromises.length : 0);
const message = 'Cancelled' + (totalCount > 0 ? ` after resolving ${doneCount} outcome${doneCount !== 1 ? 's' : ''} of ${totalCount} promise${totalCount !== 1 ? 's' : ''}` : '');
super(message);
Object.defineProperty(this, 'message', {writable: false, enumerable: true, configurable: false});
Object.defineProperty(this, 'name', {value: this.constructor.name});
Object.defineProperty(this, 'resolvedOutcomes', {value: resolvedOutcomes, enumerable: false});
Object.defineProperty(this, 'unresolvedPromises', {value: unresolvedPromises, enumerable: false});
Object.defineProperty(this, 'completed', {value: doneCount === totalCount, enumerable: false});
}
}
/**
* Module containing Promise utility functions.
* @module core-functions/promises

@@ -10,4 +39,6 @@ * @author Byron du Preez

module.exports = {
/** Returns true if the given value is a Promise or at least a "then-able"; otherwise false. */
/** Returns true if the given value is a native Promise; otherwise false */
isPromise: isPromise,
/** Returns true if the given value is a native Promise or a promise-like ("then-able") object; otherwise false */
isPromiseLike: isPromiseLike,
/** Returns a function that will wrap and convert a node-style function into a Promise-returning function */

@@ -18,26 +49,65 @@ wrap: wrap,

/** Triggers execution of the given (typically synchronous) no-arg function, which may throw an error, within a new promise and returns the new promise */
try: tryFn,
try: attempt,
/** Starts a simple timeout Promise, which will resolve after the specified delay in milliseconds */
delay: delay,
/** Transforms a result into a single Promise by using Promise.all (if result is an array of promises), the promise-result or Promise.resolve */
/** Transforms the given result into a single Promise by applying Promise.all to the given result (if it's an Array); otherwise by applying Promise.resolve to the given non-Array result */
allOrOne: allOrOne,
/** Utility function to check if a result is an array of promises */
isArrayOfPromises: isArrayOfPromises
/** Returns a promise that will return a list of Success or Failure outcomes, each of which contains either a resolved value or a rejected error, for the given promises */
every: every,
/** Maps the given promise to a native Promise of a Success or Failure outcome */
one: one,
/** Transforms the given promise-like object (or non-promise value) into a native Promise */
toPromise: toPromise,
/** An Error subclass thrown to cancel/short-circuit a promise that is waiting for a list of promises to resolve. */
CancelledError: CancelledError
};
const timers = require('./timers');
/**
* Returns true if the given value is a Promise or at least a "then-able"; otherwise false.
* Returns true if the given value is a native Promise; otherwise false.
* @param {*} value - the value to check
* @returns {boolean|*} true if its a promise (or a "then-able"); false otherwise
* @returns {boolean|*} true if its a native Promise; false otherwise
*/
function isPromise(value) {
return value instanceof Promise || (value && value.then && typeof value.then === 'function');
return value instanceof Promise;
}
if (!Promise.isPromise) { // polyfill-safe guard check
Promise.isPromise = isPromise;
/**
* Returns true if the given value is a native Promise or a promise-like ("then-able") object; otherwise false.
* @param {*} value - the value to check
* @returns {boolean|*} true if its a promise or a promise-like ("then-able") object; false otherwise
*/
function isPromiseLike(value) {
return value instanceof Promise || (!!value && !!value.then && typeof value.then === 'function');
}
/**
* Transforms the given promise-like object (or non-promise value) into a native Promise using the following process:
* 1. If the given promiseLike is already a native Promise, then just returns it;
* 2. If the given promiseLike is a "then-able", promise-like object (i.e. it has a "then" method)***, then wraps a call
* to its "then" method in a new native Promise (in order to transfer its resolved result or rejected error to the
* new Promise) and then returns the new Promise; otherwise
* 3. If the given promiseLike is anything else, returns Promise.resolve(promiseLike).
*
* ***NB: Any given "then-able" promiseLike MUST accept the same arguments as Promise.then (i.e. a resolve function and
* a reject function) - if this is NOT the case, then do NOT use this function on it!
*
* @param {Promise|PromiseLike|*} promiseLike - the promise-like object to convert into a native promise
* @returns {Promise} a native Promise of the given promise-like object's resolved result or rejected error
*/
function toPromise(promiseLike) {
return promiseLike instanceof Promise ? promiseLike : isPromiseLike(promiseLike) ?
new Promise((resolve, reject) => {
try {
// Assumption: "then-able" accepts the same arguments as Promise.then
promiseLike.then(
result => resolve(result),
error => reject(error)
);
} catch (err) {
reject(err)
}
}) : Promise.resolve(promiseLike);
}
/**
* Wraps and converts the given node-style function into a Promise-returning function, which when invoked must be passed

@@ -53,2 +123,4 @@ * all of the wrapped function's arguments other than its last callback argument.

* Example:
* // Assuming an import as follows
* const Promises = require('core-functions/promises');
*

@@ -67,3 +139,3 @@ * // crude example of a node-style function

*
* Promise.wrap(nodeStyleFunction)(arg1, arg2, ..., argN)
* Promises.wrap(nodeStyleFunction)(arg1, arg2, ..., argN)
* .then(result => ...)

@@ -74,7 +146,7 @@ * .catch(err => ...);

* Example:
* Promise.wrap(obj.nodeStyleMethod.bind(obj))(arg1, arg2, ..., argN)
* Promises.wrap(obj.nodeStyleMethod.bind(obj))(arg1, arg2, ..., argN)
* .then(result => ...)
* .catch(err => ...);
*
* OR instead use the Promise.wrapMethod function below.
* OR instead use the Promises.wrapMethod function below.
*

@@ -91,3 +163,3 @@ * @param {Function} fn - a Node-callback style function to promisify

const args = new Array(len + 1);
for (let i = 0; i < len; i++) {
for (let i = 0; i < len; ++i) {
args[i] = arguments[i];

@@ -105,5 +177,2 @@ }

}
if (!Promise.wrap) { // polyfill-safe guard check
Promise.wrap = wrap;
}

@@ -117,5 +186,6 @@ /**

* Example:
* const Promises = require('core-functions/promises');
*
* // crude example of a node-style method
* var object = {
* const object = {
* nodeStyleMethod: function nodeStyleMethod(arg1, arg2, ..., argN, callback) {

@@ -133,3 +203,3 @@ * // example synchronous invocation of callback with error

*
* Promise.wrapMethod(obj, obj.nodeStyleMethod)(arg1, arg2, ..., argN)
* Promises.wrapMethod(obj, obj.nodeStyleMethod)(arg1, arg2, ..., argN)
* .then(result => ...)

@@ -149,3 +219,3 @@ * .catch(err => ...);

const args = new Array(len + 1);
for (let i = 0; i < len; i++) {
for (let i = 0; i < len; ++i) {
args[i] = arguments[i];

@@ -163,9 +233,7 @@ }

}
if (!Promise.wrapMethod) { // polyfill-safe guard check
Promise.wrapMethod = wrapMethod;
}
/**
* Triggers execution of the given (typically synchronous) no-arg function, which may throw an error, within a new
* promise and returns this new promise.
* Triggers execution of the given (typically synchronous) no-arg function, which may throw an error, and returns a
* promise of its result or failure.
*
* Use this function to safely start a new promise chain and ensure that any errors that may be thrown (when the given

@@ -175,7 +243,10 @@ * function's body is executed) cause the promise to be properly rejected.

*
* Preamble to examples:
* const Promises = require('core-functions/promises');
*
* Example 1:
* Promise.try(() => functionThatMayThrow(arg1, arg2, ..., argN));
* Promises.try(() => functionThatMayThrow(arg1, arg2, ..., argN));
*
* Example 2:
* Promise.try(() => {
* Promises.try(() => {
* // code that may throw an error

@@ -192,14 +263,10 @@ * let result = functionThatMayThrow(arg1, arg2, ..., argN)

*
* Promise.try(functionToTry);
* Promises.try(functionToTry);
*
* @param {Function} fn - the function to execute within a promise
* @returns {Promise} the promise to execute the given function
* @returns {Promise} a promise of the executed function's result or failure
*/
function tryFn(fn) {
//return new Promise((resolve, reject) => resolve(fn()));
function attempt(fn) {
try {
const promiseOrResult = fn();
// If the executed fn returned a promise, just return that; otherwise wrap its non-promise result in a promise
//return isPromise(promiseOrResult) ? promiseOrResult : Promise.resolve(promiseOrResult);
return allOrOne(promiseOrResult);
return Promise.resolve(fn());
} catch (err) {

@@ -209,16 +276,12 @@ return Promise.reject(err);

}
if (!Promise.try) { // polyfill-safe guard check
Promise.try = tryFn;
}
/**
* Starts a simple timeout Promise, which will resolve after the specified delay in milliseconds. If any object is
* passed into this function as the cancellable argument, then this function will install a cancelTimeout method on it,
* which accepts a single optional mustResolve argument and which if subsequently invoked will cancel the timeout and
* either resolve the promise (if mustResolve) or reject the promise (default), but ONLY if the timeout
* has not triggered yet.
* Starts a simple timeout Promise, which will resolve after the specified delay in milliseconds. If any non-null object
* is passed into this function as the `cancellable` argument, then this function will also install a `cancelTimeout` method
* on it, which accepts a single optional mustResolve argument and which if subsequently invoked will cancel the timeout
* and either resolve the promise (if mustResolve) or reject the promise (default), but ONLY if the timeout has not
* triggered yet.
*
* @param {number} ms - the number of milliseconds to delay
* @param {Object|undefined|null} [cancellable] - an arbitrary object onto which a cancelTimeout method will be installed
* @returns {Function|undefined} [cancellable.cancelTimeout] - installs a cancelTimeout method on the given cancellable
* @param {Object|DelayCancellable|*} [cancellable] - an arbitrary object onto which a `cancelTimeout` method will be installed
* @returns {Promise} the timeout Promise

@@ -255,70 +318,118 @@ */

}
if (!Promise.delay) { // polyfill-safe guard check
Promise.delay = delay;
}
/**
* Transforms the given result into a single Promise by doing the following: applies Promise.all to the given result and
* returns its Promise (if result is an array of promises); or returns the given promise result (if it's already a
* Promise); otherwise wraps the given non-promise result in a Promise.resolve.
* Transforms the given result into a single Promise by applying Promise.all to the given result (if it's an Array);
* otherwise by applying Promise.resolve to the given non-Array result. Note: Promise.resolve only wraps non-promises,
* so there is no need to do any extra isPromise check for a single promise.
*
* @param {Promise|Promise[]|*} result - a promise or an array of promises or a non-promise result
* @returns {Promise} a single promise containing the given result or containing the result's results if the result was
* an array of promises
* @param {Promise[]|*[]|Promise|*} result - an Array of promises (and/or non-promise values); OR a single promise (or
* non-promise value)
* @returns {Promise} a single promise of the given non-Array result's result or of all of the given Array result's results
*/
function allOrOne(result) {
return Array.isArray(result) && result.every(r => isPromise(r)) ? Promise.all(result) :
isPromise(result) ? result : Promise.resolve(result);
return Array.isArray(result) ? Promise.all(result) : Promise.resolve(result);
}
if (!Promise.allOrOne) { // polyfill-safe guard check
Promise.allOrOne = allOrOne;
}
/**
* Returns true if the given result is an array of promises; false otherwise.
* @param {*} result - the result to check
* @returns {boolean} true if array of promises; false otherwise
* Returns a promise that will return a list of Success or Failure outcomes, each of which contains either a resolved
* value or a rejected error, for the given promises in the same sequence as the given promises, when every one of the
* given promises has resolved. The given array of `promises` can contain zero or more promises and/or non-promise
* values. If any non-null object is passed into this function as the `cancellable` argument, then this function will
* also install a `cancel` method on it, which expects no arguments and returns true if all of the promises have already
* resolved; or false otherwise. If this `cancel` method is subsequently invoked, it will attempt to short-circuit the
* `every` promise that is waiting for any remaining promises to complete by instead throwing a `CancelledError`, which
* will contain a list of any resolved outcomes and a list of any unresolved promises.
*
* Note: The returned promise should NEVER reject (UNLESS it is cancelled via the `cancellable`), since it only resolves
* with Success or Failure outcomes, which indicate whether their corresponding given promises resolved or failed.
*
* @param {Promise[]|*[]} promises - a list of promises from which to collect their outcomes
* @param {Object|Cancellable|*} [cancellable] - an arbitrary object onto which a `cancel` method will be installed
* @returns {Promise.<(Success|Failure)[]|CancelledError>} a promise that will resolve with a list of Success or Failure
* outcomes for the given promises (unless cancelled); otherwise reject with a `CancelledError` (if cancelled)
* @throws {Error} an error if the given `promises` is not an array
*/
function isArrayOfPromises(result) {
return Array.isArray(result) && result.every(r => isPromise(r))
}
if (!Promise.isArrayOfPromises) { // polyfill-safe guard check
Promise.isArrayOfPromises = isArrayOfPromises;
}
/**
* @typedef {{result:*}|{error:Error}} ResultOrError - An object containing either a result or an error.
*/
/**
* Returns a promise that returns a list of either a result or an error for each of the given promises in the same
* sequence as the given promises when every one of the given promises has resolved.
* @param {Promise[]|Promise...} promises - a list of promises from which to collect their resolved results or their rejected errors
* @returns {Promise.<ResultOrError[]>} promises - a promise of a list of either a result or an error for each of the
* given promises in the same sequence as the given promises
*/
function every(promises) {
const ps = Array.isArray(promises) ? promises : isPromise(promises) ? arguments : [];
const n = ps.length;
function every(promises, cancellable) {
if (!Array.isArray(promises)) {
throw new Error('The `every` function only accepts an array of promises and/or non-promises');
}
const n = promises.length;
if (n <= 0) {
return Promise.resolve([]);
}
const results = new Array(n);
const last = n - 1;
const outcomes = new Array(n);
let completed = false;
let cancelled = false;
// Set up a cancel method on the given cancellable object (if any)
if (cancellable && typeof cancellable === 'object') {
cancellable.cancel = () => {
cancelled = true;
return completed;
}
}
function next(i) {
return ps[i].then(
result =>
results[i] = {result: result},
err =>
results[i] = {error: err})
.then(() =>
// If remaining list (after i) contains at least one more promise, then continue; otherwise done
i < n - 1 ? next(i + 1) : results
);
let p = promises[i];
if (i > 0) {
// If we are at any element other than the first and it's NOT a promise then simply collect its value and recurse
if (!isPromiseLike(p)) {
outcomes[i] = new Success(p);
if (i < last) {
// Short-circuit if cancelled by throwing a cancelled error with the outcomes collected so far
if (cancelled) throw new CancelledError(outcomes.slice(0, i + 1), promises.slice(i + 1));
return next(i + 1);
}
completed = true;
return outcomes;
}
} else {
// Ensure that we have a native promise at the first element to start the chain
p = toPromise(p);
}
// The current element is now definitely a promise, so continue by calling its then method
return p.then(
value => {
outcomes[i] = new Success(value);
// If still not at the last element, then continue with a recursive call; otherwise return outcomes, since done
if (i < last) {
// Short-circuit if cancelled by throwing a cancelled error with the outcomes collected so far
if (cancelled) throw new CancelledError(outcomes.slice(0, i + 1), promises.slice(i + 1));
return next(i + 1);
}
completed = true;
return outcomes;
},
err => {
outcomes[i] = new Failure(err);
// If still not at the last element, then continue with a recursive call; otherwise return results, since done
if (i < last) {
// Short-circuit if cancelled by throwing a cancelled error with the outcomes collected so far
if (cancelled) throw new CancelledError(outcomes.slice(0, i + 1), promises.slice(i + 1));
return next(i + 1);
}
completed = true;
return outcomes;
}
);
}
// Start the process at the first element
return next(0);
}
if (!Promise.every) { // polyfill-safe guard check
Promise.every = every;
}
/**
* Maps the given single promise (or promise-like object or non-promise value) to a native Promise of a Success or
* Failure resolution. This function is similar to the `every` function, except that it only handles one promise.
* Note: The returned promise should NEVER reject, since it only resolves to either a Success with the resolved value or
* a Failure with the rejected error.
* @param {Promise.<*>|PromiseLike|*} promise - the promise to be mapped
* @returns {Promise.<(Success|Failure)>} a native Promise of a Success or Failure
*/
function one(promise) {
return toPromise(promise).then(
value => new Success(value),
err => new Failure(err)
);
}

@@ -1,5 +0,5 @@

# core-functions v2.0.14
# core-functions v3.0.0
Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including
strings, booleans, Promises, base 64, Arrays, Objects, standard AppErrors, etc.
strings, numbers, booleans, Dates, Promises, base 64, Arrays, Objects, standard AppErrors, sorting utilities, etc.

@@ -11,7 +11,10 @@ Currently includes:

- booleans.js - boolean utilities
- dates.js - Date utilities
- numbers.js - number utilities
- objects.js - Object utilities
- promises.js - native Promise utilities
- sorting.js - sorting utilities
- strings.js - string utilities
- timers.js - Timer/timeout utilities
- tries.js - Try, Success and Failure classes representing the outcome of a function execution

@@ -49,11 +52,17 @@ This module is exported as a [Node.js](https://nodejs.org/) module.

To use the Promise utilities (as static methods on the native `Promise` class)
To use the Date utilities
```js
require('core-functions/promises');
const Dates = require('core-functions/dates');
```
To use the Promise utilities (as exported functions)
To use the sorting utilities
```js
const promises = require('core-functions/promises');
const sorting = require('core-functions/sorting');
```
To use the Promise utilities
```js
const Promises = require('core-functions/promises');
```
To use the Object utilities

@@ -74,2 +83,44 @@ ```js

To use the `Try`, `Success` and `Failure` classes
```js
const tries = require('./tries');
const Try = tries.Try;
const Success = tries.Success;
const Failure = tries.Failure;
// Simulate getting a Success outcome from successful execution of a function, which returns a value
const outcome = Try.try(() => 'Abc');
// outcome = new Success('Abc')
assert(outcome.isSuccess());
assert(outcome.value === 'Abc');
// using map function to convert a Success('Abc') outcome's value into a Success('AbcXyz')
const outcome1 = outcome.map(v => v + 'Xyz');
assert(outcome1.isSuccess());
assert(outcome1.value === 'AbcXyz');
// Simulate getting a Failure outcome from unsuccessful execution of a function, which throws an error
const testErr = new Error("Err"); // an arbitrary error for the example
const outcome2 = Try.try(() => {throw testErr});
// outcome2 is equivalent to new Failed(new Error("Err"))
assert(outcome2.isFailure());
assert(outcome2.error === testErr);
// using recover function to convert a Failed outcome's error into a Success(123)
const outcome3 = outcome2.recover(err => 123);
assert(outcome3.isSuccess());
assert(outcome3.value === 123);
// ... or using map function to handle both successes & failures cases at the same time (similar to Promise.then)
const outcome4 = outcome.map(
value => {
return value * 42;
},
err => {
console.log(err.stack);
return -1;
}
);
```
To use the standard application errors

@@ -109,2 +160,84 @@ ```js

### 3.0.0
- Non-backward compatible changes & fixes to `promises.js` module:
- Removed all poly-filling & injection of `promises` module functions onto native `Promise` class
- Changed behaviour of the `try` function to NOT use `Promise.all` when the given function returns an array of promises,
so that it instead preserves ALL of the executed function's returned promises, "wrapped" in a `Promise.resolve`
- Changed behaviour of the `allOrOne` and `every` functions to both accept any mixture of promise and/or non-promise
values, in order to bring their behaviour in line with that of the standard `Promise.all` method
- Changed the `every` function to ONLY accept an array of `promises` (i.e. it no longer supports var args containing
promises) and added a new `cancellable` parameter to enable the wait for every promise to complete to be short-
circuited at the earliest possible opportunity and the `every` function will then instead return a rejected
`CancelledError` from which the `resolvedOutcomes` and `unresolvedPromises` can be retrieved
- Changed the `every` function's returned resolutions from literal objects containing `result` or `error` properties
to use the new `tries` modules's `Success`, `Failure` and `Try` classes instead (NB: Success has a `value` property,
and not a `result` property, so this change is not backward-compatible)
- Fixed defect that was causing the `every` function to return an empty array when the first argument was not a promise
- Renamed existing `isPromise` function to `isPromiseLike` & added new `isPromise` function that ONLY returns true for native promises
- Removed the `isArrayOfPromises` function, which was no longer useful & would have had to change after `isPromise` changed
- Added a new `one` function to convert a single promise into a native Promise that resolves to a `Success` or `Failure` outcome
- Added a new `toPromise` function to attempt to convert a promise-like into a native promise
- Used it in the new `one` function
- Changed the `every` function to use it to ensure that the first promise in the chain becomes a native Promise
- Added new `tries.js` module:
- Added `Try` superclass and `Success` and `Failure` subclasses modelled after their same-named Scala counterparts
- Added new `dates.js` module:
- Added `simpleISODateTimeRegex` & `simpleISODateRegex` regular expressions
- Added `extendedISODateTimeRegex` & `extendedISODateRegex` regular expressions
- Added `isSimpleISODateTimeLike` & `isSimpleISODateLike` functions
- Added `isSimpleISODateTime` & `isSimpleISODate` functions
- Added `isExtendedISODateTimeLike` & `isExtendedISODateLike` functions
- Added `isExtendedISODateTime` & `isExtendedISODate` functions
- Added `toSimpleISODateTime` & `toSimpleISODate` functions
- Added `toDateTime` & `toExtendedISODate` functions
- Added `isValidDate` function
- Added new `sorting.js` module:
- Added `SortType` "enum" object to defined the types of sorting supported
- Added `compareNumbers`, `compareStrings`, `compareBooleans`, `compareDates`, `compareIntegerLikes` &
`compareUndefinedOrNull` compare functions to be used with Array `sort` method
- Added `toSortable` function, which resolves the appropriate sort type and compare function to use for a given array
of values intended to be sorted and also maps the values into an array of consistent, sortable types of values
- Added `sortSortable` function (primarily for testing), which simply sorts a "Sortable" object's `sortableValues`
using its `compare` function
- Changes to `numbers.js` module:
- Added `integerRegex`, `numberRegex`, `zeroRegex`, `leadingZeroesRegex` & `trailingZeroesRegex` regular expressions
- Added `isInteger` & `isSafeInteger` functions
- Added `isNumberLike`, `isIntegerLike` & `isZeroLike` functions
- Added `toNumberLike`, `toDecimalLike`, `toDecimalLikeOrNaN`, `toIntegerLike`, `toIntegerLikeOrNaN` &
`toNumberOrIntegerLike` functions
- Added `removeLeadingZeroes`, `removeTrailingZeroes`, `zeroPadLeft` & `removeSignIfZero` functions
- Added `nearlyEqual` function for testing numbers for approximate equality
- Changes to `strings.js` module:
- Added `stringifyKeyValuePairs` function
- Added null-safe `toLowerCase` function
- Changes to `stringify` function:
- Added special cases to better support Dates, Promises, Maps & WeakMaps
- Improved conversion of Errors - changed default behaviour to use a variation of Error toString() instead of always
treating Errors as Objects (the latter behaviour is now available by passing opts.avoidErrorToString as true)
- Replaced `useToStringForErrors`, `avoidToJSONMethods` & `quoteStrings` parameters with a single, optional `opts`
parameter with optional `avoidErrorToString` (NB: renamed and changed default behaviour to use toString() for
Errors), `avoidToJSONMethods` and `quoteStrings` properties.
- Added support for also handling any legacy arguments passed instead of a new `opts` object, which means this
API change is still backward-compatible
- Added new `useJSONStringify`, `replacer` & `space` opts properties to enable `stringify`'s behaviour to be
switched to simply use `JSON.stringify` instead via its new `opts` argument
- Changes to `objects.js` module:
- Added `toKeyValuePairs` function
- Changes to `merge` function:
- Replaced `replace` & `deep` parameters with a single, optional `opts` parameter with optional `replace` & `deep`
properties
- Added support for also handling any legacy arguments passed instead of a new `opts` object, which means this
API change is still backward-compatible
- Changes to `copy` function:
- Replaced `deep` parameter with a single, optional `opts` parameter with an optional `deep` property
- Added support for also handling any legacy `deep` boolean argument passed instead of a new `opts` object, which
means this API change is still backward-compatible
- Changes to `copyNamedProperties` function:
- Replaced `compact`, `deep` & `omitPropertyIfUndefined` parameters with a single, optional `opts` parameter with
optional `replace`, `deep` & `omitIfUndefined` properties
- Added support for also handling any legacy arguments passed instead of a new `opts` object, which means this
API change is still backward-compatible
- Added new `type-defs` "module" to gather the various type definitions into one place
- Removed `test/testing.js`
### 2.0.14

@@ -111,0 +244,0 @@ - Added `copyNamedProperties` function to `objects.js` module

@@ -26,8 +26,23 @@ 'use strict';

stringify: stringify,
nthIndexOf: nthIndexOf
nthIndexOf: nthIndexOf,
toLowerCase: toLowerCase,
stringifyKeyValuePairs: stringifyKeyValuePairs
};
const inspectOpts = {depth: null, breakLength: Infinity}; // unfortunately breakLength is only available in later Node versions than 4.3.2
const breakRegex = /\s*[\n\r]+\s*/g;
const promiseInspectRegex = /^(Promise \{)([\s\n\r]+)(.*)([\s\n\r]+)(})$/;
// Attempts to get Node's util.js inspect function (if available)
const inspect = (() => {
try {
return require('util').inspect;
} catch (_) {
return undefined;
}
})();
/**
* Returns true if the given value is a string; false otherwise.
* @param {*} value the value to check
* @param {*} value - the value to check
* @return {boolean} true if its a string; false otherwise

@@ -41,3 +56,3 @@ */

* Returns true if the given string is blank (i.e. undefined, null, empty or contains only whitespace); false otherwise.
* @param {string|String} s the string to check
* @param {string|String} s - the string to check
* @return {boolean} true if blank; false otherwise

@@ -52,3 +67,3 @@ */

* otherwise.
* @param {string|String} s the string to check
* @param {string|String} s - the string to check
* @return {boolean} true if NOT blank; false otherwise

@@ -62,3 +77,3 @@ */

* Trims the given value if it is a string; otherwise returns a non-string value as is.
* @param {*} value the value to trim
* @param {*} value - the value to trim
* @returns {string|*} the trimmed string or the original non-string value

@@ -73,3 +88,3 @@ */

* the non-undefined, non-null, non-string value as is.
* @param {*} value the value to trim
* @param {*} value - the value to trim
* @returns {string|*} the trimmed string; an empty string; or the original non-undefined, non-null, non-string value

@@ -84,9 +99,14 @@ */

* Returns the given value as a string with special case handling for undefined, null, strings, numbers, booleans,
* Strings, Numbers, Booleans, Errors, Functions, Arrays, Objects and special numbers (i.e. Infinity, -Infinity and NaN)
* and also handles circular dependencies. Similar to {@linkcode JSON#stringify}, but shows more about the given value
* than JSON.stringify does, for example:
* - JSON.stringify(undefined) returns undefined, but this returns 'undefined'
* Strings, Numbers, Booleans, Dates, Promises, Errors, Functions, Arrays, Maps, WeakMaps, Objects and special numbers
* (i.e. Infinity, -Infinity and NaN) and also handles circular dependencies. However, if opts.useJSONStringify is true,
* then does NONE of this and instead just returns JSON.stringify(value, opts.replacer, opts.space) ignoring other opts.
*
* Similar to {@linkcode JSON#stringify}, but shows more about the given value than JSON.stringify does, for example:
* - JSON.stringify(undefined) returns undefined literally, but this returns 'undefined'
* - JSON.stringify({a:undefined}) returns {}, but this returns '{"a":undefined}'
* - JSON.stringify(new Error("Boom")) returns '{}', but this returns either '{"message":"Boom","name":"Error"}' (if
* useToStringForErrors is false) or '[Error: Boom]' (if useToStringForErrors is true)
* - JSON.stringify(new Error("Boom")) returns '{}', but this returns EITHER:
* - '[Error: Boom]' (or '[Error: Boom {<extras>}]') WHEN opts.avoidErrorToString is false (default); OR
* - '{"name": "Error", "message": "Boom"}' (or '{"name": "Error", "message": "Boom", <extras>}') WHEN
* opts.avoidErrorToString is true,
* where <extras> above is any and all extra properties on the Error object OTHER than name, message and stack
* - JSON.stringify(func) with function func() {} returns undefined, but this returns '[Function: func]'

@@ -107,8 +127,23 @@ * - JSON.stringify({fn: func}) with function func() {} returns '{}', but this returns '{"fn": [Function: func]}'

* @param {*} value the value to stringify
* @param {boolean|undefined} [useToStringForErrors] - whether to stringify errors using toString or as normal objects (default)
* @param {boolean|undefined} [avoidToJSONMethods] - whether to avoid using objects' toJSON methods or not (default)
* @param {boolean|undefined} [quoteStrings] - whether to surround simple string values with double-quotes or not (default)
* @param {StringifyOpts|undefined} [opts] - optional options to control how the value gets stringified
* @returns {string} the value as a string
*/
function stringify(value, useToStringForErrors, avoidToJSONMethods, quoteStrings) {
function stringify(value, opts) {
// Legacy parameters were (value, useToStringForErrors, avoidToJSONMethods, quoteStrings), so for backward compatibility,
// convert any boolean opts or (no opts, but boolean 4th or 5th arg) into an appropriate object opts
if (typeof opts === "boolean" || (!opts && (typeof arguments[3] === "boolean" || typeof arguments[4] === "boolean"))) {
opts = {avoidErrorToString: !opts, avoidToJSONMethods: !!arguments[3], quoteStrings: !!arguments[4]};
}
// Resolve any options requested
const useJSONStringify = !!opts && opts.useJSONStringify == true;
if (useJSONStringify) {
const replacer = opts && opts.replacer ? opts.replacer : undefined;
const space = opts && opts.space ? opts.space : undefined;
return JSON.stringify(value, replacer, space);
}
const avoidErrorToString = !!opts && opts.avoidErrorToString == true;
const avoidToJSONMethods = !!opts && opts.avoidToJSONMethods == true;
const quoteStrings = !!opts && opts.quoteStrings == true;
const history = new WeakMap();

@@ -134,6 +169,2 @@

// Special case for Errors - use toString() if directed, since stringify on most errors, just returns "{}"
const valueIsError = value instanceof Error;
if (valueIsError && useToStringForErrors) return `[${value}]`;
// Special case for Functions - show them as [Function: {function name}]

@@ -143,6 +174,2 @@ if (typeOfValue === 'function') return isNotBlank(value.name) ? `[Function: ${value.name}]` : '[Function: anonymous]';

if (typeOfValue === 'object') {
// Special case for objects that have toJSON methods
if (!avoidToJSONMethods && typeof value.toJSON === 'function') {
return JSON.stringify(value.toJSON());
}
// Check if already seen this same object before

@@ -161,2 +188,39 @@ if (history.has(value)) {

// Special case for console, which is otherwise very verbose
if (value instanceof console.Console) {
return value === console ? '[Console]' : '[Console {}]';
}
// Special case for Dates - show them as ISO strings rather than default toString()
if (value instanceof Date) {
const date = value.toJSON();
return date ? date : '[Invalid Date]';
}
// Special case for Promises
if (value instanceof Promise) {
// Attempts to use Node's util.js inspect function (if available), which returns more-useful strings like:
// 'Promise { <pending> }'; 'Promise { "Abc" }'
return inspect ? `[${cleanInspectedPromise(inspect(value, inspectOpts))}]` : '[Promise]';
}
// Special case for WeakMaps, which cannot be enumerated
if (value instanceof WeakMap) {
return `[WeakMap]`
}
// Special case for Maps, which currently do not have a useful toString() and are also not handled well by JSON.stringify
if (value instanceof Map) {
let result = '[Map {';
let first = true;
let i = 0;
for (let kv of value.entries()) {
const k = kv[0], v = kv[1];
result += `${!first ? ', ' : ''}${stringifyWithHistory(k, `${name}[${i}].KEY`, true)} => ${stringifyWithHistory(v, `${name}[${i}].VAL`, true)}`;
first = false;
++i;
}
result += '}]';
return result;
}
// Special case for Array objects, stringify each of its elements

@@ -167,15 +231,34 @@ if (Array.isArray(value)) {

// Special case for objects that have toJSON methods
if (!avoidToJSONMethods && typeof value.toJSON === 'function') {
return JSON.stringify(value.toJSON());
}
// Stringify the object
let names = Object.getOwnPropertyNames(value);
let prefix = '{';
let suffix = '}';
if (valueIsError) {
// Special case for Error objects - include message and name (if any), but exclude stack, which are all normally hidden with JSON.stringify
// First exclude name, message and stack
// Special cases for Errors
if (value instanceof Error) {
// First exclude name, message and stack from the list of names
names = names.filter(n => n !== 'stack' && n !== 'name' && n !== 'message');
// Second re-add name and message to the front of the list
if (value.message) names.unshift('message');
if (value.name) names.unshift('name');
if (avoidErrorToString) {
// Special case for Errors when opts.avoidErrorToString is true - treat Error as if it was just an object, but
// include name and message (if any) at the front of the list of names (but still exclude stack)
if (value.message) names.unshift('message');
if (value.name) names.unshift('name');
} else {
// Special case for Errors when opts.avoidErrorToString is true- use the Error's toString(), but also include
// any extra properties OTHER than message, name and stack in the output
if (names.length <= 0) {
return `[${value}]`; // If no extra properties then just use Error's toString()
}
// Change the prefix to include the Error's toString() info and continue as normal
prefix = `[${value} {`;
suffix = '}]';
}
}
let result = '{';
let result = prefix;
for (let i = 0; i < names.length; ++i) {

@@ -189,3 +272,3 @@ const propertyName = names[i];

}
result += '}';
result += suffix;
return result;

@@ -235,2 +318,34 @@ }

return index;
}
/**
* Converts the given value to lower-case if it is a string; otherwise returns a non-string value as is.
* @param {*} value - the value to convert to lower-case
* @returns {string|*} the lower-case string or the original non-string value
*/
function toLowerCase(value) {
return typeof value === 'string' || value instanceof String ? value.toLowerCase() : value;
}
/**
* Creates a string version of the given key value pairs array, with its keys and values separated by the given
* keyValueSeparator and its pairs separated by the given pairSeparator.
* @param {KeyValuePair[]} keyValuePairs - an array of key value pairs
* @param {StringifyKeyValuePairsOpts|undefined} [opts] - optional options to control how the array of key value pairs get stringified
* @returns {string} a string version of the given key value pairs array
*/
function stringifyKeyValuePairs(keyValuePairs, opts) {
const keyValueSeparator = opts && typeof opts.keyValueSeparator === 'string' ? opts.keyValueSeparator : ':';
const pairSeparator = opts && typeof opts.pairSeparator === 'string' ? opts.pairSeparator : ',';
return keyValuePairs && keyValuePairs.length > 0 ?
keyValuePairs.map(kv => `${kv[0]}${keyValueSeparator}${stringify(kv[1], opts)}`).join(pairSeparator) : '';
}
/**
* Workaround to avoid default behaviour that inserts new line(s) when length too long (>60?) by removing any carriage
* returns and line feeds.
* @param {string} inspectedPromise - util.inspect result for a Promise instance
*/
function cleanInspectedPromise(inspectedPromise) {
return inspectedPromise.replace(breakRegex, ' ').replace(promiseInspectRegex, '$1 $3 $5'); //.replace(/"/g, '\\"').replace(/\\'/g, '"')
}

@@ -42,19 +42,22 @@ 'use strict';

const testing = require('./testing');
// const okNotOk = testing.okNotOk;
// const checkOkNotOk = testing.checkOkNotOk;
// const checkMethodOkNotOk = testing.checkMethodOkNotOk;
const equal = testing.equal;
// const checkEqual = testing.checkEqual;
// const checkMethodEqual = testing.checkMethodEqual;
const immutable = testing.immutable;
function immutable(t, obj, propertyName, prefix) {
const now = new Date().toISOString();
const opts = {quoteStrings: true};
try {
obj[propertyName] = now;
t.fail(`${prefix ? prefix : ''}${stringify(obj, opts)} ${propertyName} is supposed to be immutable`);
} catch (err) {
// Expect an error on attempted mutation of immutable property
t.pass(`${prefix ? prefix : ''}${stringify(obj, opts)} ${propertyName} is immutable`);
//console.log(`Expected error ${err}`);
}
}
test('AppError must be initialized ', t => {
function check(appError, message, code, httpStatus, cause, causeStatus) {
equal(t, appError.message, message, `${appError} message`);
equal(t, appError.code, code, `${appError} code`);
equal(t, appError.httpStatus, httpStatus, `${appError} httpStatus`);
equal(t, appError.cause, cause, `${appError} cause`);
equal(t, appError.causeStatus, causeStatus, `${appError} causeStatus`);
t.equal(appError.message, message, `${appError} message must be ${message}`);
t.equal(appError.code, code, `${appError} code must be ${appError}`);
t.equal(appError.httpStatus, httpStatus, `${appError} httpStatus must be ${httpStatus}`);
t.equal(appError.cause, cause, `${appError} cause must be ${cause}`);
t.equal(appError.causeStatus, causeStatus, `${appError} causeStatus must be ${causeStatus}`);
}

@@ -103,7 +106,7 @@

function check(appError, message, code, httpStatus, cause, causeStatus) {
equal(t, appError.message, message, `${appError} message`);
equal(t, appError.code, code, `${appError} code`);
equal(t, appError.httpStatus, httpStatus, `${appError} httpStatus`);
equal(t, appError.cause, cause, `${appError} cause`);
equal(t, appError.causeStatus, causeStatus, `${appError} causeStatus`);
t.equal(appError.message, message, `${appError} message must be ${message}`);
t.equal(appError.code, code, `${appError} code must be ${code}`);
t.equal(appError.httpStatus, httpStatus, `${appError} httpStatus must be ${httpStatus}`);
t.equal(appError.cause, cause, `${appError} cause must be ${cause}`);
t.equal(appError.causeStatus, causeStatus, `${appError} causeStatus must be ${causeStatus}`);
}

@@ -183,8 +186,8 @@

t.ok(appError instanceof type, `toAppError(${stringify(error)}) -> (${appError}) must be instanceof ${type.name}`);
equal(t, appError.name, type.name, `toAppError(${stringify(error)}) -> (${appError}) name`);
t.equal(appError.name, type.name, `toAppError(${stringify(error)}) -> (${appError}) name must be ${type.name}`);
if (isBlank(message)) {
equal(t, appError.message, trimOrEmpty(error.message), `toAppError(${stringify(error)}, ${message}, ${code}) -> (${appError}) message`);
t.equal(appError.message, trimOrEmpty(error.message), `toAppError(${stringify(error)}, ${message}, ${code}) -> (${appError}) message must be ${trimOrEmpty(error.message)}`);
} else if (isNotBlank(message)) {
equal(t, appError.message, stringify(trimOrEmpty(message)), `toAppError(${stringify(error)}, ${message}, ${code}) -> (${appError}) message`);
t.equal(appError.message, stringify(trimOrEmpty(message)), `toAppError(${stringify(error)}, ${message}, ${code}) -> (${appError}) message must be ${stringify(trimOrEmpty(message))}`);
}

@@ -202,5 +205,5 @@

error.name ? error.name : appError.name;
equal(t, appError.code, expectedCode, `toAppError(${stringify(error)}, ${message}, ${code}) -> (${appError}) code`);
t.equal(appError.code, expectedCode, `toAppError(${stringify(error)}, ${message}, ${code}) -> (${appError}) code must be ${expectedCode}`);
} else if (isNotBlank(code)) {
equal(t, appError.code, stringify(trimOrEmpty(code)), `toAppError(${stringify(error)}, ${message}, ${code}) -> (${appError}) code`);
t.equal(appError.code, stringify(trimOrEmpty(code)), `toAppError(${stringify(error)}, ${message}, ${code}) -> (${appError}) code must be ${stringify(trimOrEmpty(code))}`);
}

@@ -297,8 +300,8 @@

t.ok(appError instanceof type, `toAppErrorForApiGateway(${stringify(error)}) -> (${appError}) must be instanceof ${type.name}`);
equal(t, appError.name, type.name, `toAppErrorForApiGateway(${stringify(error)}) -> (${appError}) name`);
t.equal(appError.name, type.name, `toAppErrorForApiGateway(${stringify(error)}) -> (${appError}) name must be ${type.name}`);
if (isBlank(message)) {
equal(t, appError.message, trimOrEmpty(error.message), `toAppErrorForApiGateway(${stringify(error)}, ${message}, ${code}) -> (${appError}) message`);
t.equal(appError.message, trimOrEmpty(error.message), `toAppErrorForApiGateway(${stringify(error)}, ${message}, ${code}) -> (${appError}) message must be ${trimOrEmpty(error.message)}`);
} else if (isNotBlank(message)) {
equal(t, appError.message, stringify(trimOrEmpty(message)), `toAppErrorForApiGateway(${stringify(error)}, ${message}, ${code}) -> (${appError}) message`);
t.equal(appError.message, stringify(trimOrEmpty(message)), `toAppErrorForApiGateway(${stringify(error)}, ${message}, ${code}) -> (${appError}) message must be ${stringify(trimOrEmpty(message))}`);
}

@@ -316,5 +319,5 @@

appError0.name;
equal(t, appError.code, trimOrEmpty(expectedCode), `toAppErrorForApiGateway(${stringify(error)}, ${message}, ${code}) -> (${appError}) code`);
t.equal(appError.code, trimOrEmpty(expectedCode), `toAppErrorForApiGateway(${stringify(error)}, ${message}, ${code}) -> (${appError}) code must be ${trimOrEmpty(expectedCode)}`);
} else if (isNotBlank(code)) {
equal(t, appError.code, stringify(trimOrEmpty(code)), `toAppErrorForApiGateway(${stringify(error)}, ${message}, ${code}) -> (${appError}) code`);
t.equal(appError.code, stringify(trimOrEmpty(code)), `toAppErrorForApiGateway(${stringify(error)}, ${message}, ${code}) -> (${appError}) code must be ${stringify(trimOrEmpty(code))}`);
}

@@ -321,0 +324,0 @@

@@ -18,15 +18,10 @@ 'use strict';

const testing = require('./testing');
// const okNotOk = testing.okNotOk;
const checkOkNotOk = testing.checkOkNotOk;
// const checkMethodOkNotOk = testing.checkMethodOkNotOk;
// const equal = testing.equal;
const checkEqual = testing.checkEqual;
// const checkMethodEqual = testing.checkMethodEqual;
function wrap(value) {
switch (typeof value) {
case 'string': return new String(value);
case 'number': return new Number(value);
case 'boolean': return new Boolean(value);
case 'string': //noinspection JSPrimitiveTypeWrapperUsage
return new String(value);
case 'number': //noinspection JSPrimitiveTypeWrapperUsage
return new Number(value);
case 'boolean': //noinspection JSPrimitiveTypeWrapperUsage
return new Boolean(value);
default: return value;

@@ -38,3 +33,3 @@ }

function check(array, expected) {
checkOkNotOk(t, Arrays.isDistinct, [array], expected, 'must be distinct', 'must NOT be distinct');
t.equal(Arrays.isDistinct(array), expected, `Arrays.isDistinct(${stringify(array)}) must ${expected ? '' : 'NOT '}be distinct`);
}

@@ -73,3 +68,3 @@ // empty array

function check(array, expected) {
checkEqual(t, Arrays.distinct, [array], expected);
t.deepEqual(Arrays.distinct(array), expected, `Arrays.distinct(array) must be ${stringify(expected)}`);
}

@@ -111,4 +106,3 @@

function check(array, type, expected) {
checkOkNotOk(t, Arrays.isArrayOfType, [array, type, strict], expected, `must be an array of ${stringify(type)}`,
`must NOT be an array of ${stringify(type)}`);
t.equal(Arrays.isArrayOfType(array, type, strict), expected, `Arrays.isDistinct(${stringify(array)}, ${type}, ${strict}) must ${expected ? '' : 'NOT '}be an array of ${stringify(type)}`);
}

@@ -115,0 +109,0 @@ // empty array

@@ -12,13 +12,6 @@ 'use strict';

const Strings = require('../strings');
// const Strings = require('../strings');
const testing = require('./testing');
// const okNotOk = testing.okNotOk;
const checkOkNotOk = testing.checkOkNotOk;
// const checkMethodOkNotOk = testing.checkMethodOkNotOk;
// const equal = testing.equal;
const checkEqual = testing.checkEqual;
// const checkMethodEqual = testing.checkMethodEqual;
function wrap(value, wrapInBoolean) {
//noinspection JSPrimitiveTypeWrapperUsage
return wrapInBoolean && !(value instanceof Boolean) ? new Boolean(value) : value;

@@ -34,4 +27,3 @@ }

function check(value, expected) {
return checkOkNotOk(t, Booleans.isBoolean, [wrap(value, wrapInBoolean)], expected, ' is a boolean',
' is NOT a boolean', toPrefix(value, wrapInBoolean));
return t.equal(Booleans.isBoolean(wrap(value, wrapInBoolean)), expected, `Booleans.isBoolean(${toPrefix(value, wrapInBoolean)}) is ${expected ? '' : 'NOT '}a boolean`); // :
}

@@ -101,4 +93,3 @@ // undefined

function check(value, expected) {
return checkOkNotOk(t, Booleans.isTrueOrFalse, [wrap(value, wrapInBoolean)], expected, ' is true or false',
' is NOT true or false', toPrefix(value, wrapInBoolean));
return t.equal(Booleans.isTrueOrFalse(wrap(value, wrapInBoolean)), expected, `Booleans.isTrueOrFalse(${toPrefix(value, wrapInBoolean)}) is ${expected ? '' : 'NOT '}true or false`); // :
}

@@ -105,0 +96,0 @@ // undefined

@@ -12,2 +12,3 @@ 'use strict';

const valueOf = Objects.valueOf;
const toKeyValuePairs = Objects.toKeyValuePairs;

@@ -17,13 +18,5 @@ const strings = require('../strings');

const testing = require('./testing');
// const okNotOk = testing.okNotOk;
// const checkOkNotOk = testing.checkOkNotOk;
// const checkMethodOkNotOk = testing.checkMethodOkNotOk;
// const equal = testing.equal;
const checkEqual = testing.checkEqual;
// const checkMethodEqual = testing.checkMethodEqual;
test('valueOf', t => {
function check(value, expected) {
return checkEqual(t, Objects.valueOf, [value], expected, false);
return t.deepEqual(Objects.valueOf(value), expected, `Objects.valueOf(${stringify(value)}) must be ${stringify(expected)}`);
}

@@ -81,3 +74,3 @@

// shallow merge with replace (all same properties)
const merge1 = Objects.merge(from, to1, true);
const merge1 = Objects.merge(from, to1, {replace: true});
t.notDeepEqual(merge1, {a: 2, b: '3', c: {d: 4, e: '5', f: 6}}, 'shallow merge with replace must not be original to');

@@ -92,3 +85,3 @@ t.deepEqual(merge1, from, 'shallow merge with replace must have all of from');

// shallow merge with replace (with to properties not in from)
const merge2 = Objects.merge(from, to2, true);
const merge2 = Objects.merge(from, to2, {replace: true});
t.notDeepEqual(merge2, to2Orig, 'shallow merge with replace must not be original to2');

@@ -105,3 +98,3 @@ t.notDeepEqual(merge2, from, 'shallow merge with replace must not be from');

const to3 = {a: 2, b: '3', c: {d: 4, e: '5', f: 6}, z: 'ZZZ'};
t.deepEqual(Objects.merge(from, to3, true, true), {
t.deepEqual(Objects.merge(from, to3, {replace: true, deep: true}), {
a: 1,

@@ -116,3 +109,3 @@ b: '2',

const to4Orig = {a: 2, b: '2', c: {d: 3, e: '5', f: 6, y: 'Y'}, x: 'X', z: 'ZZZ'};
t.deepEqual(Objects.merge(from, to4, false, true), to4Orig, 'deep merge without replace must have all of to4 and only extras of from');
t.deepEqual(Objects.merge(from, to4, {replace: false, deep: true}), to4Orig, 'deep merge without replace must have all of to4 and only extras of from');

@@ -135,3 +128,3 @@ // check that functions get merged in too

const expected5 = {a: a1, b: b, c: c, z: 'Z1'};
t.deepEqual(Objects.merge(from5, to5, false, false), expected5, 'deep merge without replace must have all functions of to5 and only extra functions of from5');
t.deepEqual(Objects.merge(from5, to5, {replace: false, deep: false}), expected5, 'deep merge without replace must have all functions of to5 and only extra functions of from5');
t.equal(to5.a, a1, 'to5.a must be function a1');

@@ -147,3 +140,3 @@ t.equal(to5.b, b, 'to5.b must be function b');

const expected6 = {a: a2, b: b, c: c, x: x, y: 'y1', z: 'Z2'};
t.deepEqual(Objects.merge(from5, to6, true, false), expected6, 'deep merge with replace must have all functions of from5 and only extra functions of to6');
t.deepEqual(Objects.merge(from5, to6, {replace: true, deep: false}), expected6, 'deep merge with replace must have all functions of from5 and only extra functions of to6');
t.equal(to6.a, a2, 'to6.a must be function a2');

@@ -156,3 +149,3 @@ t.equal(to6.x, x, 'to6.x must be function x');

const expected7 = {a: a2, b: b, c: c, x: x, y: 'y1', z: 'Z2'};
t.deepEqual(Objects.merge(from7, to7, true, true), expected7, 'deep merge with replace must have all functions of from7 and only extra functions of to7');
t.deepEqual(Objects.merge(from7, to7, {replace: true, deep: true}), expected7, 'deep merge with replace must have all functions of from7 and only extra functions of to7');

@@ -165,3 +158,3 @@ function d() {

const expected8 = {o: {a: a1, b: b, x: x, y: 'y1'}, c: c, d: d, z: 'Z1'};
t.deepEqual(Objects.merge(from8, to8, false, true), expected8, 'deep merge without replace must have all functions of to8 and only extra functions of from8');
t.deepEqual(Objects.merge(from8, to8, {replace: false, deep: true}), expected8, 'deep merge without replace must have all functions of to8 and only extra functions of from8');

@@ -171,3 +164,3 @@ const from9 = {o: {a: a2, b: b}, c: c, z: 'Z2'};

const expected9 = {o: {a: a2, b: b, x: x, y: 'y1'}, c: c, d: d, z: 'Z2'};
t.deepEqual(Objects.merge(from9, to9, true, true), expected9, 'deep merge with replace must have all functions of from9 and only extra functions of to9');
t.deepEqual(Objects.merge(from9, to9, {replace: true, deep: true}), expected9, 'deep merge with replace must have all functions of from9 and only extra functions of to9');

@@ -177,3 +170,3 @@ const from10 = {o: {a: a2, b: b}, c: c, z: 'Z2'};

const expected10 = {o: {a: a2, b: b}, c: c, d: d, z: 'Z2'};
t.deepEqual(Objects.merge(from10, to10, true, false), expected10, 'shallow merge with replace must have all of from10 and only extra top-level properties of to10');
t.deepEqual(Objects.merge(from10, to10, {replace: true, deep: false}), expected10, 'shallow merge with replace must have all of from10 and only extra top-level properties of to10');

@@ -183,3 +176,3 @@ const from11 = {o: {a: a2, b: b}, c: c, z: 'Z2'};

const expected11 = {o: {a: a1, x: x, y: 'y1'}, c: c, d: d, z: 'Z1'};
t.deepEqual(Objects.merge(from11, to11, false, false), expected11, 'shallow merge with replace must have all of to11 and only extra top-level properties of from11');
t.deepEqual(Objects.merge(from11, to11, {replace: false, deep: false}), expected11, 'shallow merge with replace must have all of to11 and only extra top-level properties of from11');

@@ -195,3 +188,3 @@ // Create infinite loops (non-DAGs)

Objects.merge(o3, c3, false, true);
Objects.merge(o3, c3, {replace: false, deep: true});
//EEK t.deepEqual fails with "Maximum call stack size exceeded"

@@ -213,33 +206,33 @@ //t.deepEqual(c3, o3, 'deep copy must be deep equal to o3');

// Non-objects
t.deepEqual(Objects.copy(undefined, false), undefined, 'shallow copy of undefined is undefined');
t.deepEqual(Objects.copy(undefined, true), undefined, 'deep copy of undefined is undefined');
t.deepEqual(Objects.copy(undefined, {deep: false}), undefined, 'shallow copy of undefined is undefined');
t.deepEqual(Objects.copy(undefined, {deep: true}), undefined, 'deep copy of undefined is undefined');
t.deepEqual(Objects.copy(null, false), null, 'shallow copy of null is null');
t.deepEqual(Objects.copy(null, true), null, 'deep copy of null is null');
t.deepEqual(Objects.copy(null, {deep: false}), null, 'shallow copy of null is null');
t.deepEqual(Objects.copy(null, {deep: true}), null, 'deep copy of null is null');
t.deepEqual(Objects.copy('', false), '', `shallow copy of '' is ''`);
t.deepEqual(Objects.copy('', true), '', `deep copy of '' is ''`);
t.deepEqual(Objects.copy('', {deep: false}), '', `shallow copy of '' is ''`);
t.deepEqual(Objects.copy('', {deep: true}), '', `deep copy of '' is ''`);
t.deepEqual(Objects.copy('abc', false), 'abc', `shallow copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy('abc', true), 'abc', `deep copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy('abc', {deep: false}), 'abc', `shallow copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy('abc', {deep: true}), 'abc', `deep copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy(123, false), 123, 'shallow copy of 123 is 123');
t.deepEqual(Objects.copy(123, true), 123, 'deep copy of 123 is 123');
t.deepEqual(Objects.copy(123, {deep: false}), 123, 'shallow copy of 123 is 123');
t.deepEqual(Objects.copy(123, {deep: true}), 123, 'deep copy of 123 is 123');
t.deepEqual(Objects.copy(123.456, false), 123.456, 'shallow copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(123.456, true), 123.456, 'deep copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(123.456, {deep: false}), 123.456, 'shallow copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(123.456, {deep: true}), 123.456, 'deep copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(true, false), true, 'shallow copy of true is true');
t.deepEqual(Objects.copy(true, true), true, 'deep copy of true is true');
t.deepEqual(Objects.copy(true, {deep: false}), true, 'shallow copy of true is true');
t.deepEqual(Objects.copy(true, {deep: true}), true, 'deep copy of true is true');
t.deepEqual(Objects.copy(false, false), false, 'shallow copy of false is false');
t.deepEqual(Objects.copy(false, false), false, 'deep copy of false is false');
t.deepEqual(Objects.copy(false, {deep: false}), false, 'shallow copy of false is false');
t.deepEqual(Objects.copy(false, {deep: false}), false, 'deep copy of false is false');
// Empty object
t.deepEqual(Objects.copy({}, false), {}, 'shallow copy of {} is {}');
t.deepEqual(Objects.copy({}, true), {}, 'deep copy of {} is {}');
t.deepEqual(Objects.copy({}, {deep: false}), {}, 'shallow copy of {} is {}');
t.deepEqual(Objects.copy({}, {deep: true}), {}, 'deep copy of {} is {}');
// Empty array
t.deepEqual(Objects.copy([], false), [], 'shallow copy of [] is []');
t.deepEqual(Objects.copy([], true), [], 'deep copy of [] is []');
t.deepEqual(Objects.copy([], {deep: false}), [], 'shallow copy of [] is []');
t.deepEqual(Objects.copy([], {deep: true}), [], 'deep copy of [] is []');

@@ -249,2 +242,39 @@ t.end();

test('copy with non-objects & empty object & empty array', t => {
// Non-objects
t.deepEqual(Objects.copy(undefined, {deep: false}), undefined, 'shallow copy of undefined is undefined');
t.deepEqual(Objects.copy(undefined, {deep: true}), undefined, 'deep copy of undefined is undefined');
t.deepEqual(Objects.copy(null, {deep: false}), null, 'shallow copy of null is null');
t.deepEqual(Objects.copy(null, {deep: true}), null, 'deep copy of null is null');
t.deepEqual(Objects.copy('', {deep: false}), '', `shallow copy of '' is ''`);
t.deepEqual(Objects.copy('', {deep: true}), '', `deep copy of '' is ''`);
t.deepEqual(Objects.copy('abc', {deep: false}), 'abc', `shallow copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy('abc', {deep: true}), 'abc', `deep copy of 'abc' is 'abc'`);
t.deepEqual(Objects.copy(123, {deep: false}), 123, 'shallow copy of 123 is 123');
t.deepEqual(Objects.copy(123, {deep: true}), 123, 'deep copy of 123 is 123');
t.deepEqual(Objects.copy(123.456, {deep: false}), 123.456, 'shallow copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(123.456, {deep: true}), 123.456, 'deep copy of 123.456 is 123.456');
t.deepEqual(Objects.copy(true, {deep: false}), true, 'shallow copy of true is true');
t.deepEqual(Objects.copy(true, {deep: true}), true, 'deep copy of true is true');
t.deepEqual(Objects.copy(false, {deep: false}), false, 'shallow copy of false is false');
t.deepEqual(Objects.copy(false, {deep: false}), false, 'deep copy of false is false');
// Empty object
t.deepEqual(Objects.copy({}, {deep: false}), {}, 'shallow copy of {} is {}');
t.deepEqual(Objects.copy({}, {deep: true}), {}, 'deep copy of {} is {}');
// Empty array
t.deepEqual(Objects.copy([], {deep: false}), [], 'shallow copy of [] is []');
t.deepEqual(Objects.copy([], {deep: true}), [], 'deep copy of [] is []');
t.end();
});
test('copy', t => {

@@ -263,3 +293,3 @@ function a() {

const o1 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'};
const c1 = Objects.copy(o1, false);
const c1 = Objects.copy(o1, {deep: false});
t.deepEqual(c1, o1, 'shallow copy circular - c1 must be deep equal to o1');

@@ -271,3 +301,3 @@ t.notEqual(c1, o1, 'shallow copy circular - c1 must not be o1');

const o2 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'};
const c2 = Objects.copy(o2, true);
const c2 = Objects.copy(o2, {deep: true});
t.deepEqual(c2, o2, 'deep copy circular - c2 must be deep equal to o2');

@@ -282,3 +312,3 @@ t.notEqual(c2, o2, 'deep copy circular - c2 must not be o2');

o3.o.p.o3Again = o3;
const c3 = Objects.copy(o3, true);
const c3 = Objects.copy(o3, {deep: true});
//EEK t.deepEqual fails with "Maximum call stack size exceeded"

@@ -305,3 +335,3 @@ //t.deepEqual(c3, o3, 'deep copy must be deep equal to o3');

const a0 = [];
const c0 = Objects.copy(a0, false);
const c0 = Objects.copy(a0, {deep: false});
t.ok(Array.isArray(c0), `shallow copy ${stringify(c0)} must be an array`);

@@ -313,3 +343,3 @@ t.notEqual(c0, a0, `shallow copy ${stringify(c0)} must not be same instance`);

const a1 = [];
const c1 = Objects.copy(a1, true);
const c1 = Objects.copy(a1, {deep: true});
t.ok(Array.isArray(c1), `deep copy ${stringify(c1)} must be an array`);

@@ -321,3 +351,3 @@ t.notEqual(c1, a1, `deep copy ${stringify(c1)} must NOT be same instance`);

const a2 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]];
const c2 = Objects.copy(a2, false);
const c2 = Objects.copy(a2, {deep: false});

@@ -335,3 +365,3 @@ t.ok(Array.isArray(c2), `shallow copy ${stringify(c2)} must be an array`);

const a3 = [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]];
const c3 = Objects.copy(a3, true);
const c3 = Objects.copy(a3, {deep: true});

@@ -361,3 +391,3 @@ t.ok(Array.isArray(c3), `deep copy ${stringify(c3)} must be an array`);

const a0 = {a: []};
const c0 = Objects.copy(a0, false);
const c0 = Objects.copy(a0, {deep: false});
t.ok(!Array.isArray(c0), `shallow copy ${stringify(c0)} must NOT be an array`);

@@ -369,3 +399,3 @@ t.notEqual(c0, a0, `shallow copy ${stringify(c0)} must not be same instance`);

const a1 = {a: []};
const c1 = Objects.copy(a1, true);
const c1 = Objects.copy(a1, {deep: true});
t.ok(!Array.isArray(c1), `deep copy ${stringify(c1)} must NOT be an array`);

@@ -377,3 +407,3 @@ t.notEqual(c1, a1, `deep copy ${stringify(c1)} must NOT be same instance`);

const a2 = {a: [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]]};
const c2 = Objects.copy(a2, false);
const c2 = Objects.copy(a2, {deep: false});

@@ -390,3 +420,3 @@ t.ok(!Array.isArray(c2), `shallow copy ${stringify(c2)} must NOT be an array`);

const a3 = {a: [1, 2, "3", undefined, null, {a: 1}, [4, 5, "6", null, undefined, {b: 2, c: [7]}]]};
const c3 = Objects.copy(a3, true);
const c3 = Objects.copy(a3, {deep: true});

@@ -415,3 +445,3 @@ t.ok(!Array.isArray(c3), `deep copy ${stringify(c3)} must NOT be an array`);

const b0 = [];
const c0 = Objects.merge(a0, b0, false, false);
const c0 = Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(c0), `shallow merge ${stringify(c0)} must be an array`);

@@ -424,3 +454,3 @@ t.notEqual(c0, a0, `shallow merge ${stringify(c0)} must not be same instance`);

const b1 = [];
const c1 = Objects.merge(a1, b1, false, true);
const c1 = Objects.merge(a1, b1, {replace: false, deep: true});
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);

@@ -437,3 +467,3 @@ t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);

const b0 = [];
const c0 = Objects.merge(a0, b0, false, false);
const c0 = Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(c0), `shallow merge ${stringify(c0)} must be an array`);

@@ -446,3 +476,3 @@ t.notEqual(c0, a0, `shallow merge ${stringify(c0)} must not be same instance`);

const b1 = [];
const c1 = Objects.merge(a1, b1, false, true);
const c1 = Objects.merge(a1, b1, {replace: false, deep: true});
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);

@@ -460,3 +490,3 @@ t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);

const x0 = [1, 2, 3];
Objects.merge(a0, b0, false, false);
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);

@@ -470,3 +500,3 @@ t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);

const x1 = ["1", 2, "3"];
const c1 = Objects.merge(a1, b1, false, true);
const c1 = Objects.merge(a1, b1, {replace: false, deep: true});
t.ok(Array.isArray(c1), `deep merge ${stringify(c1)} must be an array`);

@@ -484,3 +514,3 @@ t.notEqual(c1, a1, `deep merge ${stringify(c1)} must NOT be same instance`);

const x0 = [9, "8", "3", 4.0];
Objects.merge(a0, b0, false, false);
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);

@@ -494,3 +524,3 @@ t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);

const x1 = ["1", 2, "3", 4.0];
Objects.merge(a1, b1, true, false);
Objects.merge(a1, b1, {replace: true, deep: false});
t.ok(Array.isArray(b1), `deep merge ${stringify(b1)} must be an array`);

@@ -504,3 +534,3 @@ t.notEqual(b1, a1, `deep merge ${stringify(b1)} must NOT be same instance`);

const x2 = ["9", 8, 3, 4.0];
Objects.merge(a2, b2, false, true);
Objects.merge(a2, b2, {replace: false, deep: true});
t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);

@@ -514,3 +544,3 @@ t.notEqual(b2, a2, `deep merge ${stringify(b2)} must not be same instance`);

const x4 = ["1", 2, "3", 4.0];
Objects.merge(a4, b4, true, true);
Objects.merge(a4, b4, {replace: true, deep: true});
t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);

@@ -528,3 +558,3 @@ t.notEqual(b4, a4, `deep merge ${stringify(b4)} must NOT be same instance`);

const x0 = ["1", 2, "3", 4.0, [5]];
Objects.merge(a0, b0, false, false);
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);

@@ -538,3 +568,3 @@ t.notEqual(b0, a0, `shallow merge ${stringify(b0)} must not be same instance`);

const x1 = [9, "8", "3", 4.0, [5]];
Objects.merge(a1, b1, true, false);
Objects.merge(a1, b1, {replace: true, deep: false});
t.ok(Array.isArray(b1), `deep merge ${stringify(b1)} must be an array`);

@@ -548,3 +578,3 @@ t.notEqual(b1, a1, `deep merge ${stringify(b1)} must NOT be same instance`);

const x2 = [1, 2, 3, 4.0, [5]];
Objects.merge(a2, b2, false, true);
Objects.merge(a2, b2, {replace: false, deep: true});
t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);

@@ -558,3 +588,3 @@ t.notEqual(b2, a2, `deep merge ${stringify(b2)} must not be same instance`);

const x4 = [9, 8, "3", 4.0, [5]];
Objects.merge(a4, b4, true, true);
Objects.merge(a4, b4, {replace: true, deep: true});
t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);

@@ -574,3 +604,3 @@ t.notEqual(b4, a4, `deep merge ${stringify(b4)} must NOT be same instance`);

Objects.merge(a1, b1, false, false);
Objects.merge(a1, b1, {replace: false, deep: false});

@@ -592,3 +622,3 @@ t.ok(Array.isArray(b1), `shallow merge ${stringify(b1)} must be an array`);

Objects.merge(a2, b2, true, false);
Objects.merge(a2, b2, {replace: true, deep: false});

@@ -621,3 +651,3 @@ t.ok(Array.isArray(b2), `shallow merge ${stringify(b2)} must be an array`);

}]];
Objects.merge(a3, b3, false, true);
Objects.merge(a3, b3, {replace: false, deep: true});

@@ -639,3 +669,3 @@ t.ok(Array.isArray(b3), `deep merge ${stringify(b3)} must be an array`);

}]];
Objects.merge(a4, b4, true, true);
Objects.merge(a4, b4, {replace: true, deep: true});

@@ -660,3 +690,3 @@ t.ok(Array.isArray(b4), `deep merge ${stringify(b4)} must be an array`);

const x0 = {a: [1, 2], b: {c: [6]}};
Objects.merge(a0, b0, false, false);
Objects.merge(a0, b0, {replace: false, deep: false});
t.ok(!Array.isArray(b0), `shallow merge ${stringify(b0)} must NOT be an array`);

@@ -671,3 +701,3 @@ t.ok(Array.isArray(b0.a), `shallow merge ${stringify(b0.a)} must be an array`);

const x1 = {a: [3], b: {c: [4, 5]}};
Objects.merge(a1, b1, true, false);
Objects.merge(a1, b1, {replace: true, deep: false});
t.ok(!Array.isArray(b1), `shallow merge ${stringify(b1)} must NOT be an array`);

@@ -682,3 +712,3 @@ t.ok(Array.isArray(b1.a), `shallow merge ${stringify(b1.a)} must be an array`);

const x2 = {a: [1, 2], b: {c: [6, 5]}};
Objects.merge(a2, b2, false, true);
Objects.merge(a2, b2, {replace: false, deep: true});
t.ok(!Array.isArray(b2), `deep merge ${stringify(b2)} must NOT be an array`);

@@ -693,3 +723,3 @@ t.ok(Array.isArray(b2.a), `deep merge ${stringify(b2.a)} must be an array`);

const x3 = {a: [3, 2], b: {c: [4, 5]}};
Objects.merge(a3, b3, true, true);
Objects.merge(a3, b3, {replace: true, deep: true});
t.ok(!Array.isArray(b3), `deep merge ${stringify(b3)} must NOT be an array`);

@@ -713,3 +743,3 @@ t.ok(Array.isArray(b3.a), `deep merge ${stringify(b3.a)} must be an array`);

Objects.merge(a0, b0, false, false);
Objects.merge(a0, b0, {replace: false, deep: false});

@@ -726,3 +756,3 @@ t.ok(Array.isArray(b0), `shallow merge ${stringify(b0)} must be an array`);

Objects.merge(a1, b1, true, false);
Objects.merge(a1, b1, {replace: true, deep: false});

@@ -739,3 +769,3 @@ t.ok(Array.isArray(b1), `shallow merge ${stringify(b1)} must be an array`);

Objects.merge(a2, b2, false, true);
Objects.merge(a2, b2, {replace: false, deep: true});

@@ -751,3 +781,3 @@ t.ok(Array.isArray(b2), `deep merge ${stringify(b2)} must be an array`);

const x3 = [9,7]; x3.a = [3,2]; x3.b = {c: [4,5]};
Objects.merge(a3, b3, true, true);
Objects.merge(a3, b3, {replace: true, deep: true});
t.ok(Array.isArray(b3), `deep merge ${stringify(b3)} must be an array`);

@@ -771,3 +801,3 @@ t.deepEqual(b3, x3, `deep merge ${stringify(b3)} must be deep equal`);

Objects.merge(a0, b0, false, false);
Objects.merge(a0, b0, {replace: false, deep: false});

@@ -784,3 +814,3 @@ t.ok(!Array.isArray(b0), `shallow merge ${stringify(b0)} must not be an array`);

Objects.merge(a1, b1, true, false);
Objects.merge(a1, b1, {replace: true, deep: false});

@@ -797,3 +827,3 @@ t.ok(!Array.isArray(b1), `shallow merge ${stringify(b1)} must not be an array`);

Objects.merge(a2, b2, false, true);
Objects.merge(a2, b2, {replace: false, deep: true});

@@ -810,3 +840,3 @@ t.ok(!Array.isArray(b2), `deep merge ${stringify(b2)} must not be an array`);

Objects.merge(a3, b3, true, true);
Objects.merge(a3, b3, {replace: true, deep: true});

@@ -841,31 +871,31 @@ t.ok(!Array.isArray(b3), `deep merge ${stringify(b3)} must not be an array`);

const omit = true;
t.deepEqual(Objects.copyNamedProperties(undefined, ['a.b.c'], compact, deep, omit), undefined, `(undefined, ['a.b.c'], compact, deep, omit) must be undefined`);
t.deepEqual(Objects.copyNamedProperties(null, ['a.b.c'], compact, deep, omit), null, `(null, ['a.b.c'], compact, deep, omit) must be null`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], compact, deep, omit), {}, `({}, ['a.b.c'], compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], compact, deep, !omit), {'a.b.c': undefined}, `({}, ['a.b.c'], compact, deep, !omit) must be {'a.b.c': undefined}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], compact, deep, omit), {}, `([] ['a.b.c'], compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], compact, deep, !omit), {'a.b.c': undefined}, `([], ['a.b.c'], compact, deep, !omit) must be {'a.b.c': undefined}`);
t.deepEqual(Objects.copyNamedProperties(undefined, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}), undefined, `(undefined, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}) must be undefined`);
t.deepEqual(Objects.copyNamedProperties(null, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}), null, `(null, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}) must be null`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}), {}, `({}, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'a.b.c': undefined}, `({}, ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'a.b.c': undefined}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}), {}, `([] ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'a.b.c': undefined}, `([], ['a.b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'a.b.c': undefined}`);
const o = {a: 1, b: {c: 'c', d: {e: 'e'}}};
t.deepEqual(Objects.copyNamedProperties(o, ['a'], compact, deep, !omit), {a: 1}, `(o, [a], compact, deep, !omit) must be {a: 1}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b'], compact, deep, !omit), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], compact, deep, !omit) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {a: 1}, `(o, [a], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {a: 1}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], compact, deep, !omit).b, o.b, `(o, [b], compact, deep, !omit).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], !deep, !omit).b, o.b, `(o, [b], !deep, !omit).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {compact: compact, deep: deep, omitIfUndefined: !omit}).b, o.b, `(o, [b], {compact: compact, deep: deep, omitIfUndefined: !omit}).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b, o.b, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b must NOT be o.b`);
t.equal(Objects.copyNamedProperties(o, ['b'], compact, deep, !omit).b.c, o.b.c, `(o, [b], compact, deep, !omit).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], !deep, !omit).b.c, o.b.c, `(o, [b], !deep, !omit).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], {compact: compact, deep: deep, omitIfUndefined: !omit}).b.c, o.b.c, `(o, [b], {compact: compact, deep: deep, omitIfUndefined: !omit}).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b.c, o.b.c, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b.c must equal o.b.c`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], compact, deep, !omit).b.d, o.b.d, `(o, [b], compact, deep, !omit).b.d must NOT be o.b.d`);
t.equal(Objects.copyNamedProperties(o, ['b'], !deep, !omit).b.d, o.b.d, `(o, [b], !deep, !omit).b.d must be o.b.d`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {compact: compact, deep: deep, omitIfUndefined: !omit}).b.d, o.b.d, `(o, [b], {compact: compact, deep: deep, omitIfUndefined: !omit}).b.d must NOT be o.b.d`);
t.equal(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b.d, o.b.d, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b.d must be o.b.d`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.c'], compact, deep, !omit), {'b.c': 'c'}, `(o, [b.c], compact, deep, !omit) must be {'b.c': 'c'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d'], compact, deep, !omit), {'b.d': {e: 'e'}}, `(o, [b.d], compact, deep, !omit) must be {'b.d': {e: 'e'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d.e'], compact, deep, !omit), {'b.d.e': 'e'}, `(o, [b.d.e], compact, deep, !omit) must be {'b.d.e': 'e'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], compact, deep, !omit), {'x.y.z': undefined}, `(o, [x.y.z], compact, deep, !omit) must be {'x.y.z': undefined}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], compact, deep, omit), {}, `(o, [x.y.z], compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.c'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'b.c': 'c'}, `(o, [b.c], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'b.c': 'c'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'b.d': {e: 'e'}}, `(o, [b.d], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'b.d': {e: 'e'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d.e'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'b.d.e': 'e'}, `(o, [b.d.e], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'b.d.e': 'e'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {'x.y.z': undefined}, `(o, [x.y.z], {compact: compact, deep: deep, omitIfUndefined: !omit}) must be {'x.y.z': undefined}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], {compact: compact, deep: deep, omitIfUndefined: omit}), {}, `(o, [x.y.z], {compact: compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b'], compact, deep, !omit), o, `(o, [a,b], compact, deep, !omit) must equal o`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d'], compact, deep, !omit), {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}, `(o, [a,b], compact, deep, !omit) must equal {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], compact, deep, !omit), {a: 1, 'b.c': 'c', 'b.d.e': 'e'}, `(o, [a,b], compact, deep, !omit) must equal {a: 1, 'b.c': 'c', 'b.d.e': 'e'}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b'], {compact: compact, deep: deep, omitIfUndefined: !omit}), o, `(o, [a,b], {compact: compact, deep: deep, omitIfUndefined: !omit}) must equal o`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}, `(o, [a,b], {compact: compact, deep: deep, omitIfUndefined: !omit}) must equal {a: 1, 'b.c': 'c', 'b.d': {e: 'e'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], {compact: compact, deep: deep, omitIfUndefined: !omit}), {a: 1, 'b.c': 'c', 'b.d.e': 'e'}, `(o, [a,b], {compact: compact, deep: deep, omitIfUndefined: !omit}) must equal {a: 1, 'b.c': 'c', 'b.d.e': 'e'}`);

@@ -879,33 +909,62 @@ t.end();

const omit = true;
t.deepEqual(Objects.copyNamedProperties(undefined, ['a.b.c'], !compact, deep, omit), undefined, `(undefined, ['a.b.c'], !compact, deep, omit) must be undefined`);
t.deepEqual(Objects.copyNamedProperties(null, ['a.b.c'], !compact, deep, omit), null, `(null, ['a.b.c'], !compact, deep, omit) must be null`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], !compact, deep, omit), {}, `({}, ['a.b.c'], !compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], !compact, deep, !omit), {a: {b: {c: undefined}}}, `({}, ['a.b.c'], !compact, deep, !omit) must be {a: {b: {c: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], !compact, deep, omit), {}, `([] ['a.b.c'], !compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], !compact, deep, !omit), {a: {b: {c: undefined}}}, `([], ['a.b.c'], !compact, deep, !omit) must be {a: {b: {c: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties(undefined, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}), undefined, `(undefined, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be undefined`);
t.deepEqual(Objects.copyNamedProperties(null, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}), null, `(null, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be null`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}), {}, `({}, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties({}, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: {b: {c: undefined}}}, `({}, ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {a: {b: {c: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}), {}, `([] ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties([], ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: {b: {c: undefined}}}, `([], ['a.b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {a: {b: {c: undefined}}}`);
const o = {a: 1, b: {c: 'c', d: {e: 'e'}}};
t.deepEqual(Objects.copyNamedProperties(o, ['a'], !compact, deep, !omit), {a: 1}, `(o, [a], !compact, deep, !omit) must be {a: 1}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b'], !compact, deep, !omit), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], !compact, deep, !omit) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: 1}, `(o, [a], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {a: 1}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {b: {c: 'c', d: {e: 'e'}}}, `(o, [b], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {b: {c: 'c', d: {e: 'e'}}}`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], !compact, deep, !omit).b, o.b, `(o, [b], !compact, deep, !omit).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], !deep, !omit).b, o.b, `(o, [b], !deep, !omit).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b, o.b, `(o, [b], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b must NOT be o.b`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b, o.b, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b must NOT be o.b`);
t.equal(Objects.copyNamedProperties(o, ['b'], !compact, deep, !omit).b.c, o.b.c, `(o, [b], !compact, deep, !omit).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], !deep, !omit).b.c, o.b.c, `(o, [b], !deep, !omit).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b.c, o.b.c, `(o, [b], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b.c must equal o.b.c`);
t.equal(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b.c, o.b.c, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b.c must equal o.b.c`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], !compact, deep, !omit).b.d, o.b.d, `(o, [b], !compact, deep, !omit).b.d must NOT be o.b.d`);
t.equal(Objects.copyNamedProperties(o, ['b'], !deep, !omit).b.d, o.b.d, `(o, [b], !deep, !omit).b.d must be o.b.d`);
t.notEqual(Objects.copyNamedProperties(o, ['b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b.d, o.b.d, `(o, [b], {compact: !compact, deep: deep, omitIfUndefined: !omit}).b.d must NOT be o.b.d`);
t.equal(Objects.copyNamedProperties(o, ['b'], {deep: !deep, omitIfUndefined: !omit}).b.d, o.b.d, `(o, [b], {deep: !deep, omitIfUndefined: !omit}).b.d must be o.b.d`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.c'], !compact, deep, !omit), {b: {c: 'c'}}, `(o, [b.c], !compact, deep, !omit) must be {b: {c: 'c'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d'], !compact, deep, !omit), {b: {d: {e: 'e'}}}, `(o, [b.d], !compact, deep, !omit) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d.e'], !compact, deep, !omit), {b: {d: {e: 'e'}}}, `(o, [b.d.e], !compact, deep, !omit) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], !compact, deep, !omit), {x: {y: {z: undefined}}}, `(o, [x.y.z], !compact, deep, !omit) must be {x: {y: {z: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], !compact, deep, omit), {}, `(o, [x.y.z], !compact, deep, omit) must be {}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.c'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {b: {c: 'c'}}, `(o, [b.c], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {b: {c: 'c'}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {b: {d: {e: 'e'}}}, `(o, [b.d], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['b.d.e'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {b: {d: {e: 'e'}}}, `(o, [b.d.e], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {b: {d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {x: {y: {z: undefined}}}, `(o, [x.y.z], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must be {x: {y: {z: undefined}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['x.y.z'], {compact: !compact, deep: deep, omitIfUndefined: omit}), {}, `(o, [x.y.z], {compact: !compact, deep: deep, omitIfUndefined: omit}) must be {}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b'], !compact, deep, !omit), o, `(o, [a,b], !compact, deep, !omit) must equal o`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d'], !compact, deep, !omit), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], !compact, deep, !omit) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], !compact, deep, !omit), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], !compact, deep, !omit) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), o, `(o, [a,b], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must equal o`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.deepEqual(Objects.copyNamedProperties(o, ['a', 'b.c', 'b.d.e'], {compact: !compact, deep: deep, omitIfUndefined: !omit}), {a: 1, b: {c: 'c', d: {e: 'e'}}}, `(o, [a,b], {compact: !compact, deep: deep, omitIfUndefined: !omit}) must equal {a: 1, b: {c: 'c', d: {e: 'e'}}}`);
t.end();
});
test('toKeyValuePairs', t => {
// Applied to non-objects
let expected = [];
t.deepEqual(toKeyValuePairs(undefined), expected, `toKeyValuePairs(undefined) must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs(null), expected, `toKeyValuePairs(null) must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs('abc'), expected, `toKeyValuePairs('abc') must be ${stringify(expected)}`);
t.deepEqual(toKeyValuePairs(123), expected, `toKeyValuePairs(123) must be ${stringify(expected)}`);
// Applied to objects
expected = [];
t.deepEqual(toKeyValuePairs({}), expected, `toKeyValuePairs({}) must be ${stringify(expected)}`);
expected = [['a', 1], ['b', {c:2}], ['d', '3']];
t.deepEqual(toKeyValuePairs({a:1, b:{c:2}, d:'3'}), expected, `toKeyValuePairs({a:1, b:{c:2}, d:'3'}) must be ${stringify(expected)}`);
expected = [['d', '3'], ['b', {c:2}], ['a', 1]];
t.deepEqual(toKeyValuePairs({d:'3', b:{c:2}, a:1}), expected, `toKeyValuePairs({d:'3', b:{c:2}, a:1}) must be ${stringify(expected)}`);
// Not meant to be applied to arrays, but if so ...
expected = [['length', 0]];
t.deepEqual(toKeyValuePairs([]), expected, `toKeyValuePairs([]) must be ${stringify(expected)}`);
expected = [['0', '1'], ['1', 2], ['2', '3'], ['length', 3]];
t.deepEqual(toKeyValuePairs(['1', 2, '3']), expected, `toKeyValuePairs(['1', 2, '3']) must be ${stringify(expected)}`);
expected = [['0', '3'], ['1', 2], ['2', '1'], ['length', 3]];
t.deepEqual(toKeyValuePairs(['3',2,'1']), expected, `toKeyValuePairs(['3',2,'1']) must be ${stringify(expected)}`);
t.end();
});
{
"name": "core-functions-tests",
"version": "2.0.14",
"version": "3.0.0",
"author": "Byron du Preez",
"license": "Apache-2.0",
"private": true,
"engines": {

@@ -7,0 +8,0 @@ "node": ">=4.3.2"

@@ -10,16 +10,13 @@ 'use strict';

const tries = require('../tries');
// const Try = tries.Try;
const Success = tries.Success;
const Failure = tries.Failure;
const Promises = require('../promises');
const CancelledError = Promises.CancelledError;
const Strings = require('../strings');
const stringify = Strings.stringify;
//const testing = require('./testing');
// const okNotOk = testing.okNotOk;
// const checkOkNotOk = testing.checkOkNotOk;
// const checkMethodOkNotOk = testing.checkMethodOkNotOk;
// const equal = testing.equal;
// const checkEqual = testing.checkEqual;
// const checkMethodEqual = testing.checkMethodEqual;
const error = new Error('Fail');

@@ -67,13 +64,79 @@

// Arbitrary Promises
const p1 = Promise.resolve('p1');
const p2Error = new Error('p2 error');
const p2 = Promise.reject(p2Error);
const p3 = Promise.resolve('p3');
const p4Error = new Error('p4 error');
const p4 = Promise.reject(p4Error);
const t1 = genThenable(null, 't1', false, 1);
const t2Error = new Error('t2 error');
const t2 = genThenable(t2Error, null, false, 1);
const t3Error = new Error('t3 error');
const t3 = genThenable(t3Error, null, true, 1);
const d2Error = new Error('d2 error');
const d4Error = new Error('d4 error');
function genDelayedPromise(err, name, ms, delayCancellable, cancellable) {
const startTime = Date.now();
return Promises.delay(ms, delayCancellable).then(() => {
if (cancellable) {
if (cancellable.cancel) {
const completed = cancellable.cancel();
console.log(`Delayed promise ${name} ${completed ? '"cancelled" completed' : 'cancelled incomplete'} cancellable`);
} else {
console.log(`Delayed promise ${name} could NOT cancel given cancellable, since no cancel was installed yet!`);
}
}
const msElapsed = Date.now() - startTime;
if (msElapsed >= ms) {
console.log(`Delayed promise ${name} completed at ${msElapsed} ms (original delay was ${ms} ms)`);
} else {
console.log(`Delayed promise ${name} ended prematurely at ${msElapsed} ms out of ${ms} ms delay`);
}
if (err) throw err;
return name;
});
}
function genThenable(err, data, failSync, ms) {
return {
then: (res, rej) => {
setTimeout(() => {
if (err) {
if (!failSync) {
console.log(`"then-able".then rejecting with error ${stringify(err)}`);
rej(err);
}
} else {
console.log(`"then-able".then resolving with result ${stringify(data)}`);
res(data);
}
}, ms);
if (err && failSync) {
console.log(`"then-able".then throwing error ${stringify(err)}`);
throw err;
}
}
};
}
// ---------------------------------------------------------------------------------------------------------------------
// Promise.isPromise
// Promises.isPromise
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.isPromise', t => {
t.notOk(Promise.isPromise(undefined), 'undefined is not a promise');
t.notOk(Promise.isPromise(null), 'null is not a promise');
t.notOk(Promise.isPromise(new Error("Err")), 'An error is not a promise');
t.notOk(Promise.isPromise({}), 'An empty object is not a promise');
t.ok(Promise.isPromise(new Promise((resolve, reject) => resolve)), 'A Promise is a promise');
t.ok(Promise.isPromise({then: () => console.log('Then-able')}), 'A then-able (i.e. an object with a then method) "is" a promise');
test('Promises.isPromise', t => {
t.notOk(Promises.isPromise(undefined), 'undefined is not a promise');
t.notOk(Promises.isPromise(null), 'null is not a promise');
t.notOk(Promises.isPromise(new Error("Err")), 'An error is not a promise');
t.notOk(Promises.isPromise({}), 'An empty object is not a promise');
t.ok(Promises.isPromise(new Promise((resolve, reject) => resolve)), 'A Promise is a promise');
t.notOk(Promises.isPromise({then: () => console.log('Then-able')}), 'A then-able (i.e. an object with a then method) is NOT a promise');

@@ -84,14 +147,29 @@ t.end();

// ---------------------------------------------------------------------------------------------------------------------
// Promise.wrap
// Promises.isPromiseLike
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.wrap with node-style function that calls back with an error', t => {
const promiseReturningFn = Promise.wrap(nodeStyleFn);
test('Promises.isPromiseLike', t => {
t.notOk(Promises.isPromiseLike(undefined), 'undefined is not a promise-like');
t.notOk(Promises.isPromiseLike(null), 'null is not a promise-like');
t.notOk(Promises.isPromiseLike(new Error("Err")), 'An error is not a promise-like');
t.notOk(Promises.isPromiseLike({}), 'An empty object is not a promise-like');
t.ok(Promises.isPromiseLike(new Promise((resolve, reject) => resolve)), 'A Promise is a promise-like');
t.ok(Promises.isPromiseLike({then: () => console.log('Then-able')}), 'A then-able (i.e. an object with a then method) "is" a promise-like');
t.end();
});
// ---------------------------------------------------------------------------------------------------------------------
// Promises.wrap
// ---------------------------------------------------------------------------------------------------------------------
test('Promises.wrap with node-style function that calls back with an error', t => {
const promiseReturningFn = Promises.wrap(nodeStyleFn);
promiseReturningFn(true)
.then(result => {
t.fail(`Promise.wrap(nodeStyleFn)(true).then should NOT have got result (${result})`);
t.fail(`Promises.wrap(nodeStyleFn)(true).then should NOT have got result (${result})`);
t.end(err);
})
.catch(err => {
t.pass(`Promise.wrap(nodeStyleFn)(true).catch should have got error (${err})`);
t.pass(`Promises.wrap(nodeStyleFn)(true).catch should have got error (${err})`);
t.end();

@@ -101,11 +179,11 @@ });

test('Promise.wrap with node-style function that calls back with a successful result', t => {
const promiseReturningFn = Promise.wrap(nodeStyleFn);
test('Promises.wrap with node-style function that calls back with a successful result', t => {
const promiseReturningFn = Promises.wrap(nodeStyleFn);
promiseReturningFn(false)
.then(result => {
t.pass(`Promise.wrap(nodeStyleFn)(false).then should have got result (${result})`);
t.pass(`Promises.wrap(nodeStyleFn)(false).then should have got result (${result})`);
t.end();
})
.catch(err => {
t.fail(`Promise.wrap(nodeStyleFn)(false).catch should NOT have got error (${err})`);
t.fail(`Promises.wrap(nodeStyleFn)(false).catch should NOT have got error (${err})`);
t.end(err);

@@ -116,14 +194,14 @@ });

// ---------------------------------------------------------------------------------------------------------------------
// Promise.wrapMethod
// Promises.wrapMethod
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.wrapMethod with node-style method that calls back with an error', t => {
const promiseReturningMethod = Promise.wrapMethod(objWithNodeStyleMethod, objWithNodeStyleMethod.nodeStyleMethod);
test('Promises.wrapMethod with node-style method that calls back with an error', t => {
const promiseReturningMethod = Promises.wrapMethod(objWithNodeStyleMethod, objWithNodeStyleMethod.nodeStyleMethod);
promiseReturningMethod(true)
.then(result => {
t.fail(`Promise.wrapMethod(...)(true).then should NOT have got result (${result})`);
t.fail(`Promises.wrapMethod(...)(true).then should NOT have got result (${result})`);
t.end(err);
})
.catch(err => {
t.pass(`Promise.wrapMethod(...)(true).catch should have got error (${err})`);
t.pass(`Promises.wrapMethod(...)(true).catch should have got error (${err})`);
t.end();

@@ -133,11 +211,11 @@ });

test('Promise.wrapMethod with node-style method that calls back with a successful result', t => {
const promiseReturningMethod = Promise.wrapMethod(objWithNodeStyleMethod, objWithNodeStyleMethod.nodeStyleMethod);
test('Promises.wrapMethod with node-style method that calls back with a successful result', t => {
const promiseReturningMethod = Promises.wrapMethod(objWithNodeStyleMethod, objWithNodeStyleMethod.nodeStyleMethod);
promiseReturningMethod(false)
.then(result => {
t.pass(`Promise.wrapMethod(...)(false).then should have got result (${result})`);
t.pass(`Promises.wrapMethod(...)(false).then should have got result (${result})`);
t.end();
})
.catch(err => {
t.fail(`Promise.wrapMethod(...)(false).catch should NOT have got error (${err})`);
t.fail(`Promises.wrapMethod(...)(false).catch should NOT have got error (${err})`);
t.end(err);

@@ -148,6 +226,6 @@ });

// ---------------------------------------------------------------------------------------------------------------------
// Using standard Promise.resolve with a synchronous function that throws an error (reason for Promise.try)
// Using standard Promise.resolve with a synchronous function that throws an error (reason for Promises.try)
// ---------------------------------------------------------------------------------------------------------------------
test('Standard Promise.resolve with a synchronous function that throws an error (reason for Promise.try)', t => {
test('Standard Promise.resolve with a synchronous function that throws an error (reason for Promises.try)', t => {
const mustFail = true;

@@ -193,6 +271,6 @@ const prefix = `Promise.resolve(fallible(${mustFail}))`;

// ---------------------------------------------------------------------------------------------------------------------
// Using standard Promise.reject with a synchronous function that throws an error (another reason for Promise.try)
// Using standard Promise.reject with a synchronous function that throws an error (another reason for Promises.try)
// ---------------------------------------------------------------------------------------------------------------------
test('Standard Promise.reject with a synchronous function that throws an error (another reason for Promise.try)', t => {
test('Standard Promise.reject with a synchronous function that throws an error (another reason for Promises.try)', t => {
const mustFail = true;

@@ -300,3 +378,3 @@ const prefix = `Promise.reject(fallible(${mustFail}))`;

// Unexpected behaviour, Promise.reject does NOT unravel a promise value
t.ok(Promise.isPromise(err), `${prefix}.catch error is a promise!`);
t.ok(Promises.isPromise(err), `${prefix}.catch error is a promise!`);
err

@@ -330,3 +408,3 @@ .then(result2 => {

// Unexpected behaviour, Promise.reject does NOT unravel a promise value
t.ok(Promise.isPromise(err), `${prefix}.catch error is a promise!`);
t.ok(Promises.isPromise(err), `${prefix}.catch error is a promise!`);
err

@@ -349,10 +427,10 @@ .then(result2 => {

// ---------------------------------------------------------------------------------------------------------------------
// Promise.try with a synchronous function
// Promises.try with a synchronous function
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.try with a synchronous function that throws exception', t => {
test('Promises.try with a synchronous function that throws exception', t => {
const mustFail = true;
const prefix = `Promise.try(() => fallible(${mustFail}))`;
const prefix = `Promises.try(() => fallible(${mustFail}))`;
try {
Promise.try(() => fallible(mustFail))
Promises.try(() => fallible(mustFail))
.then(result => {

@@ -373,7 +451,7 @@ t.fail(`${prefix}.then should NOT have got result (${result})`);

test('Promise.try with a synchronous function that does not throw exception', t => {
test('Promises.try with a synchronous function that does not throw exception', t => {
const mustFail = false;
const prefix = `Promise.try(() => fallible(${mustFail}))`;
const prefix = `Promises.try(() => fallible(${mustFail}))`;
try {
Promise.try(() => fallible(mustFail))
Promises.try(() => fallible(mustFail))
.then(result => {

@@ -395,10 +473,10 @@ t.pass(`${prefix}.then should have got result (${result})`);

// ---------------------------------------------------------------------------------------------------------------------
// Promise.try with an asynchronous function that returns a promise
// Promises.try with an asynchronous function that returns a promise
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.try with an asynchronous function that returns a rejected promise', t => {
test('Promises.try with an asynchronous function that returns a rejected promise', t => {
const mustFail = true;
const prefix = `Promise.try(() => fallibleAsync(${mustFail}))`;
const prefix = `Promises.try(() => fallibleAsync(${mustFail}))`;
try {
Promise.try(() => fallibleAsync(mustFail))
Promises.try(() => fallibleAsync(mustFail))
.then(result => {

@@ -419,7 +497,7 @@ t.fail(`${prefix}.then should NOT have got result (${result})`);

test('Promise.try with an asynchronous function that returns a resolved promise', t => {
test('Promises.try with an asynchronous function that returns a resolved promise', t => {
const mustFail = false;
const prefix = `Promise.try(() => fallibleAsync(${mustFail}))`;
const prefix = `Promises.try(() => fallibleAsync(${mustFail}))`;
try {
Promise.try(() => fallibleAsync(mustFail))
Promises.try(() => fallibleAsync(mustFail))
.then(result => {

@@ -441,15 +519,15 @@ t.pass(`${prefix}.then should have got result (${result})`);

// ---------------------------------------------------------------------------------------------------------------------
// Promise.delay
// Promises.delay
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.delay with no cancellable', t => {
Promise.delay(10)
test('Promises.delay with no cancellable', t => {
Promises.delay(10)
.then(
triggered => {
t.pass('Promise.delay should have resolved and its timeout should have triggered');
t.equal(triggered, true, 'Promise.delay should have triggered');
t.pass('Promises.delay should have resolved and its timeout should have triggered');
t.equal(triggered, true, 'Promises.delay should have triggered');
t.end();
},
failed => {
t.fail('Promise.delay should have resolved and its timeout should have triggered');
() => {
t.fail('Promises.delay should have resolved and its timeout should have triggered');
t.end();

@@ -460,13 +538,13 @@ }

test('Promise.delay without cancellation of delay', t => {
test('Promises.delay without cancellation of delay', t => {
const cancellable = {};
Promise.delay(10, cancellable)
Promises.delay(10, cancellable)
.then(
triggered => {
t.pass('Promise.delay should have resolved and its timeout should have triggered');
t.equal(triggered, true, 'Promise.delay should have triggered');
t.pass('Promises.delay should have resolved and its timeout should have triggered');
t.equal(triggered, true, 'Promises.delay should have triggered');
t.end();
},
failed => {
t.fail('Promise.delay should have resolved and its timeout should have triggered');
() => {
t.fail('Promises.delay should have resolved and its timeout should have triggered');
t.end();

@@ -477,13 +555,13 @@ }

test('Promise.delay with cancellation of delay (mustResolve undefined)', t => {
test('Promises.delay with cancellation of delay (mustResolve undefined)', t => {
const cancellable = {};
Promise.delay(50, cancellable)
Promises.delay(50, cancellable)
.then(
failed => {
t.fail('Promise.delay should have rejected and its timeout should have cancelled');
() => {
t.fail('Promises.delay should have rejected and its timeout should have cancelled');
t.end();
},
triggered => {
t.pass('Promise.delay should have rejected and its timeout should been cancelled');
t.equal(triggered, false, 'Promise.delay should NOT have triggered yet');
t.pass('Promises.delay should have rejected and its timeout should been cancelled');
t.equal(triggered, false, 'Promises.delay should NOT have triggered yet');
t.end();

@@ -496,13 +574,13 @@ }

test('Promise.delay with cancellation of delay (mustResolve explicit false)', t => {
test('Promises.delay with cancellation of delay (mustResolve explicit false)', t => {
const cancellable = {};
Promise.delay(50, cancellable)
Promises.delay(50, cancellable)
.then(
failed => {
t.fail('Promise.delay should have rejected and its timeout should have cancelled');
() => {
t.fail('Promises.delay should have rejected and its timeout should have cancelled');
t.end();
},
triggered => {
t.pass('Promise.delay should have rejected and its timeout should have cancelled');
t.equal(triggered, false, 'Promise.delay should NOT have triggered yet');
t.pass('Promises.delay should have rejected and its timeout should have cancelled');
t.equal(triggered, false, 'Promises.delay should NOT have triggered yet');
t.end();

@@ -516,13 +594,13 @@ }

test('Promise.delay with cancellation of delay (mustResolve true)', t => {
test('Promises.delay with cancellation of delay (mustResolve true)', t => {
const cancellable = {};
Promise.delay(50, cancellable)
Promises.delay(50, cancellable)
.then(
triggered => {
t.pass('Promise.delay should have resolved, but its timeout should NOT have triggered');
t.equal(triggered, false, 'Promise.delay should NOT have triggered yet');
t.pass('Promises.delay should have resolved, but its timeout should NOT have triggered');
t.equal(triggered, false, 'Promises.delay should NOT have triggered yet');
t.end();
},
failed => {
t.fail('Promise.delay should NOT have rejected');
() => {
t.fail('Promises.delay should NOT have rejected');
t.end();

@@ -537,50 +615,28 @@ }

// ---------------------------------------------------------------------------------------------------------------------
// Promise.isArrayOfPromises
// Promises.allOrOne
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.isArrayOfPromises', t => {
t.notOk(Promise.isArrayOfPromises(undefined), 'undefined is not a Promise[]');
t.notOk(Promise.isArrayOfPromises(null), 'null is not a Promise[]');
t.notOk(Promise.isArrayOfPromises([null]), '[null] is not a Promise[]');
t.notOk(Promise.isArrayOfPromises(new Error("Err")), 'An error is not a Promise[]');
t.notOk(Promise.isArrayOfPromises({}), 'An empty object is not a Promise[]');
t.notOk(Promise.isArrayOfPromises(new Promise((resolve, reject) => resolve)), 'A Promise is not a Promise[]');
t.notOk(Promise.isArrayOfPromises({then: () => console.log('Then-able')}), 'A then-able is not a Promise[]');
test('Promises.allOrOne', t => {
t.ok(Promises.isPromise(Promises.allOrOne(new Error("Err"))), 'allOrOne(error) gives a promise');
t.ok(Promises.isPromise(Promises.allOrOne(undefined)), 'allOrOne(undefined) gives a promise');
t.ok(Promises.isPromise(Promises.allOrOne(null)), 'allOrOne(null) gives a promise');
t.ok(Promise.isArrayOfPromises([]), 'An empty array is a Promise[]');
t.ok(Promise.isArrayOfPromises([new Promise((resolve, reject) => resolve)]), 'A array with a Promise is a Promise[]');
t.ok(Promise.isArrayOfPromises([new Promise((resolve, reject) => resolve), new Promise((resolve, reject) => resolve)]), 'A array with 2 Promises is a Promise[]');
t.ok(Promise.isArrayOfPromises([{then: () => console.log('[Then-able]')}]), 'An array with a then-able "is" a Promise[]');
t.ok(Promise.isArrayOfPromises([{then: () => console.log('[Then-able]')}, {then: () => console.log('[Then-able2]')}]), 'An array with 2 then-ables "is" a Promise[]');
t.ok(Promise.isArrayOfPromises([{then: () => console.log('[Then-able]')}, new Promise((resolve, reject) => resolve)]), 'An array with a then-able & a promise "is" a Promise[]');
t.end();
});
// ---------------------------------------------------------------------------------------------------------------------
// Promise.allOrOne
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.allOrOne', t => {
t.ok(Promise.isPromise(Promise.allOrOne(new Error("Err"))), 'allOrOne(error) gives a promise');
t.ok(Promise.isPromise(Promise.allOrOne(undefined)), 'allOrOne(undefined) gives a promise');
t.ok(Promise.isPromise(Promise.allOrOne(null)), 'allOrOne(null) gives a promise');
const thenable = {then: () => 'Then-able'};
t.ok(Promise.isPromise(Promise.allOrOne(thenable)), 'allOrOne(then-able) is a promise');
t.equal(Promise.allOrOne(thenable), thenable, 'allOrOne(then-able) gives same then-able');
t.ok(Promise.isPromise(Promise.allOrOne([thenable])), 'allOrOne([then-able]) is a promise');
t.ok(Promise.isPromise(Promise.allOrOne([thenable, thenable])), 'allOrOne([then-able,then-able]) is a promise');
t.ok(Promises.isPromise(Promises.allOrOne(thenable)), 'allOrOne(then-able) gives a promise');
//t.equal(Promises.allOrOne(thenable), thenable, 'allOrOne(then-able) gives same then-able');
t.notEqual(Promises.allOrOne(thenable), thenable, 'allOrOne(then-able) is NOT same then-able');
t.ok(Promises.isPromise(Promises.allOrOne([thenable])), 'allOrOne([then-able]) is a promise');
t.ok(Promises.isPromise(Promises.allOrOne([thenable, thenable])), 'allOrOne([then-able,then-able]) is a promise');
const promise = new Promise((resolve, reject) => resolve('Bob'));
t.ok(Promise.isPromise(Promise.allOrOne(promise)), 'allOrOne(promise) gives a promise');
t.equal(Promise.allOrOne(promise), promise, 'allOrOne(promise) gives same promise');
t.ok(Promises.isPromise(Promises.allOrOne(promise)), 'allOrOne(promise) gives a promise');
t.equal(Promises.allOrOne(promise), promise, 'allOrOne(promise) gives same promise');
t.ok(Promise.isPromise(Promise.allOrOne([promise])), 'allOrOne([promise]) gives a promise');
t.notEqual(Promise.allOrOne([promise]), promise, 'allOrOne([promise]) does not give same promise');
t.ok(Promises.isPromise(Promises.allOrOne([promise])), 'allOrOne([promise]) gives a promise');
t.notEqual(Promises.allOrOne([promise]), promise, 'allOrOne([promise]) does not give same promise');
t.ok(Promise.isPromise(Promise.allOrOne([promise, promise])), 'allOrOne([promise, promise]) gives a promise');
t.ok(Promises.isPromise(Promises.allOrOne([promise, promise])), 'allOrOne([promise, promise]) gives a promise');
const promiseArray = [promise];
Promise.allOrOne(promiseArray)
Promises.allOrOne(promiseArray)
.then(ps => {

@@ -596,82 +652,751 @@ promiseArray[0].then(p => {

// ---------------------------------------------------------------------------------------------------------------------
// Promise.every
// Promises.every
// ---------------------------------------------------------------------------------------------------------------------
test('Promise.every', t => {
t.plan(11);
test("Promises.every()", t => {
t.throws(() => Promises.every(), Error, `Promises.every() must throw a 'not an array' Error`);
t.throws(() => Promises.every(undefined), Error, `Promises.every(undefined) must throw a 'not an array' Error`);
t.throws(() => Promises.every(null), Error, `Promises.every(null) must throw a 'not an array' Error`);
t.throws(() => Promises.every(123), Error, `Promises.every(123) must throw a 'not an array' Error`);
t.throws(() => Promises.every(Promise.resolve(1)), Error, `Promises.every(Promise.resolve(1)) must throw a 'not an array' Error`);
t.end();
});
// Empty array or undefined
Promise.every([])
test('Promises.every([])', t => {
Promises.every([]).then(results => {
const expected = [];
t.deepEqual(results, expected, `Promises.every([]) must be ${stringify(expected)}`);
t.end();
});
});
test('Promises.every([undefined])', t => {
Promises.every([undefined]).then(results => {
const expected = [new Success(undefined)];
t.deepEqual(results, expected, `Promises.every([undefined]) must be ${stringify(expected)}`);
t.end();
});
});
test('Promises.every([null])', t => {
Promises.every([null]).then(results => {
const expected = [new Success(null)];
t.deepEqual(results, expected, `Promises.every([null]) must be ${stringify(expected)}`);
t.end();
});
});
test('Promises.every([Promise.resolve(null), undefined])', t => {
Promises.every([Promise.resolve(null), undefined]).then(results => {
const expected = [new Success(null), new Success(undefined)];
t.deepEqual(results, expected, `Promises.every([Promise.resolve(null), undefined]) must be ${stringify(expected)}`);
t.end();
});
});
test("Promises.every([undefined, Promise.resolve(null)])", t => {
Promises.every([undefined, Promise.resolve(null)]).then(results => {
const expected = [new Success(undefined), new Success(null)];
t.deepEqual(results, expected, `Promises.every([undefined, Promise.resolve(null)]) must be ${stringify(expected)}`);
t.end();
});
});
test("Promises.every([Promise.resolve(null), null])", t => {
Promises.every([Promise.resolve(null), null]).then(results => {
const expected = [new Success(null), new Success(null)];
t.deepEqual(results, expected, `Promises.every([Promise.resolve(null), null]) must be ${stringify(expected)}`);
t.end();
});
});
test("Promises.every([undefined, null, Promise.resolve(null), 123, 'ABCDEF'])", t => {
Promises.every([undefined, null, Promise.resolve(null), 123, 'ABCDEF']).then(results => {
const expected = [new Success(undefined), new Success(null), new Success(null), new Success(123), new Success('ABCDEF')];
t.deepEqual(results, expected, `Promises.every([undefined, null, Promise.resolve(null), 123]) must be ${stringify(expected)}`);
t.end();
});
});
test("Promises.every([p1])", t => {
Promises.every([p1]).then(results => {
const expected = [new Success('p1')];
t.deepEqual(results, expected, `Promises.every([p1]) must be ${stringify(expected)}`);
t.end();
});
});
test("Promises.every([p1, p2])", t => {
Promises.every([p1, p2])
.then(results => {
t.deepEqual(results, [], 'Promise.every([]) must be []');
const expected = [new Success('p1'), new Failure(p2Error)];
t.deepEqual(results, expected, `Promises.every([p1,p2]) must be ${stringify(expected)}`);
t.end();
});
Promise.every(undefined)
});
test("Promises.every([p1, p2, p3])", t => {
Promises.every([p1, p2, p3])
.then(results => {
t.deepEqual(results, [], 'Promise.every(undefined) must be []');
const expected = [new Success('p1'), new Failure(p2Error), new Success('p3')];
t.deepEqual(results, expected, `Promises.every([p1,p2,p3]) must be ${stringify(expected)}`);
t.end();
});
Promise.every(null)
});
test("Promises.every([p1, p2, p3, p4])", t => {
Promises.every([p1, p2, p3, p4])
.then(results => {
t.deepEqual(results, [], 'Promise.every(null) must be []');
const expected = [new Success('p1'), new Failure(p2Error), new Success('p3'), new Failure(p4Error),];
t.deepEqual(results, expected, `Promises.every([p1,p2,p3,p4]) must be ${stringify(expected)}`);
t.end();
});
});
// Single Promise
const p1 = Promise.resolve('p1');
const expected = [{result: 'p1'}];
Promise.every(p1)
test("Promises.every with [Infinity, p4, 4.5, p3, '3.5', p2, null, p1, undefined, p2, {a:1}]", t => {
// Reversed with duplicate and with simple values
Promises.every([Infinity, p4, 4.5, p3, '3.5', p2, null, p1, undefined, p2, {a: 1}])
.then(results => {
t.deepEqual(results, expected, `Promise.every(p1) must be ${stringify(expected)}`);
const expected = [new Success(Infinity), new Failure(p4Error), new Success(4.5), new Success('p3'), new Success('3.5'), new Failure(p2Error),
new Success(null), new Success('p1'), new Success(undefined), new Failure(p2Error), new Success({a: 1})];
t.deepEqual(results, expected, `Promises.every([Infinity, p4, 4.5, p3, '3.5', p2, null, p1, undefined, p2, {a:1}]) must be ${stringify(expected)}`);
t.end();
});
});
Promise.every([p1])
.then(results => {
t.deepEqual(results, expected, `Promise.every([p1]) must be ${stringify(expected)}`);
test('Promises.every([1, 2, 3])', t => {
Promises.every([1, 2, 3]).then(results => {
const expected = [new Success(1), new Success(2), new Success(3)];
t.deepEqual(results, expected, `Promises.every([1, 2, 3]) must be ${stringify(expected)}`);
t.end();
});
});
test("Promises.every([1, '2', 3])", t => {
Promises.every([1, '2', 3]).then(results => {
const expected = [new Success(1), new Success("2"), new Success(3)];
t.deepEqual(results, expected, `Promises.every([1, '2', 3]) must be ${stringify(expected)}`);
t.end();
});
});
// =====================================================================================================================
// Promises.every with cancellations
// =====================================================================================================================
test("Promises.every([d1,d2,d3,d4]) cancelled immediately (i.e. before d1, d2, d3 & d4 resolve) will resolve only d1", t => {
const cancellable = {};
const d1Cancellable = {};
const d1 = genDelayedPromise(null, 'd1', 10, d1Cancellable);
t.ok(typeof d1Cancellable.cancelTimeout === "function", `d1Cancellable.cancelTimeout must be installed`);
const d2Cancellable = {};
const d2 = genDelayedPromise(d2Error, 'd2', 2000, d2Cancellable);
t.ok(typeof d2Cancellable.cancelTimeout === "function", `d2Cancellable.cancelTimeout must be installed`);
const d3Cancellable = {};
const d3 = genDelayedPromise(null, 'd3', 3000, d3Cancellable);
t.ok(typeof d3Cancellable.cancelTimeout === "function", `d3Cancellable.cancelTimeout must be installed`);
const d4Cancellable = {};
const d4 = genDelayedPromise(d4Error, 'd4', 4000, d4Cancellable);
t.ok(typeof d4Cancellable.cancelTimeout === "function", `d4Cancellable.cancelTimeout must be installed`);
Promises.every([d1, d2, d3, d4], cancellable).then(
results => {
t.end(`Promises.every([d1,d2,d3,d4]) when cancelled, must NOT complete successfully with results: ${stringify(results)}`);
},
err => {
t.pass(`Promises.every([d1,d2,d3,d4]) when cancelled must reject with an error`);
// Cancel the d2, d3 & d4 delays too (just for clean-up)
t.ok(d1Cancellable.cancelTimeout(true), `d1Cancellable.cancelTimeout() should have timed-out`);
t.notOk(d2Cancellable.cancelTimeout(true), `d2Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.notOk(d3Cancellable.cancelTimeout(true), `d3Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.notOk(d4Cancellable.cancelTimeout(true), `d4Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.ok(err instanceof CancelledError, `Promises.every([d1,d2,d3,d4]) rejected error ${stringify(err)} must be instanceof CancelledError`);
t.notOk(err.completed, `CancelledError.completed must be false`);
const expectedResolvedOutcomes = [new Success('d1')];
t.deepEqual(err.resolvedOutcomes, expectedResolvedOutcomes, `Promises.every([d1,d2,d3,d4]) resolvedOutcomes must be ${stringify(expectedResolvedOutcomes)}`);
const expectedUnresolvedPromises = [d2, d3, d4];
t.deepEqual(err.unresolvedPromises, expectedUnresolvedPromises, `Promises.every([d1,d2,d3,d4]) unresolvedPromises must be ${stringify(expectedUnresolvedPromises)}`);
t.end();
}
);
t.ok(typeof cancellable.cancel === "function", `cancellable.cancel must be installed`);
const completed = cancellable.cancel();
t.notOk(completed, `Promises.every([d1,d2,d3,d4]) must not be completed yet`);
});
test("Promises.every([d1,d2,d3,d4]) cancelled during d1 (i.e. before d2, d3 & d4 resolve) will resolve only d1", t => {
const cancellable = {};
const d1Cancellable = {};
const d1 = genDelayedPromise(null, 'd1', 10, d1Cancellable, cancellable);
t.ok(typeof d1Cancellable.cancelTimeout === "function", `d1Cancellable.cancelTimeout must be installed`);
const d2Cancellable = {};
const d2 = genDelayedPromise(d2Error, 'd2', 2000, d2Cancellable);
t.ok(typeof d2Cancellable.cancelTimeout === "function", `d2Cancellable.cancelTimeout must be installed`);
const d3Cancellable = {};
const d3 = genDelayedPromise(null, 'd3', 3000, d3Cancellable);
t.ok(typeof d3Cancellable.cancelTimeout === "function", `d3Cancellable.cancelTimeout must be installed`);
const d4Cancellable = {};
const d4 = genDelayedPromise(d4Error, 'd4', 4000, d4Cancellable);
t.ok(typeof d4Cancellable.cancelTimeout === "function", `d4Cancellable.cancelTimeout must be installed`);
Promises.every([d1, d2, d3, d4], cancellable).then(
results => {
t.end(`Promises.every([d1,d2,d3,d4]) when cancelled, must NOT complete successfully with results: ${stringify(results)}`);
},
err => {
t.pass(`Promises.every([d1,d2,d3,d4]) when cancelled must reject with an error`);
// Cancel the d2, d3 & d4 delays too (just for clean-up)
t.ok(d1Cancellable.cancelTimeout(true), `d1Cancellable.cancelTimeout() should have timed-out`);
t.notOk(d2Cancellable.cancelTimeout(true), `d2Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.notOk(d3Cancellable.cancelTimeout(true), `d3Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.notOk(d4Cancellable.cancelTimeout(true), `d4Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.ok(err instanceof CancelledError, `Promises.every([d1,d2,d3,d4]) rejected error ${stringify(err)} must be instanceof CancelledError`);
t.notOk(err.completed, `CancelledError.completed must be false`);
const expectedResolvedOutcomes = [new Success('d1')];
t.deepEqual(err.resolvedOutcomes, expectedResolvedOutcomes, `Promises.every([d1,d2,d3,d4]) resolvedOutcomes must be ${stringify(expectedResolvedOutcomes)}`);
const expectedUnresolvedPromises = [d2, d3, d4];
t.deepEqual(err.unresolvedPromises, expectedUnresolvedPromises, `Promises.every([d1,d2,d3,d4]) unresolvedPromises must be ${stringify(expectedUnresolvedPromises)}`);
t.end();
}
);
t.ok(typeof cancellable.cancel === "function", `cancellable.cancel must be installed`);
});
test("Promises.every([d1,d2,d3,d4]) cancelled during d2 (i.e. before d3 & d4 resolve) will resolve only d1 & d2", t => {
const cancellable = {};
const d1Cancellable = {};
const d1 = genDelayedPromise(null, 'd1', 10, d1Cancellable);
t.ok(typeof d1Cancellable.cancelTimeout === "function", `d1Cancellable.cancelTimeout must be installed`);
const d2Cancellable = {};
const d2 = genDelayedPromise(d2Error, 'd2', 20, d2Cancellable, cancellable);
t.ok(typeof d2Cancellable.cancelTimeout === "function", `d2Cancellable.cancelTimeout must be installed`);
const d3Cancellable = {};
const d3 = genDelayedPromise(null, 'd3', 3000, d3Cancellable);
t.ok(typeof d3Cancellable.cancelTimeout === "function", `d3Cancellable.cancelTimeout must be installed`);
const d4Cancellable = {};
const d4 = genDelayedPromise(d4Error, 'd4', 4000, d4Cancellable);
t.ok(typeof d4Cancellable.cancelTimeout === "function", `d4Cancellable.cancelTimeout must be installed`);
Promises.every([d1, d2, d3, d4], cancellable).then(
results => {
t.end(`Promises.every([d1,d2,d3,d4]) when cancelled, must NOT complete successfully with results: ${stringify(results)}`);
},
err => {
t.pass(`Promises.every([d1,d2,d3,d4]) when cancelled must reject with an error`);
// Cancel the d2, d3 & d4 delays too (just for clean-up)
t.ok(d1Cancellable.cancelTimeout(true), `d1Cancellable.cancelTimeout() should have timed-out`);
t.ok(d2Cancellable.cancelTimeout(true), `d2Cancellable.cancelTimeout() should have timed-out`);
t.notOk(d3Cancellable.cancelTimeout(true), `d3Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.notOk(d4Cancellable.cancelTimeout(true), `d4Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.ok(err instanceof CancelledError, `Promises.every([d1,d2,d3,d4]) rejected error ${stringify(err)} must be instanceof CancelledError`);
t.notOk(err.completed, `CancelledError.completed must be false`);
const expectedResolvedOutcomes = [new Success('d1'), new Failure(d2Error)];
t.deepEqual(err.resolvedOutcomes, expectedResolvedOutcomes, `Promises.every([d1,d2,d3,d4]) resolvedOutcomes must be ${stringify(expectedResolvedOutcomes)}`);
const expectedUnresolvedPromises = [d3, d4];
t.deepEqual(err.unresolvedPromises, expectedUnresolvedPromises, `Promises.every([d1,d2,d3,d4]) unresolvedPromises must be ${stringify(expectedUnresolvedPromises)}`);
t.end();
}
);
t.ok(typeof cancellable.cancel === "function", `cancellable.cancel must be installed`);
});
test("Promises.every([d1,d2,d3,d4]) cancelled during d3 (i.e. before d4 completes) will resolve d1, d2 & d3", t => {
const cancellable = {};
const d1Cancellable = {};
const d1 = genDelayedPromise(null, 'd1', 10, d1Cancellable);
t.ok(typeof d1Cancellable.cancelTimeout === "function", `d1Cancellable.cancelTimeout must be installed`);
const d2Cancellable = {};
const d2 = genDelayedPromise(d2Error, 'd2', 20, d2Cancellable);
t.ok(typeof d2Cancellable.cancelTimeout === "function", `d2Cancellable.cancelTimeout must be installed`);
const d3Cancellable = {};
const d3 = genDelayedPromise(null, 'd3', 30, d3Cancellable, cancellable);
t.ok(typeof d3Cancellable.cancelTimeout === "function", `d3Cancellable.cancelTimeout must be installed`);
const d4Cancellable = {};
const d4 = genDelayedPromise(d4Error, 'd4', 4000, d4Cancellable);
t.ok(typeof d4Cancellable.cancelTimeout === "function", `d4Cancellable.cancelTimeout must be installed`);
Promises.every([d1, d2, d3, d4], cancellable).then(
results => {
t.end(`Promises.every([d1,d2,d3,d4]) when cancelled, must NOT complete successfully with results: ${stringify(results)}`);
},
err => {
t.pass(`Promises.every([d1,d2,d3,d4]) when cancelled must reject with an error`);
// Cancel the d2, d3 & d4 delays too (just for clean-up)
t.ok(d1Cancellable.cancelTimeout(true), `d1Cancellable.cancelTimeout() should have timed-out`);
t.ok(d2Cancellable.cancelTimeout(true), `d2Cancellable.cancelTimeout() should have timed-out`);
t.ok(d3Cancellable.cancelTimeout(true), `d3Cancellable.cancelTimeout() should have timed-out`);
t.notOk(d4Cancellable.cancelTimeout(true), `d4Cancellable.cancelTimeout() should NOT have timed-out yet`);
t.ok(err instanceof CancelledError, `Promises.every([d1,d2,d3,d4]) rejected error ${stringify(err)} must be instanceof CancelledError`);
t.notOk(err.completed, `CancelledError.completed must be false`);
const expectedResolvedOutcomes = [new Success('d1'), new Failure(d2Error), new Success('d3')];
t.deepEqual(err.resolvedOutcomes, expectedResolvedOutcomes, `Promises.every([d1,d2,d3,d4]) resolvedOutcomes must be ${stringify(expectedResolvedOutcomes)}`);
const expectedUnresolvedPromises = [d4];
t.deepEqual(err.unresolvedPromises, expectedUnresolvedPromises, `Promises.every([d1,d2,d3,d4]) unresolvedPromises must be ${stringify(expectedUnresolvedPromises)}`);
t.end();
}
);
t.ok(typeof cancellable.cancel === "function", `cancellable.cancel must be installed`);
});
test("Promises.every([d1,d2,d3,d4]) cancelled during d4 will resolve d1, d2, d3 & d4", t => {
const cancellable = {};
const d1Cancellable = {};
const d1 = genDelayedPromise(null, 'd1', 10, d1Cancellable);
t.ok(typeof d1Cancellable.cancelTimeout === "function", `d1Cancellable.cancelTimeout must be installed`);
const d2Cancellable = {};
const d2 = genDelayedPromise(d2Error, 'd2', 20, d2Cancellable);
t.ok(typeof d2Cancellable.cancelTimeout === "function", `d2Cancellable.cancelTimeout must be installed`);
const d3Cancellable = {};
const d3 = genDelayedPromise(null, 'd3', 30, d3Cancellable);
t.ok(typeof d3Cancellable.cancelTimeout === "function", `d3Cancellable.cancelTimeout must be installed`);
const d4Cancellable = {};
const d4 = genDelayedPromise(d4Error, 'd4', 40, d4Cancellable, cancellable);
t.ok(typeof d4Cancellable.cancelTimeout === "function", `d4Cancellable.cancelTimeout must be installed`);
Promises.every([d1, d2, d3, d4], cancellable).then(
outcomes => {
t.pass(`Promises.every([d1,d2,d3,d4]) when cancelled too late must resolve with outcomes`);
// Cancel the d2, d3 & d4 delays too (just for clean-up)
t.ok(d1Cancellable.cancelTimeout(true), `d1Cancellable.cancelTimeout() should have timed-out`);
t.ok(d2Cancellable.cancelTimeout(true), `d2Cancellable.cancelTimeout() should have timed-out`);
t.ok(d3Cancellable.cancelTimeout(true), `d3Cancellable.cancelTimeout() should have timed-out`);
t.ok(d4Cancellable.cancelTimeout(true), `d4Cancellable.cancelTimeout() should have timed-out`);
const expectedOutcomes = [new Success('d1'), new Failure(d2Error), new Success('d3'), new Failure(d4Error)];
t.deepEqual(outcomes, expectedOutcomes, `Promises.every([d1,d2,d3,d4]) outcomes must be ${stringify(expectedOutcomes)}`);
t.end();
},
err => {
t.end(`Promises.every([d1,d2,d3,d4]) when cancelled too late, must NOT reject with error`, err.stack);
}
);
t.ok(typeof cancellable.cancel === "function", `cancellable.cancel must be installed`);
});
// ---------------------------------------------------------------------------------------------------------------------
// Promises.one
// ---------------------------------------------------------------------------------------------------------------------
test("Promises.one(undefined)", t => {
const p = Promises.one(undefined);
t.ok(p instanceof Promise, `Promises.one(undefined) must be a Promise`);
p.then(
results => {
const expected = new Success(undefined);
t.deepEqual(results, expected, `Promises.one(undefined) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(undefined) must NOT fail`, err.stack);
t.end();
});
});
// Multiple Promises
const p2Error = new Error('p2 error');
const p2 = Promise.reject(p2Error);
const p3 = Promise.resolve('p3');
test("Promises.one(null)", t => {
const p = Promises.one(null);
t.ok(p instanceof Promise, `Promises.one(null) must be a Promise`);
p.then(
results => {
const expected = new Success(null);
t.deepEqual(results, expected, `Promises.one(null) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(null) must NOT fail`, err.stack);
t.end();
});
});
const p4Error = new Error('p4 error');
const p4 = Promise.reject(p4Error);
test("Promises.one(1)", t => {
const p = Promises.one(1);
t.ok(p instanceof Promise, `Promises.one(1) must be a Promise`);
p.then(
results => {
const expected = new Success(1);
t.deepEqual(results, expected, `Promises.one(1) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(1) must NOT fail`, err.stack);
t.end();
});
});
// Promises as arguments
Promise.every(p1, p2)
.then(results => {
const expected = [{result: 'p1'}, {error: p2Error}];
t.deepEqual(results, expected, `Promise.every(p1,p2) must be ${stringify(expected)}`);
test("Promises.one({})", t => {
const p = Promises.one({});
t.ok(p instanceof Promise, `Promises.one({}) must be a Promise`);
p.then(
results => {
const expected = new Success({});
t.deepEqual(results, expected, `Promises.one({}) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one({}) must NOT fail`, err.stack);
t.end();
});
});
Promise.every(p1, p2, p3)
.then(results => {
const expected = [{result: 'p1'}, {error: p2Error}, {result: 'p3'}];
t.deepEqual(results, expected, `Promise.every(p1,p2,p3) must be ${stringify(expected)}`);
test("Promises.one([])", t => {
const p = Promises.one([]);
t.ok(p instanceof Promise, `Promises.one([]) must be a Promise`);
p.then(
results => {
const expected = new Success([]);
t.deepEqual(results, expected, `Promises.one([]) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one([]) must NOT fail`, err.stack);
t.end();
});
});
Promise.every(p1, p2, p3, p4)
.then(results => {
const expected = [{result: 'p1'}, {error: p2Error}, {result: 'p3'}, {error: p4Error},];
t.deepEqual(results, expected, `Promise.every(p1,p2,p3,p4) must be ${stringify(expected)}`);
test("Promises.one({a:1})", t => {
const p = Promises.one({a: 1});
t.ok(p instanceof Promise, `Promises.one({a:1}) must be a Promise`);
p.then(
results => {
const expected = new Success({a: 1});
t.deepEqual(results, expected, `Promises.one({a:1}) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one({a:1}) must NOT fail`, err.stack);
t.end();
});
});
// Promises in arrays
Promise.every([p1, p2])
.then(results => {
const expected = [{result: 'p1'}, {error: p2Error}];
t.deepEqual(results, expected, `Promise.every([p1,p2]) must be ${stringify(expected)}`);
test("Promises.one([1,'2',3])", t => {
const p = Promises.one([1, '2', 3]);
t.ok(p instanceof Promise, `Promises.one([1,'2',3]) must be a Promise`);
p.then(
results => {
const expected = new Success([1, '2', 3]);
t.deepEqual(results, expected, `Promises.one([1,'2',3]) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one([1,'2',3]) must NOT fail`, err.stack);
t.end();
});
});
Promise.every([p1, p2, p3])
.then(results => {
const expected = [{result: 'p1'}, {error: p2Error}, {result: 'p3'}];
t.deepEqual(results, expected, `Promise.every([p1,p2,p3]) must be ${stringify(expected)}`);
test("Promises.one([p1])", t => {
const p = Promises.one([p1]);
t.ok(p instanceof Promise, `Promises.one([p1]) must be a Promise`);
p.then(
results => {
const expected = new Success([p1]);
t.deepEqual(results, expected, `Promises.one([p1]) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(p1) must NOT fail`, err.stack);
t.end();
});
});
Promise.every([p1, p2, p3, p4])
.then(results => {
const expected = [{result: 'p1'}, {error: p2Error}, {result: 'p3'}, {error: p4Error},];
t.deepEqual(results, expected, `Promise.every([p1,p2,p3,p4]) must be ${stringify(expected)}`);
test("Promises.one(p1)", t => {
const p = Promises.one(p1);
t.ok(p instanceof Promise, `Promises.one(p1) must be a Promise`);
p.then(
results => {
const expected = new Success('p1');
t.deepEqual(results, expected, `Promises.one(p1) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(p1) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.one(p2)", t => {
const p = Promises.one(p2);
t.ok(p instanceof Promise, `Promises.one(p2) must be a Promise`);
p.then(
resolution => {
const expected = new Failure(p2Error);
t.deepEqual(resolution, expected, `Promises.one(p2) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(p2) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.one(t1) with 'then-able' t1 that returns a result", t => {
const p = Promises.one(t1);
t.ok(p instanceof Promise, `Promises.one(t1) must be a Promise`);
t.notEqual(p, t1, `Promises.one(t1) must NOT be t1`);
p.then(
resolution => {
const expected = new Success('t1');
t.deepEqual(resolution, expected, `Promises.one(t1) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(t1) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.one(t2) with 'then-able' t2 that rejects with an error", t => {
const p = Promises.one(t2);
t.notEqual(p, t2, `Promises.one(t2) must NOT be t2`);
t.ok(p instanceof Promise, `Promises.one(t2) must be a Promise`);
p.then(
resolution => {
const expected = new Failure(t2Error);
t.deepEqual(resolution, expected, `Promises.one(t2) must reject with ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(t2) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.one(t3) with 'then-able' t3 that throws an error synchronously", t => {
const p = Promises.one(t3);
t.ok(p instanceof Promise, `Promises.one(t3) must be a Promise`);
t.notEqual(p, t3, `Promises.one(t3) must NOT be t3`);
p.then(
resolution => {
const expected = new Failure(t3Error);
t.deepEqual(resolution, expected, `Promises.one(t3) must reject with ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.one(t3) must NOT fail`, err.stack);
t.end();
});
});
// ---------------------------------------------------------------------------------------------------------------------
// Promises.toPromise
// ---------------------------------------------------------------------------------------------------------------------
test("Promises.toPromise(undefined)", t => {
const p = Promises.toPromise(undefined);
t.ok(p instanceof Promise, `Promises.toPromise(undefined) must be a Promise`);
p.then(
results => {
const expected = undefined;
t.deepEqual(results, expected, `Promises.toPromise(undefined) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise(undefined) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise(null)", t => {
const p = Promises.toPromise(null);
t.ok(p instanceof Promise, `Promises.toPromise(null) must be a Promise`);
p.then(
results => {
const expected = null;
t.deepEqual(results, expected, `Promises.toPromise(null) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise(null) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise(1)", t => {
const p = Promises.toPromise(1);
t.ok(p instanceof Promise, `Promises.toPromise(1) must be a Promise`);
p.then(
results => {
const expected = 1;
t.deepEqual(results, expected, `Promises.toPromise(1) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise(1) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise({})", t => {
const p = Promises.toPromise({});
t.ok(p instanceof Promise, `Promises.toPromise({}) must be a Promise`);
p.then(
results => {
const expected = {};
t.deepEqual(results, expected, `Promises.toPromise({}) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise({}) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise([])", t => {
const p = Promises.toPromise([]);
t.ok(p instanceof Promise, `Promises.toPromise([]) must be a Promise`);
p.then(
results => {
const expected = [];
t.deepEqual(results, expected, `Promises.toPromise([]) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise([]) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise({a:1})", t => {
const p = Promises.toPromise({a: 1});
t.ok(p instanceof Promise, `Promises.toPromise({a: 1}) must be a Promise`);
p.then(
results => {
const expected = {a: 1};
t.deepEqual(results, expected, `Promises.toPromise({a:1}) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise({a:1}) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise([1,'2',3])", t => {
const p = Promises.toPromise([1, '2', 3]);
t.ok(p instanceof Promise, `Promises.toPromise([1,'2',3]) must be a Promise`);
p.then(
results => {
const expected = [1, '2', 3];
t.deepEqual(results, expected, `Promises.toPromise([1,'2',3]) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise([1,'2',3]) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise([p1])", t => {
const p = Promises.toPromise([p1]);
t.ok(p instanceof Promise, `Promises.toPromise([p1]) must be a Promise`);
t.notEqual(p, p1, `Promises.toPromise([p1]) must NOT be p1`);
p.then(
results => {
const expected = [p1];
t.deepEqual(results, expected, `Promises.toPromise([p1]) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise([p1]) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise(p1)", t => {
const p = Promises.toPromise(p1);
t.ok(p instanceof Promise, `Promises.toPromise(p1) must be a Promise`);
t.equal(p, p1, `Promises.toPromise(p1) must be p1`);
p.then(
results => {
const expected = 'p1';
t.deepEqual(results, expected, `Promises.toPromise(p1) must be ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise(p1) must NOT fail`, err.stack);
t.end();
});
});
test("Promises.toPromise(p2)", t => {
const p = Promises.toPromise(p2);
t.ok(p instanceof Promise, `Promises.toPromise(p2) must be a Promise`);
t.equal(p, p2, `Promises.toPromise(p2) must be p2`);
p.then(
result => {
t.fail(`Promises.toPromise(p2) must NOT pass with result (${stringify(result)})`);
t.end();
},
err => {
const expected = p2Error;
t.deepEqual(err, expected, `Promises.toPromise(p2) must be ${stringify(expected)}`);
t.end();
});
});
test("Promises.toPromise(t1) with 'then-able' t1 that resolves with a result", t => {
const p = Promises.toPromise(t1);
t.ok(p instanceof Promise, `Promises.toPromise(t1) must be a Promise`);
t.notEqual(p, t1, `Promises.toPromise(t1) must NOT be t1`);
p.then(
results => {
const expected = 't1';
t.deepEqual(results, expected, `Promises.toPromise(t1) must resolve with ${stringify(expected)}`);
t.end();
},
err => {
t.fail(`Promises.toPromise(t1) must NOT reject with ${stringify(err)}`, err.stack);
t.end();
});
});
test("Promises.toPromise(t2) with 'then-able' t2 that rejects with an error", t => {
const p = Promises.toPromise(t2);
t.ok(p instanceof Promise, `Promises.toPromise(t2) must be a Promise`);
t.notEqual(p, t2, `Promises.toPromise(t2) must NOT be t2`);
p.then(
result => {
t.fail(`Promises.toPromise(t2) must NOT resolve with result (${stringify(result)})`);
t.end();
},
err => {
const expected = t2Error;
t.equal(err, expected, `Promises.toPromise(t2) must reject with ${stringify(expected)}`);
t.end();
});
});
test("Promises.toPromise(t3) with 'then-able' t3 that throws an error synchronously", t => {
const p = Promises.toPromise(t3);
t.ok(p instanceof Promise, `Promises.toPromise(t3) must be a Promise`);
t.notEqual(p, t3, `Promises.toPromise(t3) must NOT be t3`);
p.then(
result => {
t.fail(`Promises.toPromise(t3) must NOT resolve with result (${stringify(result)})`);
t.end();
},
err => {
const expected = t3Error;
t.equal(err, expected, `Promises.toPromise(t3) must reject with ${stringify(expected)}`);
t.end();
});
});

@@ -12,11 +12,4 @@ 'use strict';

const testing = require('./testing');
// const okNotOk = testing.okNotOk;
const checkOkNotOk = testing.checkOkNotOk;
// const checkMethodOkNotOk = testing.checkMethodOkNotOk;
// const equal = testing.equal;
const checkEqual = testing.checkEqual;
// const checkMethodEqual = testing.checkMethodEqual;
function wrap(value, wrapInString) {
//noinspection JSPrimitiveTypeWrapperUsage
return wrapInString && !(value instanceof String) ? new String(value) : value;

@@ -27,3 +20,4 @@ }

const wrapped = wrap(value, wrapInString);
return wrapInString || value instanceof String ? `String(${Strings.stringify(value, true, false, true)}) = [${Strings.stringify(wrapped ? wrapped.valueOf() : value, true, false, true)}] ` : '';
const opts = {quoteStrings: true};
return wrapInString || value instanceof String ? `String(${Strings.stringify(value, opts)}) = [${Strings.stringify(wrapped ? wrapped.valueOf() : value, opts)}] ` : '';
}

@@ -33,4 +27,3 @@

function check(value, expected) {
return checkOkNotOk(t, Strings.isString, [wrap(value, wrapInString)], expected, ' is a string', ' is NOT a string',
toPrefix(value, wrapInString));
return t.equal(!!Strings.isString(wrap(value, wrapInString)), !!expected, `Strings.isString(${toPrefix(value, wrapInString)}) is ${expected ? '' : 'NOT '}a string`);
}

@@ -98,4 +91,3 @@

function check(value, expected) {
return checkOkNotOk(t, Strings.isBlank, [wrap(value, wrapInString)], expected, ' is blank', ' is NOT blank',
toPrefix(value, wrapInString));
return t.equal(!!Strings.isBlank(wrap(value, wrapInString)), !!expected, `Strings.isBlank(${toPrefix(value, wrapInString)}) is ${expected ? '' : 'NOT '}blank`);
}

@@ -174,4 +166,3 @@

function check(value, expected) {
return checkOkNotOk(t, Strings.isNotBlank, [wrap(value, wrapInString)], expected, ' is not blank', ' is blank',
toPrefix(value, wrapInString));
return t.equal(!!Strings.isNotBlank(wrap(value, wrapInString)), !!expected, `Strings.isNotBlank(${toPrefix(value, wrapInString)}) is ${expected ? 'NOT ' : ''}blank`);
}

@@ -249,85 +240,93 @@

function checkStringify(t, wrapInString) {
function check(value, expected) {
return checkEqual(t, Strings.stringify, [wrap(value, wrapInString)], expected, toPrefix(value, wrapInString));
function checkNoOpts(value, expected) {
return t.equal(Strings.stringify(wrap(value, wrapInString)), expected, `Strings.stringify(${toPrefix(value, wrapInString)} must be ${expected}`);
}
function checkWithArgs(value, useToStringForErrors, avoidToJSONMethods, quoteStrings, expected) {
return checkEqual(t, Strings.stringify, [wrap(value, wrapInString), useToStringForErrors, avoidToJSONMethods, quoteStrings], expected, toPrefix(value, wrapInString));
function checkWithOpts(value, opts, expected) {
return t.equal(Strings.stringify(wrap(value, wrapInString), opts), expected, `Strings.stringify(${toPrefix(value, wrapInString)} must be ${expected}`);
}
// undefined
check(undefined, 'undefined');
checkNoOpts(undefined, 'undefined');
// null
check(null, 'null');
checkNoOpts(null, 'null');
// objects
check({}, wrapInString ? '[object Object]' : '{}');
check({a: 1, b: 2}, wrapInString ? '[object Object]' : JSON.stringify({a: 1, b: 2}));
check({a: 1, b: 2, o: {c: 'C'}}, wrapInString ? '[object Object]' : JSON.stringify({a: 1, b: 2, o: {c: 'C'}}));
checkNoOpts({}, wrapInString ? '[object Object]' : '{}');
checkNoOpts({a: 1, b: 2}, wrapInString ? '[object Object]' : JSON.stringify({a: 1, b: 2}));
checkNoOpts({a: 1, b: 2, o: {c: 'C'}}, wrapInString ? '[object Object]' : JSON.stringify({a: 1, b: 2, o: {c: 'C'}}));
// booleans
check(true, 'true');
check(false, 'false');
checkNoOpts(true, 'true');
checkNoOpts(false, 'false');
// arrays
check([], wrapInString ? '' : '[]');
check([1, 2, 3], wrapInString ? '1,2,3' : '[1, 2, 3]');
checkNoOpts([], wrapInString ? '' : '[]');
checkNoOpts([1, 2, 3], wrapInString ? '1,2,3' : '[1, 2, 3]');
// special case numbers
check(Number.POSITIVE_INFINITY, `${Number.POSITIVE_INFINITY}`);
check(Number.NEGATIVE_INFINITY, `${Number.NEGATIVE_INFINITY}`);
check(NaN, 'NaN');
checkNoOpts(Number.POSITIVE_INFINITY, `${Number.POSITIVE_INFINITY}`);
checkNoOpts(Number.NEGATIVE_INFINITY, `${Number.NEGATIVE_INFINITY}`);
checkNoOpts(NaN, 'NaN');
// negative numbers
check(Number.MIN_VALUE, `${Number.MIN_VALUE}`);
check(Number.MIN_SAFE_INTEGER, `${Number.MIN_SAFE_INTEGER}`);
check(-123.456, '-123.456');
check(-1, '-1');
checkNoOpts(Number.MIN_VALUE, `${Number.MIN_VALUE}`);
checkNoOpts(Number.MIN_SAFE_INTEGER, `${Number.MIN_SAFE_INTEGER}`);
checkNoOpts(-123.456, '-123.456');
checkNoOpts(-1, '-1');
// zero
check(0, '0'); // not sure if this should return blank for 0
checkNoOpts(0, '0'); // not sure if this should return blank for 0
// positive numbers
check(1, '1');
check(123.456, '123.456');
check(Number.MAX_SAFE_INTEGER, `${Number.MAX_SAFE_INTEGER}`);
check(Number.MAX_VALUE, `${Number.MAX_VALUE}`);
checkNoOpts(1, '1');
checkNoOpts(123.456, '123.456');
checkNoOpts(Number.MAX_SAFE_INTEGER, `${Number.MAX_SAFE_INTEGER}`);
checkNoOpts(Number.MAX_VALUE, `${Number.MAX_VALUE}`);
// strings containing numbers
check(`${Number.MIN_VALUE}`, `${Number.MIN_VALUE}`);
check(`${Number.MIN_SAFE_INTEGER}`, `${Number.MIN_SAFE_INTEGER}`);
check('-123.456', '-123.456');
check('-1', '-1');
checkNoOpts(`${Number.MIN_VALUE}`, `${Number.MIN_VALUE}`);
checkNoOpts(`${Number.MIN_SAFE_INTEGER}`, `${Number.MIN_SAFE_INTEGER}`);
checkNoOpts('-123.456', '-123.456');
checkNoOpts('-1', '-1');
check('0', '0');
checkNoOpts('0', '0');
check('1', '1');
check('123.456', '123.456');
check(`${Number.MAX_SAFE_INTEGER}`, `${Number.MAX_SAFE_INTEGER}`);
check(`${Number.MAX_VALUE}`, `${Number.MAX_VALUE}`);
checkNoOpts('1', '1');
checkNoOpts('123.456', '123.456');
checkNoOpts(`${Number.MAX_SAFE_INTEGER}`, `${Number.MAX_SAFE_INTEGER}`);
checkNoOpts(`${Number.MAX_VALUE}`, `${Number.MAX_VALUE}`);
// strings containing only whitespace
check('', '');
check(' ', ' ');
check('\n', '\n');
check('\r', '\r');
check('\t', '\t');
check(' ', ' ');
check(' \n ', ' \n ');
check(' \r ', ' \r ');
check(' \t ', ' \t ');
check(' \n \r \t \n \r \t ', ' \n \r \t \n \r \t ');
checkNoOpts('', '');
checkNoOpts(' ', ' ');
checkNoOpts('\n', '\n');
checkNoOpts('\r', '\r');
checkNoOpts('\t', '\t');
checkNoOpts(' ', ' ');
checkNoOpts(' \n ', ' \n ');
checkNoOpts(' \r ', ' \r ');
checkNoOpts(' \t ', ' \t ');
checkNoOpts(' \n \r \t \n \r \t ', ' \n \r \t \n \r \t ');
// strings not containing numbers
check('a', 'a');
check('abc', 'abc');
check('ABC', 'ABC');
checkWithArgs('', false, false, true, '""');
checkWithArgs('ABC', false, false, true, '"ABC"');
checkNoOpts('a', 'a');
checkNoOpts('abc', 'abc');
checkNoOpts('ABC', 'ABC');
checkWithOpts('', {quoteStrings: true}, '""');
checkWithOpts('ABC', {quoteStrings: true}, '"ABC"');
// errors
check(new Error('Planned error'), wrapInString ? 'Error: Planned error' : '{"name":"Error","message":"Planned error"}');
checkWithArgs(new Error('Planned error'), true, false, false, wrapInString ? 'Error: Planned error' : '[Error: Planned error]');
checkNoOpts(new Error('Planned error'), wrapInString ? 'Error: Planned error' : '[Error: Planned error]');
checkWithOpts(new Error('Planned error'), {avoidErrorToString: false}, wrapInString ? 'Error: Planned error' : '[Error: Planned error]');
checkWithOpts(new Error('Planned error'), {avoidErrorToString: true}, wrapInString ? 'Error: Planned error' : '{"name":"Error","message":"Planned error"}');
const error = new TypeError('Planned error');
error.extra1 = 'Extra 1';
error.extra2 = 'Extra 2';
checkNoOpts(error, wrapInString ? 'TypeError: Planned error' : '[TypeError: Planned error {"extra1":"Extra 1","extra2":"Extra 2"}]');
checkWithOpts(error, {avoidErrorToString: false}, wrapInString ? 'TypeError: Planned error' : '[TypeError: Planned error {"extra1":"Extra 1","extra2":"Extra 2"}]');
checkWithOpts(error, {avoidErrorToString: true}, wrapInString ? 'TypeError: Planned error' : '{"name":"TypeError","message":"Planned error","extra1":"Extra 1","extra2":"Extra 2"}');
// circular objects

@@ -337,3 +336,3 @@ const circular0 = {a: 1, o: {b: 2}};

circular0.o.oAgain = circular0.o;
check(circular0, wrapInString ? '[object Object]' : '{"a":1,"o":{"b":2,"oAgain":[Circular: this.o]},"circular":[Circular: this]}');
checkNoOpts(circular0, wrapInString ? '[object Object]' : '{"a":1,"o":{"b":2,"oAgain":[Circular: this.o]},"circular":[Circular: this]}');

@@ -352,3 +351,3 @@ const circular1 = {a: 1, b: 2, o: {c: 'C', p: {d: 'D'}}};

circular1.o.p.pAgain = circular1.o.p;
check(circular1, wrapInString ? '[object Object]' : '{"a":1,"b":2,"o":{"c":"C","p":{"d":"D","thisAgain":[Circular: this],"oAgain":[Circular: this.o],"pAgain":[Circular: this.o.p]},"thisAgain":[Circular: this],"oAgain":[Circular: this.o],"pAgain":[Reference: this.o.p]},"thisAgain":[Circular: this],"oAgain":[Reference: this.o],"pAgain":[Reference: this.o.p]}');
checkNoOpts(circular1, wrapInString ? '[object Object]' : '{"a":1,"b":2,"o":{"c":"C","p":{"d":"D","thisAgain":[Circular: this],"oAgain":[Circular: this.o],"pAgain":[Circular: this.o.p]},"thisAgain":[Circular: this],"oAgain":[Circular: this.o],"pAgain":[Reference: this.o.p]},"thisAgain":[Circular: this],"oAgain":[Reference: this.o],"pAgain":[Reference: this.o.p]}');

@@ -362,3 +361,3 @@ // circular arrays with circular objects

check(array2, wrapInString ? 'a,[object Object],123,' : '["a", {"thisAgain":[Circular: this],"this1Again":[Circular: this[1]]}, 123, [Circular: this]]');
checkNoOpts(array2, wrapInString ? 'a,[object Object],123,' : '["a", {"thisAgain":[Circular: this],"this1Again":[Circular: this[1]]}, 123, [Circular: this]]');

@@ -371,3 +370,3 @@ const array3 = ['x', {y: 'Y'}, 123];

check(circular3, wrapInString ? '[object Object]' : '{"y":"Y","thisAgain":[Circular: this],"arrayAgain":["x", [Circular: this], 123, [Circular: this.arrayAgain]]}');
checkNoOpts(circular3, wrapInString ? '[object Object]' : '{"y":"Y","thisAgain":[Circular: this],"arrayAgain":["x", [Circular: this], 123, [Circular: this.arrayAgain]]}');

@@ -385,3 +384,3 @@ // circular objects with circular arrays

check(circular4, wrapInString ? '[object Object]' : '{"a":"A","array":["b", {"z":"Z","thisAgain":[Circular: this],"arrayAgain":[Circular: this.array]}, 456, [Circular: this.array]],"thisAgain":[Circular: this],"arrayAgain":[Reference: this.array]}');
checkNoOpts(circular4, wrapInString ? '[object Object]' : '{"a":"A","array":["b", {"z":"Z","thisAgain":[Circular: this],"arrayAgain":[Circular: this.array]}, 456, [Circular: this.array]],"thisAgain":[Circular: this],"arrayAgain":[Reference: this.array]}');

@@ -401,6 +400,6 @@ const array5 = ['c', {x: "X"}, 789];

check(array5, wrapInString ? 'c,[object Object],789,' : '["c", {"x":"X","thisAgain":[Circular: this],"this1Again":[Circular: this[1]],"circular5":{"a":"A","array":[Circular: this],"thisAgain":[Circular: this],"this1Again":[Circular: this[1]],"this1Circular5Again":[Circular: this[1].circular5],"this1Circular5ArrayAgain":[Circular: this]}}, 789, [Circular: this]]');
checkNoOpts(array5, wrapInString ? 'c,[object Object],789,' : '["c", {"x":"X","thisAgain":[Circular: this],"this1Again":[Circular: this[1]],"circular5":{"a":"A","array":[Circular: this],"thisAgain":[Circular: this],"this1Again":[Circular: this[1]],"this1Circular5Again":[Circular: this[1].circular5],"this1Circular5ArrayAgain":[Circular: this]}}, 789, [Circular: this]]');
// reference-only objects
const array6 = [{a:1}, {b:"B"}];
const array6 = [{a: 1}, {b: "B"}];
const references6 = {

@@ -413,3 +412,3 @@ array6: array6,

};
check(references6, wrapInString ? '[object Object]' : '{"array6":[{"a":1}, {"b":"B"}],"array6Again":[Reference: this.array6],"array6aOnly":[[Reference: this.array6[0]]],"array6bOnly":[[Reference: this.array6[1]]],"diffArrayWithSameElems":[[Reference: this.array6[0]], [Reference: this.array6[1]]]}');
checkNoOpts(references6, wrapInString ? '[object Object]' : '{"array6":[{"a":1}, {"b":"B"}],"array6Again":[Reference: this.array6],"array6aOnly":[[Reference: this.array6[0]]],"array6bOnly":[[Reference: this.array6[1]]],"diffArrayWithSameElems":[[Reference: this.array6[0]], [Reference: this.array6[1]]]}');

@@ -419,2 +418,3 @@ // Functions

}
//check(func, wrapInString ? 'function func() {}' : '[Function: func]'); // this test breaks if function func() {} is reformatted to multi-line

@@ -424,8 +424,8 @@ if (wrapInString) {

} else {
check(func, '[Function: func]');
checkNoOpts(func, '[Function: func]');
}
check({fn: func}, wrapInString ? '[object Object]' : '{"fn":[Function: func]}');
checkNoOpts({fn: func}, wrapInString ? '[object Object]' : '{"fn":[Function: func]}');
// undefined object properties
check({a: undefined}, wrapInString ? '[object Object]' : '{"a":undefined}');
checkNoOpts({a: undefined}, wrapInString ? '[object Object]' : '{"a":undefined}');

@@ -438,3 +438,4 @@ // objects with toJSON methods

executable: true,
execute: () => { },
execute: () => {
},
subTaskDefs: [],

@@ -444,4 +445,5 @@ parent: undefined

executable: true,
execute: () => { },
_subTasks: [],
execute: () => {
},
_subTasks: [],
_subTasksByName: {},

@@ -474,9 +476,9 @@ parent: undefined,

// default behaviour must use toJSON method
check(task, wrapInString ? '[object Object]' : '{"name":"Task1","executable":true,"state":{"code":"Unstarted","completed":false,"rejected":false},"attempts":1,"lastExecutedAt":"2016-12-01T05:09:09.119Z","subTasks":[]}');
checkNoOpts(task, wrapInString ? '[object Object]' : '{"name":"Task1","executable":true,"state":{"code":"Unstarted","completed":false,"rejected":false},"attempts":1,"lastExecutedAt":"2016-12-01T05:09:09.119Z","subTasks":[]}');
// explicit !avoidToJSONMethods must use toJSON method
checkWithArgs(task, false, false, false, wrapInString ? '[object Object]' : '{"name":"Task1","executable":true,"state":{"code":"Unstarted","completed":false,"rejected":false},"attempts":1,"lastExecutedAt":"2016-12-01T05:09:09.119Z","subTasks":[]}');
checkWithOpts(task, {avoidToJSONMethods: false}, wrapInString ? '[object Object]' : '{"name":"Task1","executable":true,"state":{"code":"Unstarted","completed":false,"rejected":false},"attempts":1,"lastExecutedAt":"2016-12-01T05:09:09.119Z","subTasks":[]}');
// explicit avoidToJSONMethods must NOT use toJSON method
checkWithArgs(task, false, true, false, wrapInString ? '[object Object]' : '{"name":"Task1","definition":{"name":"Task1","executable":true,"execute":[Function: anonymous],"subTaskDefs":[],"parent":undefined},"executable":true,"execute":[Function: anonymous],"_subTasks":[],"_subTasksByName":{},"parent":undefined,"_state":{"code":"Unstarted","completed":false,"error":undefined,"rejected":false,"reason":undefined},"_attempts":1,"_lastExecutedAt":"2016-12-01T05:09:09.119Z","_result":undefined,"_error":undefined,"_slaveTasks":[],"_frozen":true,"toJSON":[Function: toJSON]}');
checkWithOpts(task, {avoidToJSONMethods: true}, wrapInString ? '[object Object]' : '{"name":"Task1","definition":{"name":"Task1","executable":true,"execute":[Function: anonymous],"subTaskDefs":[],"parent":undefined},"executable":true,"execute":[Function: anonymous],"_subTasks":[],"_subTasksByName":{},"parent":undefined,"_state":{"code":"Unstarted","completed":false,"error":undefined,"rejected":false,"reason":undefined},"_attempts":1,"_lastExecutedAt":"2016-12-01T05:09:09.119Z","_result":undefined,"_error":undefined,"_slaveTasks":[],"_frozen":true,"toJSON":[Function: toJSON]}');
}

@@ -486,3 +488,6 @@

function check(value, expected) {
return checkEqual(t, Strings.trim, [wrap(value, wrapInString)], expected, toPrefix(value, wrapInString));
if (Number.isNaN(Strings.trim(wrap(value, wrapInString)) && Number.isNaN(expected))) {
return t.pass(`Strings.trim(${toPrefix(value, wrapInString)} must be ${expected}`);
}
return t.deepEqual(Strings.trim(wrap(value, wrapInString)), expected, `Strings.trim(${toPrefix(value, wrapInString)} must be ${expected}`);
}

@@ -563,3 +568,6 @@

function check(value, expected) {
return checkEqual(t, Strings.trimOrEmpty, [wrap(value, wrapInString)], expected, toPrefix(value, wrapInString));
if (Number.isNaN(Strings.trimOrEmpty(wrap(value, wrapInString)) && Number.isNaN(expected))) {
return t.pass(`Strings.trimOrEmpty(${toPrefix(value, wrapInString)} must be ${expected}`);
}
return t.deepEqual(Strings.trimOrEmpty(wrap(value, wrapInString)), expected, `Strings.trimOrEmpty(${toPrefix(value, wrapInString)} must be ${expected}`);
}

@@ -730,2 +738,109 @@

t.end();
});
});
test('stringify on Maps', t => {
let entries = [];
let expected = `[Map {}]`;
t.equal(Strings.stringify(new Map(entries)), expected, `stringify on Map(${Strings.stringify(entries)}) must be ${expected}`);
entries = [['a', 123]];
expected = `[Map {"a" => 123}]`;
t.equal(Strings.stringify(new Map(entries)), expected, `stringify on Map(${Strings.stringify(entries)}) must be ${expected}`);
entries = [['b', 789], [{o: 1}, 'Xyz']];
expected = `[Map {"b" => 789, {"o":1} => "Xyz"}]`;
t.equal(Strings.stringify(new Map(entries)), expected, `stringify on Map(${Strings.stringify(entries)}) must be ${expected}`);
// Map containing a Map
let map = new Map([['a', 3.14]]);
entries = [['b', 789], [{o: 1}, 'Xyz'], ['m', map], [map, map]];
expected = `[Map {"b" => 789, {"o":1} => "Xyz", "m" => [Map {"a" => 3.14}], [Reference: this[2].VAL] => [Reference: this[2].VAL]}]`;
t.equal(Strings.stringify(new Map(entries)), expected, `stringify on Map(${Strings.stringify(entries)}) must be ${expected}`);
t.end();
});
test('stringify on WeakMaps', t => {
let entries = [];
let expected = `[WeakMap]`;
t.equal(Strings.stringify(new WeakMap(entries)), expected, `stringify on WeakMap(${Strings.stringify(entries)}) must be ${expected}`);
entries = [[{'a': 1}, 123]];
expected = `[WeakMap]`;
t.equal(Strings.stringify(new WeakMap(entries)), expected, `stringify on WeakMap(${Strings.stringify(entries)}) must be ${expected}`);
entries = [[{'b': 2}, 789], [{o: 1}, 'Xyz']];
expected = `[WeakMap]`;
t.equal(Strings.stringify(new WeakMap(entries)), expected, `stringify on WeakMap(${Strings.stringify(entries)}) must be ${expected}`);
// Map containing a WeakMap
let map = new WeakMap([[{'a': 1}, 3.14]]);
entries = [['b', 789], [{o: 1}, 'Xyz'], ['m', map], [map, map]];
expected = `[Map {"b" => 789, {"o":1} => "Xyz", "m" => [WeakMap], [Reference: this[2].VAL] => [Reference: this[2].VAL]}]`;
t.equal(Strings.stringify(new Map(entries)), expected, `stringify on Map(${Strings.stringify(entries)}) must be ${expected}`);
t.end();
});
test('stringify on Promises', t => {
// Attempts to get Node's util.js inspect function (if available)
const inspect = (() => {
try {
return require('util').inspect;
} catch (_) {
return undefined;
}
})();
let p = Promise.resolve('Hi');
let expected = inspect ? `[Promise { 'Hi' }]` : `[Promise]`;
t.equal(Strings.stringify(p), expected, `stringify(Promise.resolve('Hi')) -> ${Strings.stringify(p)} must be ${expected}`);
p = Promise.resolve('Hi "Bob"');
expected = inspect ? `[Promise { 'Hi "Bob"' }]` : `[Promise]`;
t.equal(Strings.stringify(p), expected, `stringify(Promise.resolve('Hi "Bob"')) -> ${Strings.stringify(p)} must be ${expected}`);
// Long string that "breaks" to next line
const promised1 = 'shardId-000000000000:49545115243490985018280067714973144582180062593244200961';
p = Promise.resolve(promised1);
expected = inspect ? `[Promise { '${promised1}' }]` : `[Promise]`;
t.equal(Strings.stringify(p), expected, `stringify(Promise.resolve(${promised1})) -> ${Strings.stringify(p)} must be ${expected}`);
const promised2 = 'shardId-111111111111:49545115243490985018280067714973144582180062593244200962';
let promised = [promised1, promised2];
p = Promise.resolve(promised);
expected = inspect ? `[Promise { [ '${promised1}', '${promised2}' ] }]` : `[Promise]`;
t.equal(Strings.stringify(p), expected, `stringify(Promise.resolve(${promised})) -> ${Strings.stringify(p)} must be ${expected}`);
promised = {a: 1, p: Promise.resolve('Another promise')};
p = Promise.resolve(Promise.resolve(promised));
expected = inspect ? `[Promise { { a: 1, p: Promise { 'Another promise' } } }]` : `[Promise]`;
t.equal(Strings.stringify(p), expected, `stringify(Promise.resolve(Promise.resolve(${Strings.stringify(promised)}))) -> ${Strings.stringify(p)} must be ${expected}`);
promised = {a: 1, p: Promise.resolve(promised2)};
p = Promise.resolve(Promise.resolve(promised));
// if (inspect) {
// const inspectOpts = {depth: null, breakLength: Infinity};
// console.log(`####################### inspect(p}) = ${inspect(p)}`);
// console.log(`####################### inspect(p, ${Strings.stringify(inspectOpts)}) = ${inspect(p, inspectOpts)}`);
// }
expected = inspect ? `[Promise { { a: 1, p: Promise { '${promised2}' } } }]` : `[Promise]`;
t.equal(Strings.stringify(p), expected, `stringify(Promise.resolve(Promise.resolve(${Strings.stringify(promised)}))) -> ${Strings.stringify(p)} must be ${expected}`);
p = Promise.resolve({a: 1}).then(r => r);
expected = inspect ? `[Promise { <pending> }]` : `[Promise]`;
t.equal(Strings.stringify(p), expected, `stringify(Promise.resolve({a:1}).then(r => r)) -> ${Strings.stringify(p)} must be ${expected}`);
t.end();
});
test('stringify on console & Console', t => {
console.log(`console = ${Strings.stringify(console)}`);
t.equal(Strings.stringify(console), '[Console]', `stringify(console) must be '[Console]'`);
const customConsole = new console.Console(process.stdout, process.stderr);
t.notEqual(customConsole, console, `customConsole must not be console`);
t.deepEqual(Strings.stringify(customConsole), '[Console {}]', `stringify(customConsole) must be '[Console {}]'`);
t.end();
});

@@ -29,11 +29,14 @@ 'use strict';

function toFnArgsPrefix(fn, args) {
return prefixFnArgs ? `${fn.name ? fn.name : '' }${stringify(args, true, false, true)} -> ` : '';
const opts = {quoteStrings: true};
return prefixFnArgs ? `${fn.name ? fn.name : '' }${stringify(args, opts)} -> ` : '';
}
function toMethodArgsPrefix(obj, method, args) {
return prefixFnArgs ? `${obj.name ? obj.name : stringify(obj, true, false, true)}.${method.name ? method.name : '<anon>' }${stringify(args, true, false, true)} -> ` : '';
const opts = {quoteStrings: true};
return prefixFnArgs ? `${obj.name ? obj.name : stringify(obj, opts)}.${method.name ? method.name : '<anon>' }${stringify(args, opts)} -> ` : '';
}
function equal(t, actual, expected, prefix) {
const msg = `${toPrefix(prefix)}${stringify(actual, true, false, true)} must be ${stringify(expected, true, false, true)}`;
const opts = {quoteStrings: true};
const msg = `${toPrefix(prefix)}${stringify(actual, opts)} must be ${stringify(expected, opts)}`;

@@ -91,10 +94,11 @@ if (Numbers.isNaN(actual)) {

const now = new Date().toISOString();
const opts = {quoteStrings: true};
try {
obj[propertyName] = now;
t.fail(`${prefix ? prefix : ''}${stringify(obj, true, false, true)} ${propertyName} is supposed to be immutable`);
t.fail(`${prefix ? prefix : ''}${stringify(obj, opts)} ${propertyName} is supposed to be immutable`);
} catch (err) {
// Expect an error on attempted mutation of immutable property
t.pass(`${prefix ? prefix : ''}${stringify(obj, true, false, true)} ${propertyName} is immutable`);
t.pass(`${prefix ? prefix : ''}${stringify(obj, opts)} ${propertyName} is immutable`);
//console.log(`Expected error ${err}`);
}
}

@@ -12,12 +12,4 @@ 'use strict';

const Strings = require('../strings');
// const Strings = require('../strings');
const testing = require('./testing');
// const okNotOk = testing.okNotOk;
const checkOkNotOk = testing.checkOkNotOk;
// const checkMethodOkNotOk = testing.checkMethodOkNotOk;
// const equal = testing.equal;
const checkEqual = testing.checkEqual;
// const checkMethodEqual = testing.checkMethodEqual;
test('Cancel normal timeout with cancelTimeout', t => {

@@ -24,0 +16,0 @@ const timeout = setTimeout(() => {

@@ -16,11 +16,2 @@ 'use strict';

/**
* @typedef {Object} Timeout - A timeout object created by {@linkcode setTimeout} or {@linkcode setInterval}, which can
* be cancelled by using the {@linkcode clearTimeout} or {@linkcode clearInterval} functions respectively.
*
* @property {boolean} _called - a "private" flag that indicates if the Timeout has been triggered already or not
* @property {Function|null} _repeat - a "private" function that appears to be present on interval timeouts and null
* on normal timeouts
*/
/**
* Attempts to cancel the given normal timeout, which was created by a call to {@linkcode setTimeout} using the

@@ -27,0 +18,0 @@ * {@linkcode clearTimeout} function as instructed (and if the timeout looks like an interval timeout also using the

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc