Source: /home/graham/source_projects/assert-x/lib/assert-x.js

/**
 * @file {@link http://xotic750.github.io/assert-x/ assert-x}. A Javascript assertion library..
 * @version 0.0.24
 * @author Graham Fairweather <xotic750@gmail.com>
 * @copyright Copyright (c) 2013 Graham Fairweather
 * @license {@link <http://www.gnu.org/licenses/gpl-3.0.html> GPL3}
 * @module assert-x
 * @requires util-x
 */

/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*global module, define, require */

(function (globalThis) {
    'use strict';

    function factory(utilx) {
        /**
         * @namespace assertx
         */
        var assertx = {},
            maxMessageLength = 128,
            CustomError = utilx.customError('AssertionError', maxMessageLength);

        utilx.objectDefineProperty(assertx, 'AssertionError', {
            /**
             * The AssertionError constructor.
             * @name AssertionError
             * @memberOf assertx
             * @constructor
             * @augments CustomError
             * @param {object} opts
             * @return {undefined}
             */
            value: function (opts) {
                if (!utilx.isPlainObject(opts)) {
                    opts = {};
                }

                if (!utilx.isString(opts.message)) {
                    opts.message = '';
                }

                if (!utilx.isString(opts.operator)) {
                    opts.operator = '';
                }

                if (!utilx.isFunction(opts.stackStartFunction)) {
                    opts.stackStartFunction = assertx.AssertionError;
                }

                CustomError.call(this, opts.message, opts.stackStartFunction);
                this.actual = opts.actual;
                this.expected = opts.expected;
                this.operator = opts.operator;
            },
            enumerable: false,
            writable: true,
            configurable: true
        });

        utilx.inherits(assertx.AssertionError, CustomError);

        utilx.objectDefineProperties(assertx.AssertionError.prototype, {
            toString: {
                value: function () {
                    var theString;

                    if (utilx.isString(this.message) && !utilx.isEmptyString(this.message)) {
                        theString = this.name + ': ' + utilx.stringTruncate(this.message, maxMessageLength);
                    } else if (utilx.objectInstanceOf(this, assertx.AssertionError)) {
                        theString = this.name + ': ';
                        theString += utilx.stringTruncate(utilx.jsonStringify(this.actual,
                                                                utilx.customErrorReplacer), maxMessageLength) + ' ';
                        theString += this.operator + ' ';
                        theString += utilx.stringTruncate(utilx.jsonStringify(this.expected,
                                                                utilx.customErrorReplacer), maxMessageLength);
                    }

                    return theString;
                },
                enumerable: false,
                writable: true,
                configurable: true
            }
        });

        /**
         * Returns whether an exception is expected. Used by throws.
         * @private
         * @function
         * @param {*} actual
         * @param {*} expected
         * @return {boolean}
         */
        function expectedException(actual, expected) {
            var storeState,
                val;

            if (utilx.isUndefinedOrNull(actual) || utilx.isUndefinedOrNull(expected)) {
                return false;
            }

            if (utilx.isRegExp(expected) && utilx.objectInstanceOf(actual, Error)) {
                storeState = utilx.normaliseErrorIEToString.state();
                if (utilx.isFalse(storeState)) {
                    utilx.normaliseErrorIEToString.on();
                }

                val = actual.toString();
                if (utilx.isFalse(storeState)) {
                    utilx.normaliseErrorIEToString.off();
                }

                return expected.test(val);
            }

            if (utilx.objectInstanceOf(actual, expected)) {
                return true;
            }

            if (utilx.isFunction(expected)) {
                storeState = utilx.normaliseErrorIEToString.state();
                if (utilx.isFalse(storeState)) {
                    utilx.normaliseErrorIEToString.on();
                }

                val = expected.call({}, actual);
                if (utilx.isFalse(storeState)) {
                    utilx.normaliseErrorIEToString.off();
                }

                if (utilx.isTrue(val)) {
                    return true;
                }
            }

            return false;
        }

        /**
         * Throws an exception that displays the values for actual and expected separated by the provided operator.
         * @private
         * @function
         * @param {*} actual
         * @param {*} expected
         * @param {string} message
         * @param {string} operator
         * @param {function} [stackStartFunction]
         * @return {undefined}
         */
        function throwAssertionError(actual, expected, message, operator, stackStartFunction) {
            if (!utilx.isFunction(stackStartFunction)) {
                stackStartFunction = throwAssertionError;
            }

            throw new assertx.AssertionError({
                message: message,
                actual: actual,
                expected: expected,
                operator: operator,
                stackStartFunction: stackStartFunction
            });
        }

        /**
         * Returns whether an exception is expected. Used by assertx.throws and assertx.doesNotThrow.
         * @private
         * @function
         * @param {boolean} shouldThrow
         * @param {function} block
         * @param {*} expected
         * @param {string} [message]
         * @param {function} [stackStartFunction]
         * @return {undefined}
         */
        function throws(shouldThrow, block, expected, message, stackStartFunction) {
            var wasExceptionExpected,
                actual;

            if (!utilx.isFunction(stackStartFunction)) {
                if (utilx.isFunction(message)) {
                    stackStartFunction = message;
                    message = utilx.privateUndefined;
                } else {
                    stackStartFunction = throws;
                }
            }

            if ((!utilx.isString(message) || utilx.isEmptyString(message)) && utilx.isString(expected)) {
                message = expected;
                expected = null;
            }

            try {
                block();
            } catch (e) {
                actual = e;
            }

            wasExceptionExpected = expectedException(actual, expected);
            message = (expected && utilx.isString(expected.name) && utilx.isEmptyString(expected.name) ?
                       ' (' + expected.name + ').' :
                       '.') + (message ? ' ' + message : '.');

            if (utilx.isTrue(shouldThrow) && !actual) {
                throwAssertionError(actual, expected, 'Missing expected exception' + message,
                                    utilx.privateUndefined, stackStartFunction);
            }

            if (utilx.isFalse(shouldThrow) && utilx.isTrue(wasExceptionExpected)) {
                throwAssertionError(actual, expected, 'Got unwanted exception' + message,
                                    utilx.privateUndefined, stackStartFunction);
            }

            if ((utilx.isTrue(shouldThrow) && actual && expected && utilx.isFalse(wasExceptionExpected)) ||
                    (utilx.isFalse(shouldThrow) && actual)) {

                throw actual;
            }
        }

        utilx.objectDefineProperties(assertx, {
            /**
             * Throws an exception that displays the values for actual and expected.
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            fail: {
                value: function (actual, expected, message, stackStartFunction) {
                    throwAssertionError(actual, expected, message, 'fail', stackStartFunction);
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests if value is truthy, it is equivalent to assert.equal(!!value, true, message);
             * @memberOf assertx
             * @function
             * @param {*} value
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            ok: {
                value: function (value, message, stackStartFunction) {
                    var pass = utilx.toBoolean(value);

                    if (utilx.isFalse(pass)) {
                        throwAssertionError(pass, true, message, 'ok', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests if value is truthy, it is equivalent to assert.equal(!value, true, message);
             * @memberOf assertx
             * @function
             * @param {*} value
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            notOk: {
                value: function (value, message, stackStartFunction) {
                    var pass = utilx.toBoolean(value);

                    if (utilx.isTrue(pass)) {
                        throwAssertionError(pass, true, message, 'notOk', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests shallow, coercive equality with the equal comparison operator ( == ).
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            equal: {
                value: function (actual, expected, message, stackStartFunction) {
                    if (utilx.notEqual(actual, expected)) {
                        throwAssertionError(actual, expected, message, '==', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests shallow, coercive non-equality with the not equal comparison operator ( != ).
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            notEqual: {
                value: function (actual, expected, message, stackStartFunction) {
                    if (utilx.equal(actual, expected)) {
                        throwAssertionError(actual, expected, message, '!=', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests for deep equality, coercive equality with the equal comparison operator ( == ) and equivalent.
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            deepEqual: {
                value: function (actual, expected, message, stackStartFunction) {
                    if (!utilx.deepEqual(actual, expected)) {
                        throwAssertionError(actual, expected, message, 'deepEqual', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests for deep inequality.
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            notDeepEqual: {
                value: function (actual, expected, message, stackStartFunction) {
                    if (utilx.deepEqual(actual, expected)) {
                        throwAssertionError(actual, expected, message, 'notDeepEqual', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests for deep strict equality, equality with the strict equal comparison operator
             * ( === ) and equivalent.
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            deepStrictEqual: {
                value: function (actual, expected, message, stackStartFunction) {
                    if (!utilx.deepStrictEqual(actual, expected)) {
                        throwAssertionError(actual, expected, message, 'deepStrictEqual', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests for deep strict inequality.
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            notDeepStrictEqual: {
                value: function (actual, expected, message, stackStartFunction) {
                    if (utilx.deepStrictEqual(actual, expected)) {
                        throwAssertionError(actual, expected, message, 'notDeepStrictEqual', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests strict equality, as determined by the strict equality operator ( === ).
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            strictEqual: {
                value: function (actual, expected, message, stackStartFunction) {
                    if (utilx.notStrictEqual(actual, expected)) {
                        throwAssertionError(actual, expected, message, '===', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests strict non-equality, as determined by the strict not equal operator ( !== ).
             * @memberOf assertx
             * @function
             * @param {*} actual
             * @param {*} expected
             * @param {string} message
             * @param {string} operator
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            notStrictEqual: {
                value: function (actual, expected, message, stackStartFunction) {
                    if (utilx.strictEqual(actual, expected)) {
                        throwAssertionError(actual, expected, message, '!==', stackStartFunction);
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Expects block to throw an error. error can be constructor, regexp or validation function.
             * @memberOf assertx
             * @function
             * @param {function} block
             * @param {constructor|regexp|function} error
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            throws: {
                value: function (block, error, message, stackStartFunction) {
                    throws(true, block, error, message, stackStartFunction);
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Expects block not to throw an error, see assert.throws for details.
             * @memberOf assertx
             * @function
             * @param {function} block
             * @param {string} message
             * @param {function} [stackStartFunction]
             * @return {undefined}
             */
            doesNotThrow: {
                value: function (block, message, stackStartFunction) {
                    throws(false, block, message, stackStartFunction);
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * Tests if value is not a falsy value, throws if it is a truthy value.
             * Useful when testing the first argument, error in callbacks.
             * @memberOf assertx
             * @function
             * @param {*} err
             * @return {undefined}
             */
            ifError: {
                value: function (err) {
                    if (err) {
                        throw err;
                    }
                },
                enumerable: false,
                writable: true,
                configurable: true
            },

            /**
             * The Javascript library that assert-x is built on for cross environment compatability.
             * @memberOf assertx
             * @type {object}
             */
            utilx: {
                value: utilx,
                enumerable: false,
                writable: true,
                configurable: true
            }
        });

        return assertx;
    }

    /*
     *
     * UMD
     *
     */

    if (typeof globalThis !== 'object' || null === globalThis) {
        throw new TypeError('Invalid global context');
    }

    var publicAssert;

    /*global module, define */
    if (typeof module === 'object' && null !== module &&
            typeof module.exports === 'object' && null !== module.exports) {

        publicAssert = factory(require('util-x'));
        publicAssert.utilx.objectDefineProperty(publicAssert, 'factory', {
            value: function (deep) {
                var pa;

                if (publicAssert.utilx.isTrue(deep)) {
                    pa = factory(require('util-x').factory());
                } else {
                    pa = factory(require('util-x'));
                }

                publicAssert.utilx.objectDefineProperty(pa, 'factory', {
                    value: publicAssert.factory,
                    enumerable: false,
                    writable: true,
                    configurable: true
                });

                return pa;
            },
            enumerable: false,
            writable: true,
            configurable: true
        });

        publicAssert.utilx.objectDefineProperty(module, 'exports', {
            value: publicAssert,
            enumerable: false,
            writable: true,
            configurable: true
        });
    } else if (typeof define === 'function' && typeof define.amd === 'object' && null !== define.amd) {
        require.config({
            paths: {
                'util-x': '//raw.github.com/Xotic750/util-x/master/lib/util-x'
            }
        });

        define(['util-x'], function (utilx) {
            publicAssert = factory(utilx);
            publicAssert.utilx.objectDefineProperty(publicAssert, 'factory', {
                value: function (deep) {
                    var pa;

                    if (publicAssert.utilx.isTrue(deep)) {
                        pa = factory(utilx.factory());
                    } else {
                        pa = factory(utilx);
                    }

                    publicAssert.utilx.objectDefineProperty(pa, 'factory', {
                        value: publicAssert.factory,
                        enumerable: false,
                        writable: true,
                        configurable: true
                    });

                    return pa;
                },
                enumerable: false,
                writable: true,
                configurable: true
            });

            return publicAssert;
        });
    } else {
        publicAssert = factory(globalThis.utilx);
        publicAssert.utilx.objectDefineProperty(publicAssert, 'factory', {
            value: function (deep) {
                var pa;

                if (publicAssert.utilx.isTrue(deep)) {
                    pa = factory(globalThis.utilx.factory());
                } else {
                    pa = factory(globalThis.utilx);
                }

                publicAssert.utilx.objectDefineProperty(pa, 'factory', {
                    value: publicAssert.factory,
                    enumerable: false,
                    writable: true,
                    configurable: true
                });

                return pa;
            },
            enumerable: false,
            writable: true,
            configurable: true
        });

        publicAssert.utilx.objectDefineProperty(globalThis, 'assertx', {
            value: publicAssert,
            enumerable: false,
            writable: true,
            configurable: true
        });
    }
}(this));