diff --git a/Chutzpah.NoVS.sln b/Chutzpah.NoVS.sln index 31bcd0b2..e982a411 100644 --- a/Chutzpah.NoVS.sln +++ b/Chutzpah.NoVS.sln @@ -84,4 +84,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {370B87B0-118E-4F9A-A13C-8E4A9B222047} + EndGlobalSection EndGlobal diff --git a/Chutzpah/Chutzpah.csproj b/Chutzpah/Chutzpah.csproj index cc4cc4f6..6b3b8d88 100644 --- a/Chutzpah/Chutzpah.csproj +++ b/Chutzpah/Chutzpah.csproj @@ -388,6 +388,9 @@ Always + + Always + Always @@ -397,6 +400,9 @@ Always + + Always + Always @@ -406,6 +412,9 @@ Always + + Always + Always @@ -433,6 +442,9 @@ Always + + Always + Always @@ -481,6 +493,24 @@ Always + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + Always diff --git a/Chutzpah/ChutzpahJSRunners/Chrome/jasmineRunnerV3.js b/Chutzpah/ChutzpahJSRunners/Chrome/jasmineRunnerV3.js new file mode 100644 index 00000000..de410c56 --- /dev/null +++ b/Chutzpah/ChutzpahJSRunners/Chrome/jasmineRunnerV3.js @@ -0,0 +1,22 @@ +/// + +(async function () { + + const functions = require('../jasmineFunctionsV3.js'); + const chutzpahRunner = require('./chutzpahRunner.js'); + + try { + await chutzpahRunner.runner( + functions.onInitialized, + functions.onPageLoaded, + functions.isJasmineLoaded, + functions.onJasmineLoaded, + functions.isTestingDone); + + } catch (e) { + throw new Error("Failed to run jasmineRunnerV3.js: " + e); + } +})().catch(e => { + console.error(e); + process.exit(2); +}); \ No newline at end of file diff --git a/Chutzpah/ChutzpahJSRunners/JsDom/jasmineRunnerV3.js b/Chutzpah/ChutzpahJSRunners/JsDom/jasmineRunnerV3.js new file mode 100644 index 00000000..de410c56 --- /dev/null +++ b/Chutzpah/ChutzpahJSRunners/JsDom/jasmineRunnerV3.js @@ -0,0 +1,22 @@ +/// + +(async function () { + + const functions = require('../jasmineFunctionsV3.js'); + const chutzpahRunner = require('./chutzpahRunner.js'); + + try { + await chutzpahRunner.runner( + functions.onInitialized, + functions.onPageLoaded, + functions.isJasmineLoaded, + functions.onJasmineLoaded, + functions.isTestingDone); + + } catch (e) { + throw new Error("Failed to run jasmineRunnerV3.js: " + e); + } +})().catch(e => { + console.error(e); + process.exit(2); +}); \ No newline at end of file diff --git a/Chutzpah/ChutzpahJSRunners/jasmineFunctionsV3.js b/Chutzpah/ChutzpahJSRunners/jasmineFunctionsV3.js new file mode 100644 index 00000000..eb5c057b --- /dev/null +++ b/Chutzpah/ChutzpahJSRunners/jasmineFunctionsV3.js @@ -0,0 +1,163 @@ +var module = module || {}; +module.exports = module.exports || {}; + +function onInitialized() { + console.log("!!_!! onInitialized Jasmine - v3"); +} + +function isTestingDone() { + console.log("!!_!! isTestingDone"); + return window.chutzpah.isTestingFinished === true; +} + +function isJasmineLoaded() { + console.log("!!_!! isJasmineLoaded"); + return !!window.jasmine && !!window.jasmine.getEnv; +} + +function onJasmineLoaded() { + + console.log("!!_!! onJasmineLoaded"); + + function log(obj) { + console.log(JSON.stringify(obj)); + } + + + var activeTestCase = null, + fileStartTime = null, + testStartTime = null, + suites = []; + + window.chutzpah.isTestingFinished = false; + window.chutzpah.testCases = []; + + function logCoverage() { + if (window._Chutzpah_covobj_name && window[window._Chutzpah_covobj_name]) { + log({ type: "CoverageObject", object: JSON.stringify(window[window._Chutzpah_covobj_name]) }); + } + } + + function recordStackTrace(stack) { + if (stack) { + // Truncate stack to 5 deep. + stack = stack.split('\n').slice(1, 6).join('\n'); + } + return stack; + } + + + function ChutzpahJasmineReporter(options) { + + var passedCount = 0; + var failedCount = 0; + var skippedCount = 0; + + this.jasmineStarted = function () { + + fileStartTime = new Date().getTime(); + + // Testing began + log({ type: "FileStart" }); + }; + + + this.jasmineDone = function () { + var timetaken = new Date().getTime() - fileStartTime; + logCoverage(); + log({ type: "FileDone", timetaken: timetaken, passed: passedCount, failed: failedCount }); + window.chutzpah.isTestingFinished = true; + }; + + + this.suiteStarted = function (result) { + suites.push(result); + }; + + this.suiteDone = function (result) { + suites.pop(); + }; + + this.specStarted = function (result) { + var currentSuiteName = suites.length > 0 + ? suites[suites.length - 1].fullName + : null; + + testStartTime = new Date().getTime(); + var suiteName = currentSuiteName; + var specName = result.description; + var newTestCase = { moduleName: suiteName, testName: specName, testResults: [] }; + window.chutzpah.testCases.push(newTestCase); + activeTestCase = newTestCase; + log({ type: "TestStart", testCase: activeTestCase }); + }; + + this.specDone = function (result) { + if (result.status === "disabled") { + return; + } + + if (result.status === "failed") { + failedCount++; + } + else if (result.status === "pending") { + skippedCount++; + activeTestCase.skipped = true; + } + else { + passedCount++; + } + + var timetaken = new Date().getTime() - testStartTime; + activeTestCase.timetaken = timetaken; + + for (var i = 0; i < result.failedExpectations.length; i++) { + var expectation = result.failedExpectations[i]; + + var testResult = {}; + testResult.passed = false; + testResult.message = expectation.message; + testResult.stackTrace = recordStackTrace(expectation.stack); + activeTestCase.testResults.push(testResult); + + } + + // Log test case when done. This will get picked up by phantom and streamed to chutzpah. + log({ type: "TestDone", testCase: activeTestCase }); + + + }; + + } + + if (window.chutzpah.testMode) { + jasmine.getEnv().addReporter(new ChutzpahJasmineReporter()); + } + + if (window.chutzpah.testMode === 'discovery') { + // If discovery mode overwrite execute to not run the test + jasmine.getEnv().beforeAll = function () { }; + jasmine.getEnv().afterAll = function () { }; + jasmine.Spec.prototype.execute = function (onComplete) { + this.onStart(this); + this.resultCallback(this.result); + if (onComplete) + onComplete(); + }; + } + +} + +function onPageLoaded() { + console.log("!!_!! onPageLoaded"); + + if (!window.chutzpah.usingModuleLoader && window.chutzpah.autoStart !== false) { + (window.chutzpah.start || window.initializeJasmine)(); + } +} + +module.exports.onInitialized = onInitialized; +module.exports.isTestingDone = isTestingDone; +module.exports.isJasmineLoaded = isJasmineLoaded; +module.exports.onJasmineLoaded = onJasmineLoaded; +module.exports.onPageLoaded = onPageLoaded; \ No newline at end of file diff --git a/Chutzpah/ChutzpahTestHarnessFiles/Coverage/blanket_jasmine_v3.js b/Chutzpah/ChutzpahTestHarnessFiles/Coverage/blanket_jasmine_v3.js new file mode 100644 index 00000000..36ef004e --- /dev/null +++ b/Chutzpah/ChutzpahTestHarnessFiles/Coverage/blanket_jasmine_v3.js @@ -0,0 +1,5791 @@ +/*! blanket - v1.2.1 */ + +(function (define) { + (function (f) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = f() } else if (typeof define === "function" && define.amd) { define([], f) } else { var g; if (typeof window !== "undefined") { g = window } else if (typeof global !== "undefined") { g = global } else if (typeof self !== "undefined") { g = self } else { g = this } g.acorn = f() } })(function () { + var define, module, exports; return (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require == "function" && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = "MODULE_NOT_FOUND", f } var l = n[o] = { exports: {} }; t[o][0].call(l.exports, function (e) { var n = t[o][1][e]; return s(n ? n : e) }, l, l.exports, e, t, n, r) } return n[o].exports } var i = typeof require == "function" && require; for (var o = 0; o < r.length; o++) s(r[o]); return s })({ + 1: [function (_dereq_, module, exports) { + + + // The main exported interface (under `self.acorn` when in the + // browser) is a `parse` function that takes a code string and + // returns an abstract syntax tree as specified by [Mozilla parser + // API][api]. + // + // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API + + "use strict"; + + exports.parse = parse; + + // This function tries to parse a single expression at a given + // offset in a string. Useful for parsing mixed-language formats + // that embed JavaScript expressions. + + exports.parseExpressionAt = parseExpressionAt; + + // Acorn is organized as a tokenizer and a recursive-descent parser. + // The `tokenize` export provides an interface to the tokenizer. + + exports.tokenizer = tokenizer; + exports.__esModule = true; + // Acorn is a tiny, fast JavaScript parser written in JavaScript. + // + // Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and + // various contributors and released under an MIT license. + // + // Git repositories for Acorn are available at + // + // http://marijnhaverbeke.nl/git/acorn + // https://github.com/marijnh/acorn.git + // + // Please use the [github bug tracker][ghbt] to report issues. + // + // [ghbt]: https://github.com/marijnh/acorn/issues + // + // This file defines the main parser interface. The library also comes + // with a [error-tolerant parser][dammit] and an + // [abstract syntax tree walker][walk], defined in other files. + // + // [dammit]: acorn_loose.js + // [walk]: util/walk.js + + var _state = _dereq_("./state"); + + var Parser = _state.Parser; + + var _options = _dereq_("./options"); + + var getOptions = _options.getOptions; + + _dereq_("./parseutil"); + + _dereq_("./statement"); + + _dereq_("./lval"); + + _dereq_("./expression"); + + exports.Parser = _state.Parser; + exports.plugins = _state.plugins; + exports.defaultOptions = _options.defaultOptions; + + var _location = _dereq_("./location"); + + exports.SourceLocation = _location.SourceLocation; + exports.getLineInfo = _location.getLineInfo; + exports.Node = _dereq_("./node").Node; + + var _tokentype = _dereq_("./tokentype"); + + exports.TokenType = _tokentype.TokenType; + exports.tokTypes = _tokentype.types; + + var _tokencontext = _dereq_("./tokencontext"); + + exports.TokContext = _tokencontext.TokContext; + exports.tokContexts = _tokencontext.types; + + var _identifier = _dereq_("./identifier"); + + exports.isIdentifierChar = _identifier.isIdentifierChar; + exports.isIdentifierStart = _identifier.isIdentifierStart; + exports.Token = _dereq_("./tokenize").Token; + + var _whitespace = _dereq_("./whitespace"); + + exports.isNewLine = _whitespace.isNewLine; + exports.lineBreak = _whitespace.lineBreak; + exports.lineBreakG = _whitespace.lineBreakG; + var version = "1.2.2"; exports.version = version; + + function parse(input, options) { + var p = parser(options, input); + var startPos = p.pos, + startLoc = p.options.locations && p.curPosition(); + p.nextToken(); + return p.parseTopLevel(p.options.program || p.startNodeAt(startPos, startLoc)); + } + + function parseExpressionAt(input, pos, options) { + var p = parser(options, input, pos); + p.nextToken(); + return p.parseExpression(); + } + + function tokenizer(input, options) { + return parser(options, input); + } + + function parser(options, input) { + return new Parser(getOptions(options), String(input)); + } + + }, { "./expression": 6, "./identifier": 7, "./location": 8, "./lval": 9, "./node": 10, "./options": 11, "./parseutil": 12, "./state": 13, "./statement": 14, "./tokencontext": 15, "./tokenize": 16, "./tokentype": 17, "./whitespace": 19 }], 2: [function (_dereq_, module, exports) { + if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; + } else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () { } + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } + + }, {}], 3: [function (_dereq_, module, exports) { + // shim for using process in browser + + var process = module.exports = {}; + var queue = []; + var draining = false; + + function drainQueue() { + if (draining) { + return; + } + draining = true; + var currentQueue; + var len = queue.length; + while (len) { + currentQueue = queue; + queue = []; + var i = -1; + while (++i < len) { + currentQueue[i](); + } + len = queue.length; + } + draining = false; + } + process.nextTick = function (fun) { + queue.push(fun); + if (!draining) { + setTimeout(drainQueue, 0); + } + }; + + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() { } + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + // TODO(shtylman) + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function () { return 0; }; + + }, {}], 4: [function (_dereq_, module, exports) { + module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; + } + }, {}], 5: [function (_dereq_, module, exports) { + (function (process, global) { + // Copyright Joyent, Inc. and other Node contributors. + // + // Permission is hereby granted, free of charge, to any person obtaining a + // copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to permit + // persons to whom the Software is furnished to do so, subject to the + // following conditions: + // + // The above copyright notice and this permission notice shall be included + // in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var formatRegExp = /%[sdj%]/g; + exports.format = function (f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function (x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; + }; + + + // Mark that a method should not be used. + // Returns a modified function which warns once by default. + // If --no-deprecation is set, then it is a no-op. + exports.deprecate = function (fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function () { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; + }; + + + var debugs = {}; + var debugEnviron; + exports.debuglog = function (set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function () { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function () { }; + } + } + return debugs[set]; + }; + + + /** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ + /* legacy: obj, showHidden, depth, colors*/ + function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); + } + exports.inspect = inspect; + + + // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics + inspect.colors = { + 'bold': [1, 22], + 'italic': [3, 23], + 'underline': [4, 24], + 'inverse': [7, 27], + 'white': [37, 39], + 'grey': [90, 39], + 'black': [30, 39], + 'blue': [34, 39], + 'cyan': [36, 39], + 'green': [32, 39], + 'magenta': [35, 39], + 'red': [31, 39], + 'yellow': [33, 39] + }; + + // Don't use 'blue' not visible on cmd.exe + inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' + }; + + + function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } + } + + + function stylizeNoColor(str, styleType) { + return str; + } + + + function arrayToHash(array) { + var hash = {}; + + array.forEach(function (val, idx) { + hash[val] = true; + }); + + return hash; + } + + + function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function (key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); + } + + + function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); + } + + + function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; + } + + + function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function (key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; + } + + + function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function (line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function (line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; + } + + + function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function (prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; + } + + + // NOTE: These type checking functions intentionally don't use `instanceof` + // because it is fragile and can be easily faked with `Object.create()`. + function isArray(ar) { + return Array.isArray(ar); + } + exports.isArray = isArray; + + function isBoolean(arg) { + return typeof arg === 'boolean'; + } + exports.isBoolean = isBoolean; + + function isNull(arg) { + return arg === null; + } + exports.isNull = isNull; + + function isNullOrUndefined(arg) { + return arg == null; + } + exports.isNullOrUndefined = isNullOrUndefined; + + function isNumber(arg) { + return typeof arg === 'number'; + } + exports.isNumber = isNumber; + + function isString(arg) { + return typeof arg === 'string'; + } + exports.isString = isString; + + function isSymbol(arg) { + return typeof arg === 'symbol'; + } + exports.isSymbol = isSymbol; + + function isUndefined(arg) { + return arg === void 0; + } + exports.isUndefined = isUndefined; + + function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; + } + exports.isRegExp = isRegExp; + + function isObject(arg) { + return typeof arg === 'object' && arg !== null; + } + exports.isObject = isObject; + + function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; + } + exports.isDate = isDate; + + function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); + } + exports.isError = isError; + + function isFunction(arg) { + return typeof arg === 'function'; + } + exports.isFunction = isFunction; + + function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; + } + exports.isPrimitive = isPrimitive; + + exports.isBuffer = _dereq_('./support/isBuffer'); + + function objectToString(o) { + return Object.prototype.toString.call(o); + } + + + function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); + } + + + var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + + // 26 Feb 16:19:34 + function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); + } + + + // log is just a thin wrapper to console.log that prepends a timestamp + exports.log = function () { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); + }; + + + /** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ + exports.inherits = _dereq_('inherits'); + + exports._extend = function (origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; + }; + + function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + }).call(this, _dereq_('_process'), typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + }, { "./support/isBuffer": 4, "_process": 3, "inherits": 2 }], 6: [function (_dereq_, module, exports) { + // A recursive descent parser operates by defining functions for all + // syntactic elements, and recursively calling those, each function + // advancing the input stream and returning an AST node. Precedence + // of constructs (for example, the fact that `!x[1]` means `!(x[1])` + // instead of `(!x)[1]` is handled by the fact that the parser + // function that parses unary prefix operators is called first, and + // in turn calls the function that parses `[]` subscripts — that + // way, it'll receive the node for `x[1]` already parsed, and wraps + // *that* in the unary operator node. + // + // Acorn uses an [operator precedence parser][opp] to handle binary + // operator precedence, because it is much more compact than using + // the technique outlined above, which uses different, nesting + // functions to specify precedence, for all of the ten binary + // precedence levels that JavaScript defines. + // + // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser + + "use strict"; + + var tt = _dereq_("./tokentype").types; + + var Parser = _dereq_("./state").Parser; + + var reservedWords = _dereq_("./identifier").reservedWords; + + var has = _dereq_("./util").has; + + var pp = Parser.prototype; + + // Check if property name clashes with already added. + // Object/class getters and setters are not allowed to clash — + // either with each other or with an init property — and in + // strict mode, init properties are also not allowed to be repeated. + + pp.checkPropClash = function (prop, propHash) { + if (this.options.ecmaVersion >= 6) return; + var key = prop.key, + name = undefined; + switch (key.type) { + case "Identifier": + name = key.name; break; + case "Literal": + name = String(key.value); break; + default: + return; + } + var kind = prop.kind || "init", + other = undefined; + if (has(propHash, name)) { + other = propHash[name]; + var isGetSet = kind !== "init"; + if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init)) this.raise(key.start, "Redefinition of property"); + } else { + other = propHash[name] = { + init: false, + get: false, + set: false + }; + } + other[kind] = true; + }; + + // ### Expression parsing + + // These nest, from the most general expression type at the top to + // 'atomic', nondivisible expression types at the bottom. Most of + // the functions will simply let the function(s) below them parse, + // and, *if* the syntactic construct they handle is present, wrap + // the AST node that the inner parser gave them in another node. + + // Parse a full expression. The optional arguments are used to + // forbid the `in` operator (in for loops initalization expressions) + // and provide reference for storing '=' operator inside shorthand + // property assignment in contexts where both object expression + // and object pattern might appear (so it's possible to raise + // delayed syntax error at correct position). + + pp.parseExpression = function (noIn, refShorthandDefaultPos) { + var startPos = this.start, + startLoc = this.startLoc; + var expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos); + if (this.type === tt.comma) { + var node = this.startNodeAt(startPos, startLoc); + node.expressions = [expr]; + while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos)); + return this.finishNode(node, "SequenceExpression"); + } + return expr; + }; + + // Parse an assignment expression. This includes applications of + // operators like `+=`. + + pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse) { + if (this.type == tt._yield && this.inGenerator) return this.parseYield(); + + var failOnShorthandAssign = undefined; + if (!refShorthandDefaultPos) { + refShorthandDefaultPos = { start: 0 }; + failOnShorthandAssign = true; + } else { + failOnShorthandAssign = false; + } + var startPos = this.start, + startLoc = this.startLoc; + if (this.type == tt.parenL || this.type == tt.name) this.potentialArrowAt = this.start; + var left = this.parseMaybeConditional(noIn, refShorthandDefaultPos); + if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc); + if (this.type.isAssign) { + var node = this.startNodeAt(startPos, startLoc); + node.operator = this.value; + node.left = this.type === tt.eq ? this.toAssignable(left) : left; + refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly + this.checkLVal(left); + this.next(); + node.right = this.parseMaybeAssign(noIn); + return this.finishNode(node, "AssignmentExpression"); + } else if (failOnShorthandAssign && refShorthandDefaultPos.start) { + this.unexpected(refShorthandDefaultPos.start); + } + return left; + }; + + // Parse a ternary conditional (`?:`) operator. + + pp.parseMaybeConditional = function (noIn, refShorthandDefaultPos) { + var startPos = this.start, + startLoc = this.startLoc; + var expr = this.parseExprOps(noIn, refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + if (this.eat(tt.question)) { + var node = this.startNodeAt(startPos, startLoc); + node.test = expr; + node.consequent = this.parseMaybeAssign(); + this.expect(tt.colon); + node.alternate = this.parseMaybeAssign(noIn); + return this.finishNode(node, "ConditionalExpression"); + } + return expr; + }; + + // Start the precedence parser. + + pp.parseExprOps = function (noIn, refShorthandDefaultPos) { + var startPos = this.start, + startLoc = this.startLoc; + var expr = this.parseMaybeUnary(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + return this.parseExprOp(expr, startPos, startLoc, -1, noIn); + }; + + // Parse binary operators with the operator precedence parsing + // algorithm. `left` is the left-hand side of the operator. + // `minPrec` provides context that allows the function to stop and + // defer further parser to one of its callers when it encounters an + // operator that has a lower precedence than the set it is parsing. + + pp.parseExprOp = function (left, leftStartPos, leftStartLoc, minPrec, noIn) { + var prec = this.type.binop; + if (Array.isArray(leftStartPos)) { + if (this.options.locations && noIn === undefined) { + // shift arguments to left by one + noIn = minPrec; + minPrec = leftStartLoc; + // flatten leftStartPos + leftStartLoc = leftStartPos[1]; + leftStartPos = leftStartPos[0]; + } + } + if (prec != null && (!noIn || this.type !== tt._in)) { + if (prec > minPrec) { + var node = this.startNodeAt(leftStartPos, leftStartLoc); + node.left = left; + node.operator = this.value; + var op = this.type; + this.next(); + var startPos = this.start, + startLoc = this.startLoc; + node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn); + this.finishNode(node, op === tt.logicalOR || op === tt.logicalAND ? "LogicalExpression" : "BinaryExpression"); + return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn); + } + } + return left; + }; + + // Parse unary operators, both prefix and postfix. + + pp.parseMaybeUnary = function (refShorthandDefaultPos) { + if (this.type.prefix) { + var node = this.startNode(), + update = this.type === tt.incDec; + node.operator = this.value; + node.prefix = true; + this.next(); + node.argument = this.parseMaybeUnary(); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); + if (update) this.checkLVal(node.argument); else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") this.raise(node.start, "Deleting local variable in strict mode"); + return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); + } + var startPos = this.start, + startLoc = this.startLoc; + var expr = this.parseExprSubscripts(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + while (this.type.postfix && !this.canInsertSemicolon()) { + var node = this.startNodeAt(startPos, startLoc); + node.operator = this.value; + node.prefix = false; + node.argument = expr; + this.checkLVal(expr); + this.next(); + expr = this.finishNode(node, "UpdateExpression"); + } + return expr; + }; + + // Parse call, dot, and `[]`-subscript expressions. + + pp.parseExprSubscripts = function (refShorthandDefaultPos) { + var startPos = this.start, + startLoc = this.startLoc; + var expr = this.parseExprAtom(refShorthandDefaultPos); + if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr; + return this.parseSubscripts(expr, startPos, startLoc); + }; + + pp.parseSubscripts = function (base, startPos, startLoc, noCalls) { + if (Array.isArray(startPos)) { + if (this.options.locations && noCalls === undefined) { + // shift arguments to left by one + noCalls = startLoc; + // flatten startPos + startLoc = startPos[1]; + startPos = startPos[0]; + } + } + for (; ;) { + if (this.eat(tt.dot)) { + var node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.property = this.parseIdent(true); + node.computed = false; + base = this.finishNode(node, "MemberExpression"); + } else if (this.eat(tt.bracketL)) { + var node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.property = this.parseExpression(); + node.computed = true; + this.expect(tt.bracketR); + base = this.finishNode(node, "MemberExpression"); + } else if (!noCalls && this.eat(tt.parenL)) { + var node = this.startNodeAt(startPos, startLoc); + node.callee = base; + node.arguments = this.parseExprList(tt.parenR, false); + base = this.finishNode(node, "CallExpression"); + } else if (this.type === tt.backQuote) { + var node = this.startNodeAt(startPos, startLoc); + node.tag = base; + node.quasi = this.parseTemplate(); + base = this.finishNode(node, "TaggedTemplateExpression"); + } else { + return base; + } + } + }; + + // Parse an atomic expression — either a single token that is an + // expression, an expression started by a keyword like `function` or + // `new`, or an expression wrapped in punctuation like `()`, `[]`, + // or `{}`. + + pp.parseExprAtom = function (refShorthandDefaultPos) { + var node = undefined, + canBeArrow = this.potentialArrowAt == this.start; + switch (this.type) { + case tt._this: + case tt._super: + var type = this.type === tt._this ? "ThisExpression" : "Super"; + node = this.startNode(); + this.next(); + return this.finishNode(node, type); + + case tt._yield: + if (this.inGenerator) this.unexpected(); + + case tt.name: + var startPos = this.start, + startLoc = this.startLoc; + var id = this.parseIdent(this.type !== tt.name); + if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id]); + return id; + + case tt.regexp: + var value = this.value; + node = this.parseLiteral(value.value); + node.regex = { pattern: value.pattern, flags: value.flags }; + return node; + + case tt.num: case tt.string: + return this.parseLiteral(this.value); + + case tt._null: case tt._true: case tt._false: + node = this.startNode(); + node.value = this.type === tt._null ? null : this.type === tt._true; + node.raw = this.type.keyword; + this.next(); + return this.finishNode(node, "Literal"); + + case tt.parenL: + return this.parseParenAndDistinguishExpression(canBeArrow); + + case tt.bracketL: + node = this.startNode(); + this.next(); + // check whether this is array comprehension or regular array + if (this.options.ecmaVersion >= 7 && this.type === tt._for) { + return this.parseComprehension(node, false); + } + node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos); + return this.finishNode(node, "ArrayExpression"); + + case tt.braceL: + return this.parseObj(false, refShorthandDefaultPos); + + case tt._function: + node = this.startNode(); + this.next(); + return this.parseFunction(node, false); + + case tt._class: + return this.parseClass(this.startNode(), false); + + case tt._new: + return this.parseNew(); + + case tt.backQuote: + return this.parseTemplate(); + + default: + this.unexpected(); + } + }; + + pp.parseLiteral = function (value) { + var node = this.startNode(); + node.value = value; + node.raw = this.input.slice(this.start, this.end); + this.next(); + return this.finishNode(node, "Literal"); + }; + + pp.parseParenExpression = function () { + this.expect(tt.parenL); + var val = this.parseExpression(); + this.expect(tt.parenR); + return val; + }; + + pp.parseParenAndDistinguishExpression = function (canBeArrow) { + var startPos = this.start, + startLoc = this.startLoc, + val = undefined; + if (this.options.ecmaVersion >= 6) { + this.next(); + + if (this.options.ecmaVersion >= 7 && this.type === tt._for) { + return this.parseComprehension(this.startNodeAt(startPos, startLoc), true); + } + + var innerStartPos = this.start, + innerStartLoc = this.startLoc; + var exprList = [], + first = true; + var refShorthandDefaultPos = { start: 0 }, + spreadStart = undefined, + innerParenStart = undefined; + while (this.type !== tt.parenR) { + first ? first = false : this.expect(tt.comma); + if (this.type === tt.ellipsis) { + spreadStart = this.start; + exprList.push(this.parseParenItem(this.parseRest())); + break; + } else { + if (this.type === tt.parenL && !innerParenStart) { + innerParenStart = this.start; + } + exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem)); + } + } + var innerEndPos = this.start, + innerEndLoc = this.startLoc; + this.expect(tt.parenR); + + if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) { + if (innerParenStart) this.unexpected(innerParenStart); + return this.parseParenArrowList(startPos, startLoc, exprList); + } + + if (!exprList.length) this.unexpected(this.lastTokStart); + if (spreadStart) this.unexpected(spreadStart); + if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start); + + if (exprList.length > 1) { + val = this.startNodeAt(innerStartPos, innerStartLoc); + val.expressions = exprList; + this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc); + } else { + val = exprList[0]; + } + } else { + val = this.parseParenExpression(); + } + + if (this.options.preserveParens) { + var par = this.startNodeAt(startPos, startLoc); + par.expression = val; + return this.finishNode(par, "ParenthesizedExpression"); + } else { + return val; + } + }; + + pp.parseParenItem = function (item) { + return item; + }; + + pp.parseParenArrowList = function (startPos, startLoc, exprList) { + return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList); + }; + + // New's precedence is slightly tricky. It must allow its argument + // to be a `[]` or dot subscript expression, but not a call — at + // least, not without wrapping it in parentheses. Thus, it uses the + + var empty = []; + + pp.parseNew = function () { + var node = this.startNode(); + var meta = this.parseIdent(true); + if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) { + node.meta = meta; + node.property = this.parseIdent(true); + if (node.property.name !== "target") this.raise(node.property.start, "The only valid meta property for new is new.target"); + return this.finishNode(node, "MetaProperty"); + } + var startPos = this.start, + startLoc = this.startLoc; + node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true); + if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false); else node.arguments = empty; + return this.finishNode(node, "NewExpression"); + }; + + // Parse template expression. + + pp.parseTemplateElement = function () { + var elem = this.startNode(); + elem.value = { + raw: this.input.slice(this.start, this.end), + cooked: this.value + }; + this.next(); + elem.tail = this.type === tt.backQuote; + return this.finishNode(elem, "TemplateElement"); + }; + + pp.parseTemplate = function () { + var node = this.startNode(); + this.next(); + node.expressions = []; + var curElt = this.parseTemplateElement(); + node.quasis = [curElt]; + while (!curElt.tail) { + this.expect(tt.dollarBraceL); + node.expressions.push(this.parseExpression()); + this.expect(tt.braceR); + node.quasis.push(curElt = this.parseTemplateElement()); + } + this.next(); + return this.finishNode(node, "TemplateLiteral"); + }; + + // Parse an object literal or binding pattern. + + pp.parseObj = function (isPattern, refShorthandDefaultPos) { + var node = this.startNode(), + first = true, + propHash = {}; + node.properties = []; + this.next(); + while (!this.eat(tt.braceR)) { + if (!first) { + this.expect(tt.comma); + if (this.afterTrailingComma(tt.braceR)) break; + } else first = false; + + var prop = this.startNode(), + isGenerator = undefined, + startPos = undefined, + startLoc = undefined; + if (this.options.ecmaVersion >= 6) { + prop.method = false; + prop.shorthand = false; + if (isPattern || refShorthandDefaultPos) { + startPos = this.start; + startLoc = this.startLoc; + } + if (!isPattern) isGenerator = this.eat(tt.star); + } + this.parsePropertyName(prop); + this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos); + this.checkPropClash(prop, propHash); + node.properties.push(this.finishNode(prop, "Property")); + } + return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression"); + }; + + pp.parsePropertyValue = function (prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos) { + if (this.eat(tt.colon)) { + prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos); + prop.kind = "init"; + } else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) { + if (isPattern) this.unexpected(); + prop.kind = "init"; + prop.method = true; + prop.value = this.parseMethod(isGenerator); + } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && (this.type != tt.comma && this.type != tt.braceR)) { + if (isGenerator || isPattern) this.unexpected(); + prop.kind = prop.key.name; + this.parsePropertyName(prop); + prop.value = this.parseMethod(false); + } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") { + prop.kind = "init"; + if (isPattern) { + if (this.isKeyword(prop.key.name) || this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name)) || !this.options.allowReserved && this.isReservedWord(prop.key.name)) this.raise(prop.key.start, "Binding " + prop.key.name); + prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); + } else if (this.type === tt.eq && refShorthandDefaultPos) { + if (!refShorthandDefaultPos.start) refShorthandDefaultPos.start = this.start; + prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); + } else { + prop.value = prop.key; + } + prop.shorthand = true; + } else this.unexpected(); + }; + + pp.parsePropertyName = function (prop) { + if (this.options.ecmaVersion >= 6) { + if (this.eat(tt.bracketL)) { + prop.computed = true; + prop.key = this.parseMaybeAssign(); + this.expect(tt.bracketR); + return prop.key; + } else { + prop.computed = false; + } + } + return prop.key = this.type === tt.num || this.type === tt.string ? this.parseExprAtom() : this.parseIdent(true); + }; + + // Initialize empty function node. + + pp.initFunction = function (node) { + node.id = null; + if (this.options.ecmaVersion >= 6) { + node.generator = false; + node.expression = false; + } + }; + + // Parse object or class method. + + pp.parseMethod = function (isGenerator) { + var node = this.startNode(); + this.initFunction(node); + this.expect(tt.parenL); + node.params = this.parseBindingList(tt.parenR, false, false); + var allowExpressionBody = undefined; + if (this.options.ecmaVersion >= 6) { + node.generator = isGenerator; + allowExpressionBody = true; + } else { + allowExpressionBody = false; + } + this.parseFunctionBody(node, allowExpressionBody); + return this.finishNode(node, "FunctionExpression"); + }; + + // Parse arrow function expression with given parameters. + + pp.parseArrowExpression = function (node, params) { + this.initFunction(node); + node.params = this.toAssignableList(params, true); + this.parseFunctionBody(node, true); + return this.finishNode(node, "ArrowFunctionExpression"); + }; + + // Parse function body and check parameters. + + pp.parseFunctionBody = function (node, allowExpression) { + var isExpression = allowExpression && this.type !== tt.braceL; + + if (isExpression) { + node.body = this.parseMaybeAssign(); + node.expression = true; + } else { + // Start a new scope with regard to labels and the `inFunction` + // flag (restore them to their old value afterwards). + var oldInFunc = this.inFunction, + oldInGen = this.inGenerator, + oldLabels = this.labels; + this.inFunction = true; this.inGenerator = node.generator; this.labels = []; + node.body = this.parseBlock(true); + node.expression = false; + this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels; + } + + // If this is a strict mode function, verify that argument names + // are not repeated, and it does not try to bind the words `eval` + // or `arguments`. + if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) { + var nameHash = {}, + oldStrict = this.strict; + this.strict = true; + if (node.id) this.checkLVal(node.id, true); + for (var i = 0; i < node.params.length; i++) { + this.checkLVal(node.params[i], true, nameHash); + } this.strict = oldStrict; + } + }; + + // Parses a comma-separated list of expressions, and returns them as + // an array. `close` is the token type that ends the list, and + // `allowEmpty` can be turned on to allow subsequent commas with + // nothing in between them to be parsed as `null` (which is needed + // for array literals). + + pp.parseExprList = function (close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) { + var elts = [], + first = true; + while (!this.eat(close)) { + if (!first) { + this.expect(tt.comma); + if (allowTrailingComma && this.afterTrailingComma(close)) break; + } else first = false; + + if (allowEmpty && this.type === tt.comma) { + elts.push(null); + } else { + if (this.type === tt.ellipsis) elts.push(this.parseSpread(refShorthandDefaultPos)); else elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos)); + } + } + return elts; + }; + + // Parse the next token as an identifier. If `liberal` is true (used + // when parsing properties), it will also convert keywords into + // identifiers. + + pp.parseIdent = function (liberal) { + var node = this.startNode(); + if (liberal && this.options.allowReserved == "never") liberal = false; + if (this.type === tt.name) { + if (!liberal && (!this.options.allowReserved && this.isReservedWord(this.value) || this.strict && reservedWords.strict(this.value) && (this.options.ecmaVersion >= 6 || this.input.slice(this.start, this.end).indexOf("\\") == -1))) this.raise(this.start, "The keyword '" + this.value + "' is reserved"); + node.name = this.value; + } else if (liberal && this.type.keyword) { + node.name = this.type.keyword; + } else { + this.unexpected(); + } + this.next(); + return this.finishNode(node, "Identifier"); + }; + + // Parses yield expression inside generator. + + pp.parseYield = function () { + var node = this.startNode(); + this.next(); + if (this.type == tt.semi || this.canInsertSemicolon() || this.type != tt.star && !this.type.startsExpr) { + node.delegate = false; + node.argument = null; + } else { + node.delegate = this.eat(tt.star); + node.argument = this.parseMaybeAssign(); + } + return this.finishNode(node, "YieldExpression"); + }; + + // Parses array and generator comprehensions. + + pp.parseComprehension = function (node, isGenerator) { + node.blocks = []; + while (this.type === tt._for) { + var block = this.startNode(); + this.next(); + this.expect(tt.parenL); + block.left = this.parseBindingAtom(); + this.checkLVal(block.left, true); + this.expectContextual("of"); + block.right = this.parseExpression(); + this.expect(tt.parenR); + node.blocks.push(this.finishNode(block, "ComprehensionBlock")); + } + node.filter = this.eat(tt._if) ? this.parseParenExpression() : null; + node.body = this.parseExpression(); + this.expect(isGenerator ? tt.parenR : tt.bracketR); + node.generator = isGenerator; + return this.finishNode(node, "ComprehensionExpression"); + }; + + }, { "./identifier": 7, "./state": 13, "./tokentype": 17, "./util": 18 }], 7: [function (_dereq_, module, exports) { + + + // Test whether a given character code starts an identifier. + + "use strict"; + + exports.isIdentifierStart = isIdentifierStart; + + // Test whether a given character is part of an identifier. + + exports.isIdentifierChar = isIdentifierChar; + exports.__esModule = true; + // This is a trick taken from Esprima. It turns out that, on + // non-Chrome browsers, to check whether a string is in a set, a + // predicate containing a big ugly `switch` statement is faster than + // a regular expression, and on Chrome the two are about on par. + // This function uses `eval` (non-lexical) to produce such a + // predicate from a space-separated string of words. + // + // It starts by sorting the words by length. + + function makePredicate(words) { + words = words.split(" "); + var f = "", + cats = []; + out: for (var i = 0; i < words.length; ++i) { + for (var j = 0; j < cats.length; ++j) { + if (cats[j][0].length == words[i].length) { + cats[j].push(words[i]); + continue out; + } + } cats.push([words[i]]); + } + function compareTo(arr) { + if (arr.length == 1) { + return f += "return str === " + JSON.stringify(arr[0]) + ";"; + } f += "switch(str){"; + for (var i = 0; i < arr.length; ++i) { + f += "case " + JSON.stringify(arr[i]) + ":"; + } f += "return true}return false;"; + } + + // When there are more than three length categories, an outer + // switch first dispatches on the lengths, to save on comparisons. + + if (cats.length > 3) { + cats.sort(function (a, b) { + return b.length - a.length; + }); + f += "switch(str.length){"; + for (var i = 0; i < cats.length; ++i) { + var cat = cats[i]; + f += "case " + cat[0].length + ":"; + compareTo(cat); + } + f += "}" + + // Otherwise, simply generate a flat `switch` statement. + + ; + } else { + compareTo(words); + } + return new Function("str", f); + } + + // Reserved word lists for various dialects of the language + + var reservedWords = { + 3: makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"), + 5: makePredicate("class enum extends super const export import"), + 6: makePredicate("enum await"), + strict: makePredicate("implements interface let package private protected public static yield"), + strictBind: makePredicate("eval arguments") + }; + + exports.reservedWords = reservedWords; + // And the keywords + + var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"; + + var keywords = { + 5: makePredicate(ecma5AndLessKeywords), + 6: makePredicate(ecma5AndLessKeywords + " let const class extends export import yield super") + }; + + exports.keywords = keywords; + // ## Character categories + + // Big ugly regular expressions that match characters in the + // whitespace, identifier, and identifier-start categories. These + // are only applied when a character is found to actually have a + // code point above 128. + // Generated by `tools/generate-identifier-regex.js`. + + var nonASCIIidentifierStartChars = "ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙա-ևא-תװ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠ-ࢲऄ-हऽॐक़-ॡॱ-ঀঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ々-〇〡-〩〱-〵〸-〼ぁ-ゖ゛-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚝꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞭꞰꞱꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꩾ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭟꭤꭥꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ"; + var nonASCIIidentifierChars = "‌‍·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-٩ٰۖ-ۜ۟-۪ۤۧۨ-ۭ۰-۹ܑܰ-݊ަ-ް߀-߉߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣤ-ःऺ-़ा-ॏ॑-ॗॢॣ०-९ঁ-ঃ়া-ৄেৈো-্ৗৢৣ০-৯ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑ੦-ੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣ૦-૯ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣ୦-୯ஂா-ூெ-ைொ-்ௗ௦-௯ఀ-ఃా-ౄె-ైొ-్ౕౖౢౣ౦-౯ಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣ೦-೯ഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣ൦-൯ංඃ්ා-ුූෘ-ෟ෦-෯ෲෳัิ-ฺ็-๎๐-๙ັິ-ູົຼ່-ໍ໐-໙༘༙༠-༩༹༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှ၀-၉ၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏ-ႝ፝-፟፩-፱ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝០-៩᠋-᠍᠐-᠙ᢩᤠ-ᤫᤰ-᤻᥆-᥏ᦰ-ᧀᧈᧉ᧐-᧚ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼-᪉᪐-᪙᪰-᪽ᬀ-ᬄ᬴-᭄᭐-᭙᭫-᭳ᮀ-ᮂᮡ-ᮭ᮰-᮹᯦-᯳ᰤ-᰷᱀-᱉᱐-᱙᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷼-᷿‿⁀⁔⃐-⃥⃜⃡-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꘠-꘩꙯ꙴ-꙽ꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-꣄꣐-꣙꣠-꣱꤀-꤉ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀꧐-꧙ꧥ꧰-꧹ꨩ-ꨶꩃꩌꩍ꩐-꩙ꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭꯰-꯹ﬞ︀-️︠-︭︳︴﹍-﹏0-9_"; + + var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); + var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); + + nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; + + // These are a run-length and offset encoded representation of the + // >0xffff code points that are a valid part of identifiers. The + // offset starts at 0x10000, and each pair of numbers represents an + // offset to the next range, and then a size of the range. They were + // generated by tools/generate-identifier-regex.js + var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 17, 26, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 99, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 98, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 26, 45, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 955, 52, 76, 44, 33, 24, 27, 35, 42, 34, 4, 0, 13, 47, 15, 3, 22, 0, 38, 17, 2, 24, 133, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 32, 4, 287, 47, 21, 1, 2, 0, 185, 46, 82, 47, 21, 0, 60, 42, 502, 63, 32, 0, 449, 56, 1288, 920, 104, 110, 2962, 1070, 13266, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 881, 68, 12, 0, 67, 12, 16481, 1, 3071, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 4149, 196, 1340, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42710, 42, 4148, 12, 221, 16355, 541]; + var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 1306, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 52, 0, 13, 2, 49, 13, 16, 9, 83, 11, 168, 11, 6, 9, 8, 2, 57, 0, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 316, 19, 13, 9, 214, 6, 3, 8, 112, 16, 16, 9, 82, 12, 9, 9, 535, 9, 20855, 9, 135, 4, 60, 6, 26, 9, 1016, 45, 17, 3, 19723, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 4305, 6, 792618, 239]; + + // This has a complexity linear to the value of the code. The + // assumption is that looking up astral identifier characters is + // rare. + function isInAstralSet(code, set) { + var pos = 65536; + for (var i = 0; i < set.length; i += 2) { + pos += set[i]; + if (pos > code) { + return false; + } pos += set[i + 1]; + if (pos >= code) { + return true; + } + } + } + function isIdentifierStart(code, astral) { + if (code < 65) { + return code === 36; + } if (code < 91) { + return true; + } if (code < 97) { + return code === 95; + } if (code < 123) { + return true; + } if (code <= 65535) { + return code >= 170 && nonASCIIidentifierStart.test(String.fromCharCode(code)); + } if (astral === false) { + return false; + } return isInAstralSet(code, astralIdentifierStartCodes); + } + + function isIdentifierChar(code, astral) { + if (code < 48) { + return code === 36; + } if (code < 58) { + return true; + } if (code < 65) { + return false; + } if (code < 91) { + return true; + } if (code < 97) { + return code === 95; + } if (code < 123) { + return true; + } if (code <= 65535) { + return code >= 170 && nonASCIIidentifier.test(String.fromCharCode(code)); + } if (astral === false) { + return false; + } return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes); + } + + }, {}], 8: [function (_dereq_, module, exports) { + "use strict"; + + var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + + // The `getLineInfo` function is mostly useful when the + // `locations` option is off (for performance reasons) and you + // want to find the line/column position for a given character + // offset. `input` should be the code string that the offset refers + // into. + + exports.getLineInfo = getLineInfo; + exports.__esModule = true; + + var Parser = _dereq_("./state").Parser; + + var lineBreakG = _dereq_("./whitespace").lineBreakG; + + var deprecate = _dereq_("util").deprecate; + + // These are used when `options.locations` is on, for the + // `startLoc` and `endLoc` properties. + + var Position = exports.Position = (function () { + function Position(line, col) { + _classCallCheck(this, Position); + + this.line = line; + this.column = col; + } + + Position.prototype.offset = function offset(n) { + return new Position(this.line, this.column + n); + }; + + return Position; + })(); + + var SourceLocation = exports.SourceLocation = function SourceLocation(p, start, end) { + _classCallCheck(this, SourceLocation); + + this.start = start; + this.end = end; + if (p.sourceFile !== null) this.source = p.sourceFile; + }; + + function getLineInfo(input, offset) { + for (var line = 1, cur = 0; ;) { + lineBreakG.lastIndex = cur; + var match = lineBreakG.exec(input); + if (match && match.index < offset) { + ++line; + cur = match.index + match[0].length; + } else { + return new Position(line, offset - cur); + } + } + } + + var pp = Parser.prototype; + + // This function is used to raise exceptions on parse errors. It + // takes an offset integer (into the current `input`) to indicate + // the location of the error, attaches the position to the end + // of the error message, and then raises a `SyntaxError` with that + // message. + + pp.raise = function (pos, message) { + var loc = getLineInfo(this.input, pos); + message += " (" + loc.line + ":" + loc.column + ")"; + var err = new SyntaxError(message); + err.pos = pos; err.loc = loc; err.raisedAt = this.pos; + throw err; + }; + + pp.curPosition = function () { + return new Position(this.curLine, this.pos - this.lineStart); + }; + + pp.markPosition = function () { + return this.options.locations ? [this.start, this.startLoc] : this.start; + }; + + }, { "./state": 13, "./whitespace": 19, "util": 5 }], 9: [function (_dereq_, module, exports) { + "use strict"; + + var tt = _dereq_("./tokentype").types; + + var Parser = _dereq_("./state").Parser; + + var reservedWords = _dereq_("./identifier").reservedWords; + + var has = _dereq_("./util").has; + + var pp = Parser.prototype; + + // Convert existing expression atom to assignable pattern + // if possible. + + pp.toAssignable = function (node, isBinding) { + if (this.options.ecmaVersion >= 6 && node) { + switch (node.type) { + case "Identifier": + case "ObjectPattern": + case "ArrayPattern": + case "AssignmentPattern": + break; + + case "ObjectExpression": + node.type = "ObjectPattern"; + for (var i = 0; i < node.properties.length; i++) { + var prop = node.properties[i]; + if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter"); + this.toAssignable(prop.value, isBinding); + } + break; + + case "ArrayExpression": + node.type = "ArrayPattern"; + this.toAssignableList(node.elements, isBinding); + break; + + case "AssignmentExpression": + if (node.operator === "=") { + node.type = "AssignmentPattern"; + } else { + this.raise(node.left.end, "Only '=' operator can be used for specifying default value."); + } + break; + + case "ParenthesizedExpression": + node.expression = this.toAssignable(node.expression, isBinding); + break; + + case "MemberExpression": + if (!isBinding) break; + + default: + this.raise(node.start, "Assigning to rvalue"); + } + } + return node; + }; + + // Convert list of expression atoms to binding list. + + pp.toAssignableList = function (exprList, isBinding) { + var end = exprList.length; + if (end) { + var last = exprList[end - 1]; + if (last && last.type == "RestElement") { + --end; + } else if (last && last.type == "SpreadElement") { + last.type = "RestElement"; + var arg = last.argument; + this.toAssignable(arg, isBinding); + if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") this.unexpected(arg.start); + --end; + } + } + for (var i = 0; i < end; i++) { + var elt = exprList[i]; + if (elt) this.toAssignable(elt, isBinding); + } + return exprList; + }; + + // Parses spread element. + + pp.parseSpread = function (refShorthandDefaultPos) { + var node = this.startNode(); + this.next(); + node.argument = this.parseMaybeAssign(refShorthandDefaultPos); + return this.finishNode(node, "SpreadElement"); + }; + + pp.parseRest = function () { + var node = this.startNode(); + this.next(); + node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected(); + return this.finishNode(node, "RestElement"); + }; + + // Parses lvalue (assignable) atom. + + pp.parseBindingAtom = function () { + if (this.options.ecmaVersion < 6) return this.parseIdent(); + switch (this.type) { + case tt.name: + return this.parseIdent(); + + case tt.bracketL: + var node = this.startNode(); + this.next(); + node.elements = this.parseBindingList(tt.bracketR, true, true); + return this.finishNode(node, "ArrayPattern"); + + case tt.braceL: + return this.parseObj(true); + + default: + this.unexpected(); + } + }; + + pp.parseBindingList = function (close, allowEmpty, allowTrailingComma) { + var elts = [], + first = true; + while (!this.eat(close)) { + if (first) first = false; else this.expect(tt.comma); + if (allowEmpty && this.type === tt.comma) { + elts.push(null); + } else if (allowTrailingComma && this.afterTrailingComma(close)) { + break; + } else if (this.type === tt.ellipsis) { + var rest = this.parseRest(); + this.parseBindingListItem(rest); + elts.push(rest); + this.expect(close); + break; + } else { + var elem = this.parseMaybeDefault(this.start, this.startLoc); + this.parseBindingListItem(elem); + elts.push(elem); + } + } + return elts; + }; + + pp.parseBindingListItem = function (param) { + return param; + }; + + // Parses assignment pattern around given atom if possible. + + pp.parseMaybeDefault = function (startPos, startLoc, left) { + if (Array.isArray(startPos)) { + if (this.options.locations && noCalls === undefined) { + // shift arguments to left by one + left = startLoc; + // flatten startPos + startLoc = startPos[1]; + startPos = startPos[0]; + } + } + left = left || this.parseBindingAtom(); + if (!this.eat(tt.eq)) return left; + var node = this.startNodeAt(startPos, startLoc); + node.operator = "="; + node.left = left; + node.right = this.parseMaybeAssign(); + return this.finishNode(node, "AssignmentPattern"); + }; + + // Verify that a node is an lval — something that can be assigned + // to. + + pp.checkLVal = function (expr, isBinding, checkClashes) { + switch (expr.type) { + case "Identifier": + if (this.strict && (reservedWords.strictBind(expr.name) || reservedWords.strict(expr.name))) this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode"); + if (checkClashes) { + if (has(checkClashes, expr.name)) this.raise(expr.start, "Argument name clash in strict mode"); + checkClashes[expr.name] = true; + } + break; + + case "MemberExpression": + if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression"); + break; + + case "ObjectPattern": + for (var i = 0; i < expr.properties.length; i++) { + this.checkLVal(expr.properties[i].value, isBinding, checkClashes); + } break; + + case "ArrayPattern": + for (var i = 0; i < expr.elements.length; i++) { + var elem = expr.elements[i]; + if (elem) this.checkLVal(elem, isBinding, checkClashes); + } + break; + + case "AssignmentPattern": + this.checkLVal(expr.left, isBinding, checkClashes); + break; + + case "RestElement": + this.checkLVal(expr.argument, isBinding, checkClashes); + break; + + case "ParenthesizedExpression": + this.checkLVal(expr.expression, isBinding, checkClashes); + break; + + default: + this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue"); + } + }; + + }, { "./identifier": 7, "./state": 13, "./tokentype": 17, "./util": 18 }], 10: [function (_dereq_, module, exports) { + "use strict"; + + var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + + exports.__esModule = true; + + var Parser = _dereq_("./state").Parser; + + var SourceLocation = _dereq_("./location").SourceLocation; + + // Start an AST node, attaching a start offset. + + var pp = Parser.prototype; + + var Node = exports.Node = function Node() { + _classCallCheck(this, Node); + }; + + pp.startNode = function () { + var node = new Node(); + node.start = this.start; + if (this.options.locations) node.loc = new SourceLocation(this, this.startLoc); + if (this.options.directSourceFile) node.sourceFile = this.options.directSourceFile; + if (this.options.ranges) node.range = [this.start, 0]; + return node; + }; + + pp.startNodeAt = function (pos, loc) { + var node = new Node(); + if (Array.isArray(pos)) { + if (this.options.locations && loc === undefined) { + // flatten pos + loc = pos[1]; + pos = pos[0]; + } + } + node.start = pos; + if (this.options.locations) node.loc = new SourceLocation(this, loc); + if (this.options.directSourceFile) node.sourceFile = this.options.directSourceFile; + if (this.options.ranges) node.range = [pos, 0]; + return node; + }; + + // Finish an AST node, adding `type` and `end` properties. + + pp.finishNode = function (node, type) { + node.type = type; + node.end = this.lastTokEnd; + if (this.options.locations) node.loc.end = this.lastTokEndLoc; + if (this.options.ranges) node.range[1] = this.lastTokEnd; + return node; + }; + + // Finish node at given position + + pp.finishNodeAt = function (node, type, pos, loc) { + node.type = type; + if (Array.isArray(pos)) { + if (this.options.locations && loc === undefined) { + // flatten pos + loc = pos[1]; + pos = pos[0]; + } + } + node.end = pos; + if (this.options.locations) node.loc.end = loc; + if (this.options.ranges) node.range[1] = pos; + return node; + }; + + }, { "./location": 8, "./state": 13 }], 11: [function (_dereq_, module, exports) { + + + // Interpret and default an options object + + "use strict"; + + exports.getOptions = getOptions; + exports.__esModule = true; + + var _util = _dereq_("./util"); + + var has = _util.has; + var isArray = _util.isArray; + + var SourceLocation = _dereq_("./location").SourceLocation; + + // A second optional argument can be given to further configure + // the parser process. These options are recognized: + + var defaultOptions = { + // `ecmaVersion` indicates the ECMAScript version to parse. Must + // be either 3, or 5, or 6. This influences support for strict + // mode, the set of reserved words, support for getters and + // setters and other features. + ecmaVersion: 5, + // Source type ("script" or "module") for different semantics + sourceType: "script", + // `onInsertedSemicolon` can be a callback that will be called + // when a semicolon is automatically inserted. It will be passed + // th position of the comma as an offset, and if `locations` is + // enabled, it is given the location as a `{line, column}` object + // as second argument. + onInsertedSemicolon: null, + // `onTrailingComma` is similar to `onInsertedSemicolon`, but for + // trailing commas. + onTrailingComma: null, + // By default, reserved words are not enforced. Disable + // `allowReserved` to enforce them. When this option has the + // value "never", reserved words and keywords can also not be + // used as property names. + allowReserved: true, + // When enabled, a return at the top level is not considered an + // error. + allowReturnOutsideFunction: false, + // When enabled, import/export statements are not constrained to + // appearing at the top of the program. + allowImportExportEverywhere: false, + // When enabled, hashbang directive in the beginning of file + // is allowed and treated as a line comment. + allowHashBang: false, + // When `locations` is on, `loc` properties holding objects with + // `start` and `end` properties in `{line, column}` form (with + // line being 1-based and column 0-based) will be attached to the + // nodes. + locations: false, + // A function can be passed as `onToken` option, which will + // cause Acorn to call that function with object in the same + // format as tokenize() returns. Note that you are not + // allowed to call the parser from the callback—that will + // corrupt its internal state. + onToken: null, + // A function can be passed as `onComment` option, which will + // cause Acorn to call that function with `(block, text, start, + // end)` parameters whenever a comment is skipped. `block` is a + // boolean indicating whether this is a block (`/* */`) comment, + // `text` is the content of the comment, and `start` and `end` are + // character offsets that denote the start and end of the comment. + // When the `locations` option is on, two more parameters are + // passed, the full `{line, column}` locations of the start and + // end of the comments. Note that you are not allowed to call the + // parser from the callback—that will corrupt its internal state. + onComment: null, + // Nodes have their start and end characters offsets recorded in + // `start` and `end` properties (directly on the node, rather than + // the `loc` object, which holds line/column data. To also add a + // [semi-standardized][range] `range` property holding a `[start, + // end]` array with the same numbers, set the `ranges` option to + // `true`. + // + // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678 + ranges: false, + // It is possible to parse multiple files into a single AST by + // passing the tree produced by parsing the first file as + // `program` option in subsequent parses. This will add the + // toplevel forms of the parsed file to the `Program` (top) node + // of an existing parse tree. + program: null, + // When `locations` is on, you can pass this to record the source + // file in every node's `loc` object. + sourceFile: null, + // This value, if given, is stored in every node, whether + // `locations` is on or off. + directSourceFile: null, + // When enabled, parenthesized expressions are represented by + // (non-standard) ParenthesizedExpression nodes + preserveParens: false, + plugins: {} + }; exports.defaultOptions = defaultOptions; + + function getOptions(opts) { + var options = {}; + for (var opt in defaultOptions) { + options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; + } if (isArray(options.onToken)) { + (function () { + var tokens = options.onToken; + options.onToken = function (token) { + return tokens.push(token); + }; + })(); + } + if (isArray(options.onComment)) options.onComment = pushComment(options, options.onComment); + + return options; + } + + function pushComment(options, array) { + return function (block, text, start, end, startLoc, endLoc) { + var comment = { + type: block ? "Block" : "Line", + value: text, + start: start, + end: end + }; + if (options.locations) comment.loc = new SourceLocation(this, startLoc, endLoc); + if (options.ranges) comment.range = [start, end]; + array.push(comment); + }; + } + + }, { "./location": 8, "./util": 18 }], 12: [function (_dereq_, module, exports) { + "use strict"; + + var tt = _dereq_("./tokentype").types; + + var Parser = _dereq_("./state").Parser; + + var lineBreak = _dereq_("./whitespace").lineBreak; + + var pp = Parser.prototype; + + // ## Parser utilities + + // Test whether a statement node is the string literal `"use strict"`. + + pp.isUseStrict = function (stmt) { + return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && stmt.expression.type === "Literal" && stmt.expression.value === "use strict"; + }; + + // Predicate that tests whether the next token is of the given + // type, and if yes, consumes it as a side effect. + + pp.eat = function (type) { + if (this.type === type) { + this.next(); + return true; + } else { + return false; + } + }; + + // Tests whether parsed token is a contextual keyword. + + pp.isContextual = function (name) { + return this.type === tt.name && this.value === name; + }; + + // Consumes contextual keyword if possible. + + pp.eatContextual = function (name) { + return this.value === name && this.eat(tt.name); + }; + + // Asserts that following token is given contextual keyword. + + pp.expectContextual = function (name) { + if (!this.eatContextual(name)) this.unexpected(); + }; + + // Test whether a semicolon can be inserted at the current position. + + pp.canInsertSemicolon = function () { + return this.type === tt.eof || this.type === tt.braceR || lineBreak.test(this.input.slice(this.lastTokEnd, this.start)); + }; + + pp.insertSemicolon = function () { + if (this.canInsertSemicolon()) { + if (this.options.onInsertedSemicolon) this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); + return true; + } + }; + + // Consume a semicolon, or, failing that, see if we are allowed to + // pretend that there is a semicolon at this position. + + pp.semicolon = function () { + if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected(); + }; + + pp.afterTrailingComma = function (tokType) { + if (this.type == tokType) { + if (this.options.onTrailingComma) this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); + this.next(); + return true; + } + }; + + // Expect a token of a given type. If found, consume it, otherwise, + // raise an unexpected token error. + + pp.expect = function (type) { + this.eat(type) || this.unexpected(); + }; + + // Raise an unexpected token error. + + pp.unexpected = function (pos) { + this.raise(pos != null ? pos : this.start, "Unexpected token"); + }; + + }, { "./state": 13, "./tokentype": 17, "./whitespace": 19 }], 13: [function (_dereq_, module, exports) { + "use strict"; + + exports.Parser = Parser; + exports.__esModule = true; + + var _identifier = _dereq_("./identifier"); + + var reservedWords = _identifier.reservedWords; + var keywords = _identifier.keywords; + + var tt = _dereq_("./tokentype").types; + + var lineBreak = _dereq_("./whitespace").lineBreak; + + function Parser(options, input, startPos) { + this.options = options; + this.sourceFile = this.options.sourceFile || null; + this.isKeyword = keywords[this.options.ecmaVersion >= 6 ? 6 : 5]; + this.isReservedWord = reservedWords[this.options.ecmaVersion]; + this.input = input; + + // Load plugins + this.loadPlugins(this.options.plugins); + + // Set up token state + + // The current position of the tokenizer in the input. + if (startPos) { + this.pos = startPos; + this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos)); + this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length; + } else { + this.pos = this.lineStart = 0; + this.curLine = 1; + } + + // Properties of the current token: + // Its type + this.type = tt.eof; + // For tokens that include more information than their type, the value + this.value = null; + // Its start and end offset + this.start = this.end = this.pos; + // And, if locations are used, the {line, column} object + // corresponding to those offsets + this.startLoc = this.endLoc = null; + + // Position information for the previous token + this.lastTokEndLoc = this.lastTokStartLoc = null; + this.lastTokStart = this.lastTokEnd = this.pos; + + // The context stack is used to superficially track syntactic + // context to predict whether a regular expression is allowed in a + // given position. + this.context = this.initialContext(); + this.exprAllowed = true; + + // Figure out if it's a module code. + this.strict = this.inModule = this.options.sourceType === "module"; + + // Used to signify the start of a potential arrow function + this.potentialArrowAt = -1; + + // Flags to track whether we are in a function, a generator. + this.inFunction = this.inGenerator = false; + // Labels in scope. + this.labels = []; + + // If enabled, skip leading hashbang line. + if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === "#!") this.skipLineComment(2); + } + + Parser.prototype.extend = function (name, f) { + this[name] = f(this[name]); + }; + + // Registered plugins + + var plugins = {}; + + exports.plugins = plugins; + Parser.prototype.loadPlugins = function (plugins) { + for (var _name in plugins) { + var plugin = exports.plugins[_name]; + if (!plugin) throw new Error("Plugin '" + _name + "' not found"); + plugin(this, plugins[_name]); + } + }; + + }, { "./identifier": 7, "./tokentype": 17, "./whitespace": 19 }], 14: [function (_dereq_, module, exports) { + "use strict"; + + var tt = _dereq_("./tokentype").types; + + var Parser = _dereq_("./state").Parser; + + var lineBreak = _dereq_("./whitespace").lineBreak; + + var pp = Parser.prototype; + + // ### Statement parsing + + // Parse a program. Initializes the parser, reads any number of + // statements, and wraps them in a Program node. Optionally takes a + // `program` argument. If present, the statements will be appended + // to its body instead of creating a new node. + + pp.parseTopLevel = function (node) { + var first = true; + if (!node.body) node.body = []; + while (this.type !== tt.eof) { + var stmt = this.parseStatement(true, true); + node.body.push(stmt); + if (first && this.isUseStrict(stmt)) this.setStrict(true); + first = false; + } + this.next(); + if (this.options.ecmaVersion >= 6) { + node.sourceType = this.options.sourceType; + } + return this.finishNode(node, "Program"); + }; + + var loopLabel = { kind: "loop" }, + switchLabel = { kind: "switch" }; + + // Parse a single statement. + // + // If expecting a statement and finding a slash operator, parse a + // regular expression literal. This is to handle cases like + // `if (foo) /blah/.exec(foo)`, where looking at the previous token + // does not help. + + pp.parseStatement = function (declaration, topLevel) { + var starttype = this.type, + node = this.startNode(); + + // Most types of statements are recognized by the keyword they + // start with. Many are trivial to parse, some require a bit of + // complexity. + + switch (starttype) { + case tt._break: case tt._continue: + return this.parseBreakContinueStatement(node, starttype.keyword); + case tt._debugger: + return this.parseDebuggerStatement(node); + case tt._do: + return this.parseDoStatement(node); + case tt._for: + return this.parseForStatement(node); + case tt._function: + if (!declaration && this.options.ecmaVersion >= 6) this.unexpected(); + return this.parseFunctionStatement(node); + case tt._class: + if (!declaration) this.unexpected(); + return this.parseClass(node, true); + case tt._if: + return this.parseIfStatement(node); + case tt._return: + return this.parseReturnStatement(node); + case tt._switch: + return this.parseSwitchStatement(node); + case tt._throw: + return this.parseThrowStatement(node); + case tt._try: + return this.parseTryStatement(node); + case tt._let: case tt._const: + if (!declaration) this.unexpected(); // NOTE: falls through to _var + case tt._var: + return this.parseVarStatement(node, starttype); + case tt._while: + return this.parseWhileStatement(node); + case tt._with: + return this.parseWithStatement(node); + case tt.braceL: + return this.parseBlock(); + case tt.semi: + return this.parseEmptyStatement(node); + case tt._export: + case tt._import: + if (!this.options.allowImportExportEverywhere) { + if (!topLevel) this.raise(this.start, "'import' and 'export' may only appear at the top level"); + if (!this.inModule) this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); + } + return starttype === tt._import ? this.parseImport(node) : this.parseExport(node); + + // If the statement does not start with a statement keyword or a + // brace, it's an ExpressionStatement or LabeledStatement. We + // simply start parsing an expression, and afterwards, if the + // next token is a colon and the expression was a simple + // Identifier node, we switch to interpreting it as a label. + default: + var maybeName = this.value, + expr = this.parseExpression(); + if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) return this.parseLabeledStatement(node, maybeName, expr); else return this.parseExpressionStatement(node, expr); + } + }; + + pp.parseBreakContinueStatement = function (node, keyword) { + var isBreak = keyword == "break"; + this.next(); + if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null; else if (this.type !== tt.name) this.unexpected(); else { + node.label = this.parseIdent(); + this.semicolon(); + } + + // Verify that there is an actual destination to break or + // continue to. + for (var i = 0; i < this.labels.length; ++i) { + var lab = this.labels[i]; + if (node.label == null || lab.name === node.label.name) { + if (lab.kind != null && (isBreak || lab.kind === "loop")) break; + if (node.label && isBreak) break; + } + } + if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword); + return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement"); + }; + + pp.parseDebuggerStatement = function (node) { + this.next(); + this.semicolon(); + return this.finishNode(node, "DebuggerStatement"); + }; + + pp.parseDoStatement = function (node) { + this.next(); + this.labels.push(loopLabel); + node.body = this.parseStatement(false); + this.labels.pop(); + this.expect(tt._while); + node.test = this.parseParenExpression(); + if (this.options.ecmaVersion >= 6) this.eat(tt.semi); else this.semicolon(); + return this.finishNode(node, "DoWhileStatement"); + }; + + // Disambiguating between a `for` and a `for`/`in` or `for`/`of` + // loop is non-trivial. Basically, we have to parse the init `var` + // statement or expression, disallowing the `in` operator (see + // the second parameter to `parseExpression`), and then check + // whether the next token is `in` or `of`. When there is no init + // part (semicolon immediately after the opening parenthesis), it + // is a regular `for` loop. + + pp.parseForStatement = function (node) { + this.next(); + this.labels.push(loopLabel); + this.expect(tt.parenL); + if (this.type === tt.semi) return this.parseFor(node, null); + if (this.type === tt._var || this.type === tt._let || this.type === tt._const) { + var _init = this.startNode(), + varKind = this.type; + this.next(); + this.parseVar(_init, true, varKind); + this.finishNode(_init, "VariableDeclaration"); + if ((this.type === tt._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) && _init.declarations.length === 1 && !(varKind !== tt._var && _init.declarations[0].init)) return this.parseForIn(node, _init); + return this.parseFor(node, _init); + } + var refShorthandDefaultPos = { start: 0 }; + var init = this.parseExpression(true, refShorthandDefaultPos); + if (this.type === tt._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) { + this.toAssignable(init); + this.checkLVal(init); + return this.parseForIn(node, init); + } else if (refShorthandDefaultPos.start) { + this.unexpected(refShorthandDefaultPos.start); + } + return this.parseFor(node, init); + }; + + pp.parseFunctionStatement = function (node) { + this.next(); + return this.parseFunction(node, true); + }; + + pp.parseIfStatement = function (node) { + this.next(); + node.test = this.parseParenExpression(); + node.consequent = this.parseStatement(false); + node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null; + return this.finishNode(node, "IfStatement"); + }; + + pp.parseReturnStatement = function (node) { + if (!this.inFunction && !this.options.allowReturnOutsideFunction) this.raise(this.start, "'return' outside of function"); + this.next(); + + // In `return` (and `break`/`continue`), the keywords with + // optional arguments, we eagerly look for a semicolon or the + // possibility to insert one. + + if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null; else { + node.argument = this.parseExpression(); this.semicolon(); + } + return this.finishNode(node, "ReturnStatement"); + }; + + pp.parseSwitchStatement = function (node) { + this.next(); + node.discriminant = this.parseParenExpression(); + node.cases = []; + this.expect(tt.braceL); + this.labels.push(switchLabel); + + // Statements under must be grouped (by label) in SwitchCase + // nodes. `cur` is used to keep the node that we are currently + // adding statements to. + + for (var cur, sawDefault; this.type != tt.braceR;) { + if (this.type === tt._case || this.type === tt._default) { + var isCase = this.type === tt._case; + if (cur) this.finishNode(cur, "SwitchCase"); + node.cases.push(cur = this.startNode()); + cur.consequent = []; + this.next(); + if (isCase) { + cur.test = this.parseExpression(); + } else { + if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses"); + sawDefault = true; + cur.test = null; + } + this.expect(tt.colon); + } else { + if (!cur) this.unexpected(); + cur.consequent.push(this.parseStatement(true)); + } + } + if (cur) this.finishNode(cur, "SwitchCase"); + this.next(); // Closing brace + this.labels.pop(); + return this.finishNode(node, "SwitchStatement"); + }; + + pp.parseThrowStatement = function (node) { + this.next(); + if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) this.raise(this.lastTokEnd, "Illegal newline after throw"); + node.argument = this.parseExpression(); + this.semicolon(); + return this.finishNode(node, "ThrowStatement"); + }; + + // Reused empty array added for node fields that are always empty. + + var empty = []; + + pp.parseTryStatement = function (node) { + this.next(); + node.block = this.parseBlock(); + node.handler = null; + if (this.type === tt._catch) { + var clause = this.startNode(); + this.next(); + this.expect(tt.parenL); + clause.param = this.parseBindingAtom(); + this.checkLVal(clause.param, true); + this.expect(tt.parenR); + clause.guard = null; + clause.body = this.parseBlock(); + node.handler = this.finishNode(clause, "CatchClause"); + } + node.guardedHandlers = empty; + node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null; + if (!node.handler && !node.finalizer) this.raise(node.start, "Missing catch or finally clause"); + return this.finishNode(node, "TryStatement"); + }; + + pp.parseVarStatement = function (node, kind) { + this.next(); + this.parseVar(node, false, kind); + this.semicolon(); + return this.finishNode(node, "VariableDeclaration"); + }; + + pp.parseWhileStatement = function (node) { + this.next(); + node.test = this.parseParenExpression(); + this.labels.push(loopLabel); + node.body = this.parseStatement(false); + this.labels.pop(); + return this.finishNode(node, "WhileStatement"); + }; + + pp.parseWithStatement = function (node) { + if (this.strict) this.raise(this.start, "'with' in strict mode"); + this.next(); + node.object = this.parseParenExpression(); + node.body = this.parseStatement(false); + return this.finishNode(node, "WithStatement"); + }; + + pp.parseEmptyStatement = function (node) { + this.next(); + return this.finishNode(node, "EmptyStatement"); + }; + + pp.parseLabeledStatement = function (node, maybeName, expr) { + for (var i = 0; i < this.labels.length; ++i) { + if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared"); + } var kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null; + this.labels.push({ name: maybeName, kind: kind }); + node.body = this.parseStatement(true); + this.labels.pop(); + node.label = expr; + return this.finishNode(node, "LabeledStatement"); + }; + + pp.parseExpressionStatement = function (node, expr) { + node.expression = expr; + this.semicolon(); + return this.finishNode(node, "ExpressionStatement"); + }; + + // Parse a semicolon-enclosed block of statements, handling `"use + // strict"` declarations when `allowStrict` is true (used for + // function bodies). + + pp.parseBlock = function (allowStrict) { + var node = this.startNode(), + first = true, + oldStrict = undefined; + node.body = []; + this.expect(tt.braceL); + while (!this.eat(tt.braceR)) { + var stmt = this.parseStatement(true); + node.body.push(stmt); + if (first && allowStrict && this.isUseStrict(stmt)) { + oldStrict = this.strict; + this.setStrict(this.strict = true); + } + first = false; + } + if (oldStrict === false) this.setStrict(false); + return this.finishNode(node, "BlockStatement"); + }; + + // Parse a regular `for` loop. The disambiguation code in + // `parseStatement` will already have parsed the init statement or + // expression. + + pp.parseFor = function (node, init) { + node.init = init; + this.expect(tt.semi); + node.test = this.type === tt.semi ? null : this.parseExpression(); + this.expect(tt.semi); + node.update = this.type === tt.parenR ? null : this.parseExpression(); + this.expect(tt.parenR); + node.body = this.parseStatement(false); + this.labels.pop(); + return this.finishNode(node, "ForStatement"); + }; + + // Parse a `for`/`in` and `for`/`of` loop, which are almost + // same from parser's perspective. + + pp.parseForIn = function (node, init) { + var type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"; + this.next(); + node.left = init; + node.right = this.parseExpression(); + this.expect(tt.parenR); + node.body = this.parseStatement(false); + this.labels.pop(); + return this.finishNode(node, type); + }; + + // Parse a list of variable declarations. + + pp.parseVar = function (node, isFor, kind) { + node.declarations = []; + node.kind = kind.keyword; + for (; ;) { + var decl = this.startNode(); + this.parseVarId(decl); + if (this.eat(tt.eq)) { + decl.init = this.parseMaybeAssign(isFor); + } else if (kind === tt._const && !(this.type === tt._in || this.options.ecmaVersion >= 6 && this.isContextual("of"))) { + this.unexpected(); + } else if (decl.id.type != "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) { + this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value"); + } else { + decl.init = null; + } + node.declarations.push(this.finishNode(decl, "VariableDeclarator")); + if (!this.eat(tt.comma)) break; + } + return node; + }; + + pp.parseVarId = function (decl) { + decl.id = this.parseBindingAtom(); + this.checkLVal(decl.id, true); + }; + + // Parse a function declaration or literal (depending on the + // `isStatement` parameter). + + pp.parseFunction = function (node, isStatement, allowExpressionBody) { + this.initFunction(node); + if (this.options.ecmaVersion >= 6) node.generator = this.eat(tt.star); + if (isStatement || this.type === tt.name) node.id = this.parseIdent(); + this.parseFunctionParams(node); + this.parseFunctionBody(node, allowExpressionBody); + return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); + }; + + pp.parseFunctionParams = function (node) { + this.expect(tt.parenL); + node.params = this.parseBindingList(tt.parenR, false, false); + }; + + // Parse a class declaration or literal (depending on the + // `isStatement` parameter). + + pp.parseClass = function (node, isStatement) { + this.next(); + this.parseClassId(node, isStatement); + this.parseClassSuper(node); + var classBody = this.startNode(); + var hadConstructor = false; + classBody.body = []; + this.expect(tt.braceL); + while (!this.eat(tt.braceR)) { + if (this.eat(tt.semi)) continue; + var method = this.startNode(); + var isGenerator = this.eat(tt.star); + var isMaybeStatic = this.type === tt.name && this.value === "static"; + this.parsePropertyName(method); + method["static"] = isMaybeStatic && this.type !== tt.parenL; + if (method["static"]) { + if (isGenerator) this.unexpected(); + isGenerator = this.eat(tt.star); + this.parsePropertyName(method); + } + method.kind = "method"; + if (!method.computed) { + var key = method.key; + + var isGetSet = false; + if (!isGenerator && key.type === "Identifier" && this.type !== tt.parenL && (key.name === "get" || key.name === "set")) { + isGetSet = true; + method.kind = key.name; + key = this.parsePropertyName(method); + } + if (!method["static"] && (key.type === "Identifier" && key.name === "constructor" || key.type === "Literal" && key.value === "constructor")) { + if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class"); + if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier"); + if (isGenerator) this.raise(key.start, "Constructor can't be a generator"); + method.kind = "constructor"; + hadConstructor = true; + } + } + this.parseClassMethod(classBody, method, isGenerator); + } + node.body = this.finishNode(classBody, "ClassBody"); + return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); + }; + + pp.parseClassMethod = function (classBody, method, isGenerator) { + method.value = this.parseMethod(isGenerator); + classBody.body.push(this.finishNode(method, "MethodDefinition")); + }; + + pp.parseClassId = function (node, isStatement) { + node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null; + }; + + pp.parseClassSuper = function (node) { + node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null; + }; + + // Parses module export declaration. + + pp.parseExport = function (node) { + this.next(); + // export * from '...' + if (this.eat(tt.star)) { + this.expectContextual("from"); + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); + this.semicolon(); + return this.finishNode(node, "ExportAllDeclaration"); + } + if (this.eat(tt._default)) { + // export default ... + var expr = this.parseMaybeAssign(); + var needsSemi = true; + if (expr.type == "FunctionExpression" || expr.type == "ClassExpression") { + needsSemi = false; + if (expr.id) { + expr.type = expr.type == "FunctionExpression" ? "FunctionDeclaration" : "ClassDeclaration"; + } + } + node.declaration = expr; + if (needsSemi) this.semicolon(); + return this.finishNode(node, "ExportDefaultDeclaration"); + } + // export var|const|let|function|class ... + if (this.shouldParseExportStatement()) { + node.declaration = this.parseStatement(true); + node.specifiers = []; + node.source = null; + } else { + // export { x, y as z } [from '...'] + node.declaration = null; + node.specifiers = this.parseExportSpecifiers(); + if (this.eatContextual("from")) { + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); + } else { + node.source = null; + } + this.semicolon(); + } + return this.finishNode(node, "ExportNamedDeclaration"); + }; + + pp.shouldParseExportStatement = function () { + return this.type.keyword; + }; + + // Parses a comma-separated list of module exports. + + pp.parseExportSpecifiers = function () { + var nodes = [], + first = true; + // export { x, y as z } [from '...'] + this.expect(tt.braceL); + while (!this.eat(tt.braceR)) { + if (!first) { + this.expect(tt.comma); + if (this.afterTrailingComma(tt.braceR)) break; + } else first = false; + + var node = this.startNode(); + node.local = this.parseIdent(this.type === tt._default); + node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local; + nodes.push(this.finishNode(node, "ExportSpecifier")); + } + return nodes; + }; + + // Parses import declaration. + + pp.parseImport = function (node) { + this.next(); + // import '...' + if (this.type === tt.string) { + node.specifiers = empty; + node.source = this.parseExprAtom(); + node.kind = ""; + } else { + node.specifiers = this.parseImportSpecifiers(); + this.expectContextual("from"); + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected(); + } + this.semicolon(); + return this.finishNode(node, "ImportDeclaration"); + }; + + // Parses a comma-separated list of module imports. + + pp.parseImportSpecifiers = function () { + var nodes = [], + first = true; + if (this.type === tt.name) { + // import defaultObj, { x, y as z } from '...' + var node = this.startNode(); + node.local = this.parseIdent(); + this.checkLVal(node.local, true); + nodes.push(this.finishNode(node, "ImportDefaultSpecifier")); + if (!this.eat(tt.comma)) return nodes; + } + if (this.type === tt.star) { + var node = this.startNode(); + this.next(); + this.expectContextual("as"); + node.local = this.parseIdent(); + this.checkLVal(node.local, true); + nodes.push(this.finishNode(node, "ImportNamespaceSpecifier")); + return nodes; + } + this.expect(tt.braceL); + while (!this.eat(tt.braceR)) { + if (!first) { + this.expect(tt.comma); + if (this.afterTrailingComma(tt.braceR)) break; + } else first = false; + + var node = this.startNode(); + node.imported = this.parseIdent(true); + node.local = this.eatContextual("as") ? this.parseIdent() : node.imported; + this.checkLVal(node.local, true); + nodes.push(this.finishNode(node, "ImportSpecifier")); + } + return nodes; + }; + + }, { "./state": 13, "./tokentype": 17, "./whitespace": 19 }], 15: [function (_dereq_, module, exports) { + "use strict"; + + var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + + exports.__esModule = true; + // The algorithm used to determine whether a regexp can appear at a + // given point in the program is loosely based on sweet.js' approach. + // See https://github.com/mozilla/sweet.js/wiki/design + + var Parser = _dereq_("./state").Parser; + + var tt = _dereq_("./tokentype").types; + + var lineBreak = _dereq_("./whitespace").lineBreak; + + var TokContext = exports.TokContext = function TokContext(token, isExpr, preserveSpace, override) { + _classCallCheck(this, TokContext); + + this.token = token; + this.isExpr = isExpr; + this.preserveSpace = preserveSpace; + this.override = override; + }; + + var types = { + b_stat: new TokContext("{", false), + b_expr: new TokContext("{", true), + b_tmpl: new TokContext("${", true), + p_stat: new TokContext("(", false), + p_expr: new TokContext("(", true), + q_tmpl: new TokContext("`", true, true, function (p) { + return p.readTmplToken(); + }), + f_expr: new TokContext("function", true) + }; + + exports.types = types; + var pp = Parser.prototype; + + pp.initialContext = function () { + return [types.b_stat]; + }; + + pp.braceIsBlock = function (prevType) { + var parent = undefined; + if (prevType === tt.colon && (parent = this.curContext()).token == "{") return !parent.isExpr; + if (prevType === tt._return) return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)); + if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof) return true; + if (prevType == tt.braceL) return this.curContext() === types.b_stat; + return !this.exprAllowed; + }; + + pp.updateContext = function (prevType) { + var update = undefined, + type = this.type; + if (type.keyword && prevType == tt.dot) this.exprAllowed = false; else if (update = type.updateContext) update.call(this, prevType); else this.exprAllowed = type.beforeExpr; + }; + + // Token-specific context update code + + tt.parenR.updateContext = tt.braceR.updateContext = function () { + if (this.context.length == 1) { + this.exprAllowed = true; + return; + } + var out = this.context.pop(); + if (out === types.b_stat && this.curContext() === types.f_expr) { + this.context.pop(); + this.exprAllowed = false; + } else if (out === types.b_tmpl) { + this.exprAllowed = true; + } else { + this.exprAllowed = !out.isExpr; + } + }; + + tt.braceL.updateContext = function (prevType) { + this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr); + this.exprAllowed = true; + }; + + tt.dollarBraceL.updateContext = function () { + this.context.push(types.b_tmpl); + this.exprAllowed = true; + }; + + tt.parenL.updateContext = function (prevType) { + var statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while; + this.context.push(statementParens ? types.p_stat : types.p_expr); + this.exprAllowed = true; + }; + + tt.incDec.updateContext = function () { }; + + tt._function.updateContext = function () { + if (this.curContext() !== types.b_stat) this.context.push(types.f_expr); + this.exprAllowed = false; + }; + + tt.backQuote.updateContext = function () { + if (this.curContext() === types.q_tmpl) this.context.pop(); else this.context.push(types.q_tmpl); + this.exprAllowed = false; + }; + + // tokExprAllowed stays unchanged + + }, { "./state": 13, "./tokentype": 17, "./whitespace": 19 }], 16: [function (_dereq_, module, exports) { + "use strict"; + + var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + + exports.__esModule = true; + + var _identifier = _dereq_("./identifier"); + + var isIdentifierStart = _identifier.isIdentifierStart; + var isIdentifierChar = _identifier.isIdentifierChar; + + var _tokentype = _dereq_("./tokentype"); + + var tt = _tokentype.types; + var keywordTypes = _tokentype.keywords; + + var Parser = _dereq_("./state").Parser; + + var SourceLocation = _dereq_("./location").SourceLocation; + + var _whitespace = _dereq_("./whitespace"); + + var lineBreak = _whitespace.lineBreak; + var lineBreakG = _whitespace.lineBreakG; + var isNewLine = _whitespace.isNewLine; + var nonASCIIwhitespace = _whitespace.nonASCIIwhitespace; + + // Object type used to represent tokens. Note that normally, tokens + // simply exist as properties on the parser object. This is only + // used for the onToken callback and the external tokenizer. + + var Token = exports.Token = function Token(p) { + _classCallCheck(this, Token); + + this.type = p.type; + this.value = p.value; + this.start = p.start; + this.end = p.end; + if (p.options.locations) this.loc = new SourceLocation(p, p.startLoc, p.endLoc); + if (p.options.ranges) this.range = [p.start, p.end]; + }; + + // ## Tokenizer + + var pp = Parser.prototype; + + // Are we running under Rhino? + var isRhino = typeof Packages !== "undefined"; + + // Move to the next token + + pp.next = function () { + if (this.options.onToken) this.options.onToken(new Token(this)); + + this.lastTokEnd = this.end; + this.lastTokStart = this.start; + this.lastTokEndLoc = this.endLoc; + this.lastTokStartLoc = this.startLoc; + this.nextToken(); + }; + + pp.getToken = function () { + this.next(); + return new Token(this); + }; + + // If we're in an ES6 environment, make parsers iterable + if (typeof Symbol !== "undefined") pp[Symbol.iterator] = function () { + var self = this; + return { + next: function next() { + var token = self.getToken(); + return { + done: token.type === tt.eof, + value: token + }; + } + }; + }; + + // Toggle strict mode. Re-reads the next number or string to please + // pedantic tests (`"use strict"; 010;` should fail). + + pp.setStrict = function (strict) { + this.strict = strict; + if (this.type !== tt.num && this.type !== tt.string) return; + this.pos = this.start; + if (this.options.locations) { + while (this.pos < this.lineStart) { + this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1; + --this.curLine; + } + } + this.nextToken(); + }; + + pp.curContext = function () { + return this.context[this.context.length - 1]; + }; + + // Read a single token, updating the parser object's token-related + // properties. + + pp.nextToken = function () { + var curContext = this.curContext(); + if (!curContext || !curContext.preserveSpace) this.skipSpace(); + + this.start = this.pos; + if (this.options.locations) this.startLoc = this.curPosition(); + if (this.pos >= this.input.length) return this.finishToken(tt.eof); + + if (curContext.override) return curContext.override(this); else this.readToken(this.fullCharCodeAtPos()); + }; + + pp.readToken = function (code) { + // Identifier or keyword. '\uXXXX' sequences are allowed in + // identifiers, so '\' also dispatches to that. + if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */) return this.readWord(); + + return this.getTokenFromCode(code); + }; + + pp.fullCharCodeAtPos = function () { + var code = this.input.charCodeAt(this.pos); + if (code <= 55295 || code >= 57344) return code; + var next = this.input.charCodeAt(this.pos + 1); + return (code << 10) + next - 56613888; + }; + + pp.skipBlockComment = function () { + var startLoc = this.options.onComment && this.options.locations && this.curPosition(); + var start = this.pos, + end = this.input.indexOf("*/", this.pos += 2); + if (end === -1) this.raise(this.pos - 2, "Unterminated comment"); + this.pos = end + 2; + if (this.options.locations) { + lineBreakG.lastIndex = start; + var match = undefined; + while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) { + ++this.curLine; + this.lineStart = match.index + match[0].length; + } + } + if (this.options.onComment) this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos, startLoc, this.options.locations && this.curPosition()); + }; + + pp.skipLineComment = function (startSkip) { + var start = this.pos; + var startLoc = this.options.onComment && this.options.locations && this.curPosition(); + var ch = this.input.charCodeAt(this.pos += startSkip); + while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { + ++this.pos; + ch = this.input.charCodeAt(this.pos); + } + if (this.options.onComment) this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, startLoc, this.options.locations && this.curPosition()); + }; + + // Called at the start of the parse and after every token. Skips + // whitespace and comments, and. + + pp.skipSpace = function () { + while (this.pos < this.input.length) { + var ch = this.input.charCodeAt(this.pos); + if (ch === 32) { + // ' ' + ++this.pos; + } else if (ch === 13) { + ++this.pos; + var next = this.input.charCodeAt(this.pos); + if (next === 10) { + ++this.pos; + } + if (this.options.locations) { + ++this.curLine; + this.lineStart = this.pos; + } + } else if (ch === 10 || ch === 8232 || ch === 8233) { + ++this.pos; + if (this.options.locations) { + ++this.curLine; + this.lineStart = this.pos; + } + } else if (ch > 8 && ch < 14) { + ++this.pos; + } else if (ch === 47) { + // '/' + var next = this.input.charCodeAt(this.pos + 1); + if (next === 42) { + // '*' + this.skipBlockComment(); + } else if (next === 47) { + // '/' + this.skipLineComment(2); + } else break; + } else if (ch === 160) { + // '\xa0' + ++this.pos; + } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { + ++this.pos; + } else { + break; + } + } + }; + + // Called at the end of every token. Sets `end`, `val`, and + // maintains `context` and `exprAllowed`, and skips the space after + // the token, so that the next one's `start` will point at the + // right position. + + pp.finishToken = function (type, val) { + this.end = this.pos; + if (this.options.locations) this.endLoc = this.curPosition(); + var prevType = this.type; + this.type = type; + this.value = val; + + this.updateContext(prevType); + }; + + // ### Token reading + + // This is the function that is called to fetch the next token. It + // is somewhat obscure, because it works in character codes rather + // than characters, and because operator parsing has been inlined + // into it. + // + // All in the name of speed. + // + pp.readToken_dot = function () { + var next = this.input.charCodeAt(this.pos + 1); + if (next >= 48 && next <= 57) return this.readNumber(true); + var next2 = this.input.charCodeAt(this.pos + 2); + if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { + // 46 = dot '.' + this.pos += 3; + return this.finishToken(tt.ellipsis); + } else { + ++this.pos; + return this.finishToken(tt.dot); + } + }; + + pp.readToken_slash = function () { + // '/' + var next = this.input.charCodeAt(this.pos + 1); + if (this.exprAllowed) { + ++this.pos; return this.readRegexp(); + } + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.slash, 1); + }; + + pp.readToken_mult_modulo = function (code) { + // '%*' + var next = this.input.charCodeAt(this.pos + 1); + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(code === 42 ? tt.star : tt.modulo, 1); + }; + + pp.readToken_pipe_amp = function (code) { + // '|&' + var next = this.input.charCodeAt(this.pos + 1); + if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2); + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1); + }; + + pp.readToken_caret = function () { + // '^' + var next = this.input.charCodeAt(this.pos + 1); + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.bitwiseXOR, 1); + }; + + pp.readToken_plus_min = function (code) { + // '+-' + var next = this.input.charCodeAt(this.pos + 1); + if (next === code) { + if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 && lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) { + // A `-->` line comment + this.skipLineComment(3); + this.skipSpace(); + return this.nextToken(); + } + return this.finishOp(tt.incDec, 2); + } + if (next === 61) return this.finishOp(tt.assign, 2); + return this.finishOp(tt.plusMin, 1); + }; + + pp.readToken_lt_gt = function (code) { + // '<>' + var next = this.input.charCodeAt(this.pos + 1); + var size = 1; + if (next === code) { + size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; + if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1); + return this.finishOp(tt.bitShift, size); + } + if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && this.input.charCodeAt(this.pos + 3) == 45) { + if (this.inModule) this.unexpected(); + // ` + + + + + Jasmine Spec Runner + @@TestFrameworkDependencies@@ + @@CodeCoverageDependencies@@ + @@ReferencedCSSFiles@@ + @@ReferencedJSFiles@@ + @@TestJSFile@@ + + + + @@TestHtmlTemplateFiles@@ + + diff --git a/Chutzpah/ChutzpahTestHarnessFiles/Jasmine/v3/jasmine.js b/Chutzpah/ChutzpahTestHarnessFiles/Jasmine/v3/jasmine.js new file mode 100644 index 00000000..4875624e --- /dev/null +++ b/Chutzpah/ChutzpahTestHarnessFiles/Jasmine/v3/jasmine.js @@ -0,0 +1,8218 @@ +/* +Copyright (c) 2008-2019 Pivotal Labs + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +// eslint-disable-next-line no-unused-vars +var getJasmineRequireObj = (function(jasmineGlobal) { + var jasmineRequire; + + if ( + typeof module !== 'undefined' && + module.exports && + typeof exports !== 'undefined' + ) { + if (typeof global !== 'undefined') { + jasmineGlobal = global; + } else { + jasmineGlobal = {}; + } + jasmineRequire = exports; + } else { + if ( + typeof window !== 'undefined' && + typeof window.toString === 'function' && + window.toString() === '[object GjsGlobal]' + ) { + jasmineGlobal = window; + } + jasmineRequire = jasmineGlobal.jasmineRequire = {}; + } + + function getJasmineRequire() { + return jasmineRequire; + } + + getJasmineRequire().core = function(jRequire) { + var j$ = {}; + + jRequire.base(j$, jasmineGlobal); + j$.util = jRequire.util(j$); + j$.errors = jRequire.errors(); + j$.formatErrorMsg = jRequire.formatErrorMsg(); + j$.Any = jRequire.Any(j$); + j$.Anything = jRequire.Anything(j$); + j$.CallTracker = jRequire.CallTracker(j$); + j$.MockDate = jRequire.MockDate(); + j$.getClearStack = jRequire.clearStack(j$); + j$.Clock = jRequire.Clock(); + j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); + j$.Env = jRequire.Env(j$); + j$.StackTrace = jRequire.StackTrace(j$); + j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); + j$.ExpectationFilterChain = jRequire.ExpectationFilterChain(); + j$.Expector = jRequire.Expector(j$); + j$.Expectation = jRequire.Expectation(j$); + j$.buildExpectationResult = jRequire.buildExpectationResult(); + j$.noopTimer = jRequire.noopTimer(); + j$.JsApiReporter = jRequire.JsApiReporter(j$); + j$.matchersUtil = jRequire.matchersUtil(j$); + j$.ObjectContaining = jRequire.ObjectContaining(j$); + j$.ArrayContaining = jRequire.ArrayContaining(j$); + j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); + j$.MapContaining = jRequire.MapContaining(j$); + j$.SetContaining = jRequire.SetContaining(j$); + j$.pp = jRequire.pp(j$); + j$.QueueRunner = jRequire.QueueRunner(j$); + j$.ReportDispatcher = jRequire.ReportDispatcher(j$); + j$.Spec = jRequire.Spec(j$); + j$.Spy = jRequire.Spy(j$); + j$.SpyFactory = jRequire.SpyFactory(j$); + j$.SpyRegistry = jRequire.SpyRegistry(j$); + j$.SpyStrategy = jRequire.SpyStrategy(j$); + j$.StringMatching = jRequire.StringMatching(j$); + j$.UserContext = jRequire.UserContext(j$); + j$.Suite = jRequire.Suite(j$); + j$.Timer = jRequire.Timer(); + j$.TreeProcessor = jRequire.TreeProcessor(); + j$.version = jRequire.version(); + j$.Order = jRequire.Order(); + j$.DiffBuilder = jRequire.DiffBuilder(j$); + j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); + j$.ObjectPath = jRequire.ObjectPath(j$); + j$.GlobalErrors = jRequire.GlobalErrors(j$); + + j$.Truthy = jRequire.Truthy(j$); + j$.Falsy = jRequire.Falsy(j$); + j$.Empty = jRequire.Empty(j$); + j$.NotEmpty = jRequire.NotEmpty(j$); + + j$.matchers = jRequire.requireMatchers(jRequire, j$); + j$.asyncMatchers = jRequire.requireAsyncMatchers(jRequire, j$); + + return j$; + }; + + return getJasmineRequire; +})(this); + +getJasmineRequireObj().requireMatchers = function(jRequire, j$) { + var availableMatchers = [ + 'nothing', + 'toBe', + 'toBeCloseTo', + 'toBeDefined', + 'toBeInstanceOf', + 'toBeFalse', + 'toBeFalsy', + 'toBeGreaterThan', + 'toBeGreaterThanOrEqual', + 'toBeLessThan', + 'toBeLessThanOrEqual', + 'toBeNaN', + 'toBeNegativeInfinity', + 'toBeNull', + 'toBePositiveInfinity', + 'toBeTrue', + 'toBeTruthy', + 'toBeUndefined', + 'toContain', + 'toEqual', + 'toHaveBeenCalled', + 'toHaveBeenCalledBefore', + 'toHaveBeenCalledTimes', + 'toHaveBeenCalledWith', + 'toHaveClass', + 'toMatch', + 'toThrow', + 'toThrowError', + 'toThrowMatching' + ], + matchers = {}; + + for (var i = 0; i < availableMatchers.length; i++) { + var name = availableMatchers[i]; + matchers[name] = jRequire[name](j$); + } + + return matchers; +}; + +getJasmineRequireObj().base = function(j$, jasmineGlobal) { + j$.unimplementedMethod_ = function() { + throw new Error('unimplemented method'); + }; + + /** + * Maximum object depth the pretty printer will print to. + * Set this to a lower value to speed up pretty printing if you have large objects. + * @name jasmine.MAX_PRETTY_PRINT_DEPTH + * @since 1.3.0 + */ + j$.MAX_PRETTY_PRINT_DEPTH = 8; + /** + * Maximum number of array elements to display when pretty printing objects. + * This will also limit the number of keys and values displayed for an object. + * Elements past this number will be ellipised. + * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH + * @since 2.7.0 + */ + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; + /** + * Maximum number of characters to display when pretty printing objects. + * Characters past this number will be ellipised. + * @name jasmine.MAX_PRETTY_PRINT_CHARS + * @since 2.9.0 + */ + j$.MAX_PRETTY_PRINT_CHARS = 1000; + /** + * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. + * @name jasmine.DEFAULT_TIMEOUT_INTERVAL + * @since 1.3.0 + */ + j$.DEFAULT_TIMEOUT_INTERVAL = 5000; + + j$.getGlobal = function() { + return jasmineGlobal; + }; + + /** + * Get the currently booted Jasmine Environment. + * + * @name jasmine.getEnv + * @since 1.3.0 + * @function + * @return {Env} + */ + j$.getEnv = function(options) { + var env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); + //jasmine. singletons in here (setTimeout blah blah). + return env; + }; + + j$.isArray_ = function(value) { + return j$.isA_('Array', value); + }; + + j$.isObject_ = function(value) { + return ( + !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value) + ); + }; + + j$.isString_ = function(value) { + return j$.isA_('String', value); + }; + + j$.isNumber_ = function(value) { + return j$.isA_('Number', value); + }; + + j$.isFunction_ = function(value) { + return j$.isA_('Function', value); + }; + + j$.isAsyncFunction_ = function(value) { + return j$.isA_('AsyncFunction', value); + }; + + j$.isTypedArray_ = function(value) { + return ( + j$.isA_('Float32Array', value) || + j$.isA_('Float64Array', value) || + j$.isA_('Int16Array', value) || + j$.isA_('Int32Array', value) || + j$.isA_('Int8Array', value) || + j$.isA_('Uint16Array', value) || + j$.isA_('Uint32Array', value) || + j$.isA_('Uint8Array', value) || + j$.isA_('Uint8ClampedArray', value) + ); + }; + + j$.isA_ = function(typeName, value) { + return j$.getType_(value) === '[object ' + typeName + ']'; + }; + + j$.isError_ = function(value) { + if (value instanceof Error) { + return true; + } + if (value && value.constructor && value.constructor.constructor) { + var valueGlobal = value.constructor.constructor('return this'); + if (j$.isFunction_(valueGlobal)) { + valueGlobal = valueGlobal(); + } + + if (valueGlobal.Error && value instanceof valueGlobal.Error) { + return true; + } + } + return false; + }; + + j$.getType_ = function(value) { + return Object.prototype.toString.apply(value); + }; + + j$.isDomNode = function(obj) { + // Node is a function, because constructors + return typeof jasmineGlobal.Node !== 'undefined' + ? obj instanceof jasmineGlobal.Node + : obj !== null && + typeof obj === 'object' && + typeof obj.nodeType === 'number' && + typeof obj.nodeName === 'string'; + // return obj.nodeType > 0; + }; + + j$.isMap = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + typeof jasmineGlobal.Map !== 'undefined' && + obj.constructor === jasmineGlobal.Map + ); + }; + + j$.isSet = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + typeof jasmineGlobal.Set !== 'undefined' && + obj.constructor === jasmineGlobal.Set + ); + }; + + j$.isPromise = function(obj) { + return ( + typeof jasmineGlobal.Promise !== 'undefined' && + !!obj && + obj.constructor === jasmineGlobal.Promise + ); + }; + + j$.isPromiseLike = function(obj) { + return !!obj && j$.isFunction_(obj.then); + }; + + j$.fnNameFor = function(func) { + if (func.name) { + return func.name; + } + + var matches = + func.toString().match(/^\s*function\s*(\w+)\s*\(/) || + func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/); + + return matches ? matches[1] : ''; + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is an instance of the specified class/constructor. + * @name jasmine.any + * @since 1.3.0 + * @function + * @param {Constructor} clazz - The constructor to check against. + */ + j$.any = function(clazz) { + return new j$.Any(clazz); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is not `null` and not `undefined`. + * @name jasmine.anything + * @since 2.2.0 + * @function + */ + j$.anything = function() { + return new j$.Anything(); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is `true` or anything truthy. + * @name jasmine.truthy + * @since 3.1.0 + * @function + */ + j$.truthy = function() { + return new j$.Truthy(); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey. + * @name jasmine.falsy + * @since 3.1.0 + * @function + */ + j$.falsy = function() { + return new j$.Falsy(); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is empty. + * @name jasmine.empty + * @since 3.1.0 + * @function + */ + j$.empty = function() { + return new j$.Empty(); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is not empty. + * @name jasmine.notEmpty + * @since 3.1.0 + * @function + */ + j$.notEmpty = function() { + return new j$.NotEmpty(); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared contains at least the keys and values. + * @name jasmine.objectContaining + * @since 1.3.0 + * @function + * @param {Object} sample - The subset of properties that _must_ be in the actual. + */ + j$.objectContaining = function(sample) { + return new j$.ObjectContaining(sample); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`. + * @name jasmine.stringMatching + * @since 2.2.0 + * @function + * @param {RegExp|String} expected + */ + j$.stringMatching = function(expected) { + return new j$.StringMatching(expected); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is an `Array` that contains at least the elements in the sample. + * @name jasmine.arrayContaining + * @since 2.2.0 + * @function + * @param {Array} sample + */ + j$.arrayContaining = function(sample) { + return new j$.ArrayContaining(sample); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. + * @name jasmine.arrayWithExactContents + * @since 2.8.0 + * @function + * @param {Array} sample + */ + j$.arrayWithExactContents = function(sample) { + return new j$.ArrayWithExactContents(sample); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if every key/value pair in the sample passes the deep equality comparison + * with at least one key/value pair in the actual value being compared + * @name jasmine.mapContaining + * @since 3.5.0 + * @function + * @param {Map} sample - The subset of items that _must_ be in the actual. + */ + j$.mapContaining = function(sample) { + return new j$.MapContaining(sample); + }; + + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if every item in the sample passes the deep equality comparison + * with at least one item in the actual value being compared + * @name jasmine.setContaining + * @since 3.5.0 + * @function + * @param {Set} sample - The subset of items that _must_ be in the actual. + */ + j$.setContaining = function(sample) { + return new j$.SetContaining(sample); + }; + + j$.isSpy = function(putativeSpy) { + if (!putativeSpy) { + return false; + } + return ( + putativeSpy.and instanceof j$.SpyStrategy && + putativeSpy.calls instanceof j$.CallTracker + ); + }; +}; + +getJasmineRequireObj().util = function(j$) { + var util = {}; + + util.inherit = function(childClass, parentClass) { + var Subclass = function() {}; + Subclass.prototype = parentClass.prototype; + childClass.prototype = new Subclass(); + }; + + util.htmlEscape = function(str) { + if (!str) { + return str; + } + return str + .replace(/&/g, '&') + .replace(//g, '>'); + }; + + util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) { + arrayOfArgs.push(args[i]); + } + return arrayOfArgs; + }; + + util.isUndefined = function(obj) { + return obj === void 0; + }; + + util.arrayContains = function(array, search) { + var i = array.length; + while (i--) { + if (array[i] === search) { + return true; + } + } + return false; + }; + + util.clone = function(obj) { + if (Object.prototype.toString.apply(obj) === '[object Array]') { + return obj.slice(); + } + + var cloned = {}; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + cloned[prop] = obj[prop]; + } + } + + return cloned; + }; + + util.cloneArgs = function(args) { + var clonedArgs = []; + var argsAsArray = j$.util.argsToArray(args); + for (var i = 0; i < argsAsArray.length; i++) { + var str = Object.prototype.toString.apply(argsAsArray[i]), + primitives = /^\[object (Boolean|String|RegExp|Number)/; + + // All falsey values are either primitives, `null`, or `undefined. + if (!argsAsArray[i] || str.match(primitives)) { + clonedArgs.push(argsAsArray[i]); + } else { + clonedArgs.push(j$.util.clone(argsAsArray[i])); + } + } + return clonedArgs; + }; + + util.getPropertyDescriptor = function(obj, methodName) { + var descriptor, + proto = obj; + + do { + descriptor = Object.getOwnPropertyDescriptor(proto, methodName); + proto = Object.getPrototypeOf(proto); + } while (!descriptor && proto); + + return descriptor; + }; + + util.objectDifference = function(obj, toRemove) { + var diff = {}; + + for (var key in obj) { + if (util.has(obj, key) && !util.has(toRemove, key)) { + diff[key] = obj[key]; + } + } + + return diff; + }; + + util.has = function(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); + }; + + util.errorWithStack = function errorWithStack() { + // Don't throw and catch if we don't have to, because it makes it harder + // for users to debug their code with exception breakpoints. + var error = new Error(); + + if (error.stack) { + return error; + } + + // But some browsers (e.g. Phantom) only provide a stack trace if we throw. + try { + throw new Error(); + } catch (e) { + return e; + } + }; + + function callerFile() { + var trace = new j$.StackTrace(util.errorWithStack()); + return trace.frames[2].file; + } + + util.jasmineFile = (function() { + var result; + + return function() { + if (!result) { + result = callerFile(); + } + + return result; + }; + })(); + + function StopIteration() {} + StopIteration.prototype = Object.create(Error.prototype); + StopIteration.prototype.constructor = StopIteration; + + // useful for maps and sets since `forEach` is the only IE11-compatible way to iterate them + util.forEachBreakable = function(iterable, iteratee) { + function breakLoop() { + throw new StopIteration(); + } + + try { + iterable.forEach(function(value, key) { + iteratee(breakLoop, value, key, iterable); + }); + } catch (error) { + if (!(error instanceof StopIteration)) throw error; + } + }; + + return util; +}; + +getJasmineRequireObj().Spec = function(j$) { + function Spec(attrs) { + this.expectationFactory = attrs.expectationFactory; + this.asyncExpectationFactory = attrs.asyncExpectationFactory; + this.resultCallback = attrs.resultCallback || function() {}; + this.id = attrs.id; + this.description = attrs.description || ''; + this.queueableFn = attrs.queueableFn; + this.beforeAndAfterFns = + attrs.beforeAndAfterFns || + function() { + return { befores: [], afters: [] }; + }; + this.userContext = + attrs.userContext || + function() { + return {}; + }; + this.onStart = attrs.onStart || function() {}; + this.getSpecName = + attrs.getSpecName || + function() { + return ''; + }; + this.expectationResultFactory = + attrs.expectationResultFactory || function() {}; + this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; + this.catchingExceptions = + attrs.catchingExceptions || + function() { + return true; + }; + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; + this.timer = attrs.timer || j$.noopTimer; + + if (!this.queueableFn.fn) { + this.pend(); + } + + /** + * @typedef SpecResult + * @property {Int} id - The unique id of this spec. + * @property {String} description - The description passed to the {@link it} that created this spec. + * @property {String} fullName - The full description including all ancestors of this spec. + * @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec. + * @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec. + * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec. + * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. + * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. + * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. + */ + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + failedExpectations: [], + passedExpectations: [], + deprecationWarnings: [], + pendingReason: '', + duration: null + }; + } + + Spec.prototype.addExpectationResult = function(passed, data, isError) { + var expectationResult = this.expectationResultFactory(data); + if (passed) { + this.result.passedExpectations.push(expectationResult); + } else { + this.result.failedExpectations.push(expectationResult); + + if (this.throwOnExpectationFailure && !isError) { + throw new j$.errors.ExpectationFailed(); + } + } + }; + + Spec.prototype.expect = function(actual) { + return this.expectationFactory(actual, this); + }; + + Spec.prototype.expectAsync = function(actual) { + return this.asyncExpectationFactory(actual, this); + }; + + Spec.prototype.execute = function(onComplete, excluded, failSpecWithNoExp) { + var self = this; + + var onStart = { + fn: function(done) { + self.timer.start(); + self.onStart(self, done); + } + }; + + var complete = { + fn: function(done) { + self.queueableFn.fn = null; + self.result.status = self.status(excluded, failSpecWithNoExp); + self.resultCallback(self.result, done); + } + }; + + var fns = this.beforeAndAfterFns(); + var regularFns = fns.befores.concat(this.queueableFn); + + var runnerConfig = { + isLeaf: true, + queueableFns: regularFns, + cleanupFns: fns.afters, + onException: function() { + self.onException.apply(self, arguments); + }, + onComplete: function() { + self.result.duration = self.timer.elapsed(); + onComplete( + self.result.status === 'failed' && + new j$.StopExecutionError('spec failed') + ); + }, + userContext: this.userContext() + }; + + if (this.markedPending || excluded === true) { + runnerConfig.queueableFns = []; + runnerConfig.cleanupFns = []; + } + + runnerConfig.queueableFns.unshift(onStart); + runnerConfig.cleanupFns.push(complete); + + this.queueRunnerFactory(runnerConfig); + }; + + Spec.prototype.onException = function onException(e) { + if (Spec.isPendingSpecException(e)) { + this.pend(extractCustomPendingMessage(e)); + return; + } + + if (e instanceof j$.errors.ExpectationFailed) { + return; + } + + this.addExpectationResult( + false, + { + matcherName: '', + passed: false, + expected: '', + actual: '', + error: e + }, + true + ); + }; + + Spec.prototype.pend = function(message) { + this.markedPending = true; + if (message) { + this.result.pendingReason = message; + } + }; + + Spec.prototype.getResult = function() { + this.result.status = this.status(); + return this.result; + }; + + Spec.prototype.status = function(excluded, failSpecWithNoExpectations) { + if (excluded === true) { + return 'excluded'; + } + + if (this.markedPending) { + return 'pending'; + } + + if ( + this.result.failedExpectations.length > 0 || + (failSpecWithNoExpectations && + this.result.failedExpectations.length + + this.result.passedExpectations.length === + 0) + ) { + return 'failed'; + } + + return 'passed'; + }; + + Spec.prototype.getFullName = function() { + return this.getSpecName(this); + }; + + Spec.prototype.addDeprecationWarning = function(deprecation) { + if (typeof deprecation === 'string') { + deprecation = { message: deprecation }; + } + this.result.deprecationWarnings.push( + this.expectationResultFactory(deprecation) + ); + }; + + var extractCustomPendingMessage = function(e) { + var fullMessage = e.toString(), + boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), + boilerplateEnd = + boilerplateStart + Spec.pendingSpecExceptionMessage.length; + + return fullMessage.substr(boilerplateEnd); + }; + + Spec.pendingSpecExceptionMessage = '=> marked Pending'; + + Spec.isPendingSpecException = function(e) { + return !!( + e && + e.toString && + e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1 + ); + }; + + return Spec; +}; + +if (typeof window == void 0 && typeof exports == 'object') { + /* globals exports */ + exports.Spec = jasmineRequire.Spec; +} + +/*jshint bitwise: false*/ + +getJasmineRequireObj().Order = function() { + function Order(options) { + this.random = 'random' in options ? options.random : true; + var seed = (this.seed = options.seed || generateSeed()); + this.sort = this.random ? randomOrder : naturalOrder; + + function naturalOrder(items) { + return items; + } + + function randomOrder(items) { + var copy = items.slice(); + copy.sort(function(a, b) { + return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); + }); + return copy; + } + + function generateSeed() { + return String(Math.random()).slice(-5); + } + + // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function + // used to get a different output when the key changes slightly. + // We use your return to sort the children randomly in a consistent way when + // used in conjunction with a seed + + function jenkinsHash(key) { + var hash, i; + for (hash = i = 0; i < key.length; ++i) { + hash += key.charCodeAt(i); + hash += hash << 10; + hash ^= hash >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; + } + } + + return Order; +}; + +getJasmineRequireObj().Env = function(j$) { + /** + * _Note:_ Do not construct this directly, Jasmine will make one during booting. + * @name Env + * @since 2.0.0 + * @classdesc The Jasmine environment + * @constructor + */ + function Env(options) { + options = options || {}; + + var self = this; + var global = options.global || j$.getGlobal(); + var customPromise; + + var totalSpecsDefined = 0; + + var realSetTimeout = global.setTimeout; + var realClearTimeout = global.clearTimeout; + var clearStack = j$.getClearStack(global); + this.clock = new j$.Clock( + global, + function() { + return new j$.DelayedFunctionScheduler(); + }, + new j$.MockDate(global) + ); + + var runnableResources = {}; + + var currentSpec = null; + var currentlyExecutingSuites = []; + var currentDeclarationSuite = null; + var hasFailures = false; + + /** + * This represents the available options to configure Jasmine. + * Options that are not provided will use their default values + * @interface Configuration + * @since 3.3.0 + */ + var config = { + /** + * Whether to randomize spec execution order + * @name Configuration#random + * @since 3.3.0 + * @type Boolean + * @default true + */ + random: true, + /** + * Seed to use as the basis of randomization. + * Null causes the seed to be determined randomly at the start of execution. + * @name Configuration#seed + * @since 3.3.0 + * @type function + * @default null + */ + seed: null, + /** + * Whether to stop execution of the suite after the first spec failure + * @name Configuration#failFast + * @since 3.3.0 + * @type Boolean + * @default false + */ + failFast: false, + /** + * Whether to fail the spec if it ran no expectations. By default + * a spec that ran no expectations is reported as passed. Setting this + * to true will report such spec as a failure. + * @name Configuration#failSpecWithNoExpectations + * @since 3.5.0 + * @type Boolean + * @default false + */ + failSpecWithNoExpectations: false, + /** + * Whether to cause specs to only have one expectation failure. + * @name Configuration#oneFailurePerSpec + * @since 3.3.0 + * @type Boolean + * @default false + */ + oneFailurePerSpec: false, + /** + * Function to use to filter specs + * @name Configuration#specFilter + * @since 3.3.0 + * @type function + * @default true + */ + specFilter: function() { + return true; + }, + /** + * Whether or not reporters should hide disabled specs from their output. + * Currently only supported by Jasmine's HTMLReporter + * @name Configuration#hideDisabled + * @since 3.3.0 + * @type Boolean + * @default false + */ + hideDisabled: false, + /** + * Set to provide a custom promise library that Jasmine will use if it needs + * to create a promise. If not set, it will default to whatever global Promise + * library is available (if any). + * @name Configuration#Promise + * @since 3.5.0 + * @type function + * @default undefined + */ + Promise: undefined + }; + + var currentSuite = function() { + return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; + }; + + var currentRunnable = function() { + return currentSpec || currentSuite(); + }; + + var globalErrors = null; + + var installGlobalErrors = function() { + if (globalErrors) { + return; + } + + globalErrors = new j$.GlobalErrors(); + globalErrors.install(); + }; + + if (!options.suppressLoadErrors) { + installGlobalErrors(); + globalErrors.pushListener(function( + message, + filename, + lineno, + colNo, + err + ) { + topSuite.result.failedExpectations.push({ + passed: false, + globalErrorType: 'load', + message: message, + stack: err && err.stack, + filename: filename, + lineno: lineno + }); + }); + } + + /** + * Configure your jasmine environment + * @name Env#configure + * @since 3.3.0 + * @argument {Configuration} configuration + * @function + */ + this.configure = function(configuration) { + if (configuration.specFilter) { + config.specFilter = configuration.specFilter; + } + + if (configuration.hasOwnProperty('random')) { + config.random = !!configuration.random; + } + + if (configuration.hasOwnProperty('seed')) { + config.seed = configuration.seed; + } + + if (configuration.hasOwnProperty('failFast')) { + config.failFast = configuration.failFast; + } + + if (configuration.hasOwnProperty('failSpecWithNoExpectations')) { + config.failSpecWithNoExpectations = + configuration.failSpecWithNoExpectations; + } + + if (configuration.hasOwnProperty('oneFailurePerSpec')) { + config.oneFailurePerSpec = configuration.oneFailurePerSpec; + } + + if (configuration.hasOwnProperty('hideDisabled')) { + config.hideDisabled = configuration.hideDisabled; + } + + // Don't use hasOwnProperty to check for Promise existence because Promise + // can be initialized to undefined, either explicitly or by using the + // object returned from Env#configuration. In particular, Karma does this. + if (configuration.Promise) { + if ( + typeof configuration.Promise.resolve === 'function' && + typeof configuration.Promise.reject === 'function' + ) { + customPromise = configuration.Promise; + } else { + throw new Error( + 'Custom promise library missing `resolve`/`reject` functions' + ); + } + } + }; + + /** + * Get the current configuration for your jasmine environment + * @name Env#configuration + * @since 3.3.0 + * @function + * @returns {Configuration} + */ + this.configuration = function() { + var result = {}; + for (var property in config) { + result[property] = config[property]; + } + return result; + }; + + Object.defineProperty(this, 'specFilter', { + get: function() { + self.deprecated( + 'Getting specFilter directly from Env is deprecated and will be removed in a future version of Jasmine, please check the specFilter option from `configuration`' + ); + return config.specFilter; + }, + set: function(val) { + self.deprecated( + 'Setting specFilter directly on Env is deprecated and will be removed in a future version of Jasmine, please use the specFilter option in `configure`' + ); + config.specFilter = val; + } + }); + + this.setDefaultSpyStrategy = function(defaultStrategyFn) { + if (!currentRunnable()) { + throw new Error( + 'Default spy strategy must be set in a before function or a spec' + ); + } + runnableResources[ + currentRunnable().id + ].defaultStrategyFn = defaultStrategyFn; + }; + + this.addSpyStrategy = function(name, fn) { + if (!currentRunnable()) { + throw new Error( + 'Custom spy strategies must be added in a before function or a spec' + ); + } + runnableResources[currentRunnable().id].customSpyStrategies[name] = fn; + }; + + this.addCustomEqualityTester = function(tester) { + if (!currentRunnable()) { + throw new Error( + 'Custom Equalities must be added in a before function or a spec' + ); + } + runnableResources[currentRunnable().id].customEqualityTesters.push( + tester + ); + }; + + this.addMatchers = function(matchersToAdd) { + if (!currentRunnable()) { + throw new Error( + 'Matchers must be added in a before function or a spec' + ); + } + var customMatchers = + runnableResources[currentRunnable().id].customMatchers; + for (var matcherName in matchersToAdd) { + customMatchers[matcherName] = matchersToAdd[matcherName]; + } + }; + + this.addAsyncMatchers = function(matchersToAdd) { + if (!currentRunnable()) { + throw new Error( + 'Async Matchers must be added in a before function or a spec' + ); + } + var customAsyncMatchers = + runnableResources[currentRunnable().id].customAsyncMatchers; + for (var matcherName in matchersToAdd) { + customAsyncMatchers[matcherName] = matchersToAdd[matcherName]; + } + }; + + j$.Expectation.addCoreMatchers(j$.matchers); + j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); + + var nextSpecId = 0; + var getNextSpecId = function() { + return 'spec' + nextSpecId++; + }; + + var nextSuiteId = 0; + var getNextSuiteId = function() { + return 'suite' + nextSuiteId++; + }; + + var expectationFactory = function(actual, spec) { + return j$.Expectation.factory({ + util: j$.matchersUtil, + customEqualityTesters: runnableResources[spec.id].customEqualityTesters, + customMatchers: runnableResources[spec.id].customMatchers, + actual: actual, + addExpectationResult: addExpectationResult + }); + + function addExpectationResult(passed, result) { + return spec.addExpectationResult(passed, result); + } + }; + + var asyncExpectationFactory = function(actual, spec) { + return j$.Expectation.asyncFactory({ + util: j$.matchersUtil, + customEqualityTesters: runnableResources[spec.id].customEqualityTesters, + customAsyncMatchers: runnableResources[spec.id].customAsyncMatchers, + actual: actual, + addExpectationResult: addExpectationResult + }); + + function addExpectationResult(passed, result) { + return spec.addExpectationResult(passed, result); + } + }; + + var defaultResourcesForRunnable = function(id, parentRunnableId) { + var resources = { + spies: [], + customEqualityTesters: [], + customMatchers: {}, + customAsyncMatchers: {}, + customSpyStrategies: {}, + defaultStrategyFn: undefined + }; + + if (runnableResources[parentRunnableId]) { + resources.customEqualityTesters = j$.util.clone( + runnableResources[parentRunnableId].customEqualityTesters + ); + resources.customMatchers = j$.util.clone( + runnableResources[parentRunnableId].customMatchers + ); + resources.customAsyncMatchers = j$.util.clone( + runnableResources[parentRunnableId].customAsyncMatchers + ); + resources.defaultStrategyFn = + runnableResources[parentRunnableId].defaultStrategyFn; + } + + runnableResources[id] = resources; + }; + + var clearResourcesForRunnable = function(id) { + spyRegistry.clearSpies(); + delete runnableResources[id]; + }; + + var beforeAndAfterFns = function(suite) { + return function() { + var befores = [], + afters = []; + + while (suite) { + befores = befores.concat(suite.beforeFns); + afters = afters.concat(suite.afterFns); + + suite = suite.parentSuite; + } + + return { + befores: befores.reverse(), + afters: afters + }; + }; + }; + + var getSpecName = function(spec, suite) { + var fullName = [spec.description], + suiteFullName = suite.getFullName(); + + if (suiteFullName !== '') { + fullName.unshift(suiteFullName); + } + return fullName.join(' '); + }; + + // TODO: we may just be able to pass in the fn instead of wrapping here + var buildExpectationResult = j$.buildExpectationResult, + exceptionFormatter = new j$.ExceptionFormatter(), + expectationResultFactory = function(attrs) { + attrs.messageFormatter = exceptionFormatter.message; + attrs.stackFormatter = exceptionFormatter.stack; + + return buildExpectationResult(attrs); + }; + + /** + * Sets whether Jasmine should throw an Error when an expectation fails. + * This causes a spec to only have one expectation failure. + * @name Env#throwOnExpectationFailure + * @since 2.3.0 + * @function + * @param {Boolean} value Whether to throw when a expectation fails + * @deprecated Use the `oneFailurePerSpec` option with {@link Env#configure} + */ + this.throwOnExpectationFailure = function(value) { + this.deprecated( + 'Setting throwOnExpectationFailure directly on Env is deprecated and will be removed in a future version of Jasmine, please use the oneFailurePerSpec option in `configure`' + ); + this.configure({ oneFailurePerSpec: !!value }); + }; + + this.throwingExpectationFailures = function() { + this.deprecated( + 'Getting throwingExpectationFailures directly from Env is deprecated and will be removed in a future version of Jasmine, please check the oneFailurePerSpec option from `configuration`' + ); + return config.oneFailurePerSpec; + }; + + /** + * Set whether to stop suite execution when a spec fails + * @name Env#stopOnSpecFailure + * @since 2.7.0 + * @function + * @param {Boolean} value Whether to stop suite execution when a spec fails + * @deprecated Use the `failFast` option with {@link Env#configure} + */ + this.stopOnSpecFailure = function(value) { + this.deprecated( + 'Setting stopOnSpecFailure directly is deprecated and will be removed in a future version of Jasmine, please use the failFast option in `configure`' + ); + this.configure({ failFast: !!value }); + }; + + this.stoppingOnSpecFailure = function() { + this.deprecated( + 'Getting stoppingOnSpecFailure directly from Env is deprecated and will be removed in a future version of Jasmine, please check the failFast option from `configuration`' + ); + return config.failFast; + }; + + /** + * Set whether to randomize test execution order + * @name Env#randomizeTests + * @since 2.4.0 + * @function + * @param {Boolean} value Whether to randomize execution order + * @deprecated Use the `random` option with {@link Env#configure} + */ + this.randomizeTests = function(value) { + this.deprecated( + 'Setting randomizeTests directly is deprecated and will be removed in a future version of Jasmine, please use the random option in `configure`' + ); + config.random = !!value; + }; + + this.randomTests = function() { + this.deprecated( + 'Getting randomTests directly from Env is deprecated and will be removed in a future version of Jasmine, please check the random option from `configuration`' + ); + return config.random; + }; + + /** + * Set the random number seed for spec randomization + * @name Env#seed + * @since 2.4.0 + * @function + * @param {Number} value The seed value + * @deprecated Use the `seed` option with {@link Env#configure} + */ + this.seed = function(value) { + this.deprecated( + 'Setting seed directly is deprecated and will be removed in a future version of Jasmine, please use the seed option in `configure`' + ); + if (value) { + config.seed = value; + } + return config.seed; + }; + + this.hidingDisabled = function(value) { + this.deprecated( + 'Getting hidingDisabled directly from Env is deprecated and will be removed in a future version of Jasmine, please check the hideDisabled option from `configuration`' + ); + return config.hideDisabled; + }; + + /** + * @name Env#hideDisabled + * @since 3.2.0 + * @function + */ + this.hideDisabled = function(value) { + this.deprecated( + 'Setting hideDisabled directly is deprecated and will be removed in a future version of Jasmine, please use the hideDisabled option in `configure`' + ); + config.hideDisabled = !!value; + }; + + this.deprecated = function(deprecation) { + var runnable = currentRunnable() || topSuite; + runnable.addDeprecationWarning(deprecation); + if ( + typeof console !== 'undefined' && + typeof console.error === 'function' + ) { + console.error('DEPRECATION:', deprecation); + } + }; + + var queueRunnerFactory = function(options, args) { + var failFast = false; + if (options.isLeaf) { + failFast = config.oneFailurePerSpec; + } else if (!options.isReporter) { + failFast = config.failFast; + } + options.clearStack = options.clearStack || clearStack; + options.timeout = { + setTimeout: realSetTimeout, + clearTimeout: realClearTimeout + }; + options.fail = self.fail; + options.globalErrors = globalErrors; + options.completeOnFirstError = failFast; + options.onException = + options.onException || + function(e) { + (currentRunnable() || topSuite).onException(e); + }; + options.deprecated = self.deprecated; + + new j$.QueueRunner(options).execute(args); + }; + + var topSuite = new j$.Suite({ + env: this, + id: getNextSuiteId(), + description: 'Jasmine__TopLevel__Suite', + expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, + expectationResultFactory: expectationResultFactory + }); + defaultResourcesForRunnable(topSuite.id); + currentDeclarationSuite = topSuite; + + this.topSuite = function() { + return topSuite; + }; + + /** + * This represents the available reporter callback for an object passed to {@link Env#addReporter}. + * @interface Reporter + * @see custom_reporter + */ + var reporter = new j$.ReportDispatcher( + [ + /** + * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts. + * @function + * @name Reporter#jasmineStarted + * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'jasmineStarted', + /** + * When the entire suite has finished execution `jasmineDone` is called + * @function + * @name Reporter#jasmineDone + * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running. + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'jasmineDone', + /** + * `suiteStarted` is invoked when a `describe` starts to run + * @function + * @name Reporter#suiteStarted + * @param {SuiteResult} result Information about the individual {@link describe} being run + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'suiteStarted', + /** + * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run + * + * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`. + * @function + * @name Reporter#suiteDone + * @param {SuiteResult} result + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'suiteDone', + /** + * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) + * @function + * @name Reporter#specStarted + * @param {SpecResult} result Information about the individual {@link it} being run + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'specStarted', + /** + * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run. + * + * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed. + * @function + * @name Reporter#specDone + * @param {SpecResult} result + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'specDone' + ], + queueRunnerFactory + ); + + this.execute = function(runnablesToRun) { + installGlobalErrors(); + + if (!runnablesToRun) { + if (focusedRunnables.length) { + runnablesToRun = focusedRunnables; + } else { + runnablesToRun = [topSuite.id]; + } + } + + var order = new j$.Order({ + random: config.random, + seed: config.seed + }); + + var processor = new j$.TreeProcessor({ + tree: topSuite, + runnableIds: runnablesToRun, + queueRunnerFactory: queueRunnerFactory, + failSpecWithNoExpectations: config.failSpecWithNoExpectations, + nodeStart: function(suite, next) { + currentlyExecutingSuites.push(suite); + defaultResourcesForRunnable(suite.id, suite.parentSuite.id); + reporter.suiteStarted(suite.result, next); + suite.startTimer(); + }, + nodeComplete: function(suite, result, next) { + if (suite !== currentSuite()) { + throw new Error('Tried to complete the wrong suite'); + } + + clearResourcesForRunnable(suite.id); + currentlyExecutingSuites.pop(); + + if (result.status === 'failed') { + hasFailures = true; + } + suite.endTimer(); + reporter.suiteDone(result, next); + }, + orderChildren: function(node) { + return order.sort(node.children); + }, + excludeNode: function(spec) { + return !config.specFilter(spec); + } + }); + + if (!processor.processTree().valid) { + throw new Error( + 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' + ); + } + + var jasmineTimer = new j$.Timer(); + jasmineTimer.start(); + + /** + * Information passed to the {@link Reporter#jasmineStarted} event. + * @typedef JasmineStartedInfo + * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. + * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. + */ + reporter.jasmineStarted( + { + totalSpecsDefined: totalSpecsDefined, + order: order + }, + function() { + currentlyExecutingSuites.push(topSuite); + + processor.execute(function() { + clearResourcesForRunnable(topSuite.id); + currentlyExecutingSuites.pop(); + var overallStatus, incompleteReason; + + if (hasFailures || topSuite.result.failedExpectations.length > 0) { + overallStatus = 'failed'; + } else if (focusedRunnables.length > 0) { + overallStatus = 'incomplete'; + incompleteReason = 'fit() or fdescribe() was found'; + } else if (totalSpecsDefined === 0) { + overallStatus = 'incomplete'; + incompleteReason = 'No specs found'; + } else { + overallStatus = 'passed'; + } + + /** + * Information passed to the {@link Reporter#jasmineDone} event. + * @typedef JasmineDoneInfo + * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'. + * @property {Int} totalTime - The total time (in ms) that it took to execute the suite + * @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete. + * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. + * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. + * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. + */ + reporter.jasmineDone( + { + overallStatus: overallStatus, + totalTime: jasmineTimer.elapsed(), + incompleteReason: incompleteReason, + order: order, + failedExpectations: topSuite.result.failedExpectations, + deprecationWarnings: topSuite.result.deprecationWarnings + }, + function() {} + ); + }); + } + ); + }; + + /** + * Add a custom reporter to the Jasmine environment. + * @name Env#addReporter + * @since 2.0.0 + * @function + * @param {Reporter} reporterToAdd The reporter to be added. + * @see custom_reporter + */ + this.addReporter = function(reporterToAdd) { + reporter.addReporter(reporterToAdd); + }; + + /** + * Provide a fallback reporter if no other reporters have been specified. + * @name Env#provideFallbackReporter + * @since 2.5.0 + * @function + * @param {Reporter} reporterToAdd The reporter + * @see custom_reporter + */ + this.provideFallbackReporter = function(reporterToAdd) { + reporter.provideFallbackReporter(reporterToAdd); + }; + + /** + * Clear all registered reporters + * @name Env#clearReporters + * @since 2.5.2 + * @function + */ + this.clearReporters = function() { + reporter.clearReporters(); + }; + + var spyFactory = new j$.SpyFactory( + function getCustomStrategies() { + var runnable = currentRunnable(); + + if (runnable) { + return runnableResources[runnable.id].customSpyStrategies; + } + + return {}; + }, + function getDefaultStrategyFn() { + var runnable = currentRunnable(); + + if (runnable) { + return runnableResources[runnable.id].defaultStrategyFn; + } + + return undefined; + }, + function getPromise() { + return customPromise || global.Promise; + } + ); + + var spyRegistry = new j$.SpyRegistry({ + currentSpies: function() { + if (!currentRunnable()) { + throw new Error( + 'Spies must be created in a before function or a spec' + ); + } + return runnableResources[currentRunnable().id].spies; + }, + createSpy: function(name, originalFn) { + return self.createSpy(name, originalFn); + } + }); + + this.allowRespy = function(allow) { + spyRegistry.allowRespy(allow); + }; + + this.spyOn = function() { + return spyRegistry.spyOn.apply(spyRegistry, arguments); + }; + + this.spyOnProperty = function() { + return spyRegistry.spyOnProperty.apply(spyRegistry, arguments); + }; + + this.spyOnAllFunctions = function() { + return spyRegistry.spyOnAllFunctions.apply(spyRegistry, arguments); + }; + + this.createSpy = function(name, originalFn) { + if (arguments.length === 1 && j$.isFunction_(name)) { + originalFn = name; + name = originalFn.name; + } + + return spyFactory.createSpy(name, originalFn); + }; + + this.createSpyObj = function(baseName, methodNames, propertyNames) { + return spyFactory.createSpyObj(baseName, methodNames, propertyNames); + }; + + var ensureIsFunction = function(fn, caller) { + if (!j$.isFunction_(fn)) { + throw new Error( + caller + ' expects a function argument; received ' + j$.getType_(fn) + ); + } + }; + + var ensureIsFunctionOrAsync = function(fn, caller) { + if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { + throw new Error( + caller + ' expects a function argument; received ' + j$.getType_(fn) + ); + } + }; + + function ensureIsNotNested(method) { + var runnable = currentRunnable(); + if (runnable !== null && runnable !== undefined) { + throw new Error( + "'" + method + "' should only be used in 'describe' function" + ); + } + } + + var suiteFactory = function(description) { + var suite = new j$.Suite({ + env: self, + id: getNextSuiteId(), + description: description, + parentSuite: currentDeclarationSuite, + expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, + expectationResultFactory: expectationResultFactory, + throwOnExpectationFailure: config.oneFailurePerSpec + }); + + return suite; + }; + + this.describe = function(description, specDefinitions) { + ensureIsNotNested('describe'); + ensureIsFunction(specDefinitions, 'describe'); + var suite = suiteFactory(description); + if (specDefinitions.length > 0) { + throw new Error('describe does not expect any arguments'); + } + if (currentDeclarationSuite.markedPending) { + suite.pend(); + } + addSpecsToSuite(suite, specDefinitions); + return suite; + }; + + this.xdescribe = function(description, specDefinitions) { + ensureIsNotNested('xdescribe'); + ensureIsFunction(specDefinitions, 'xdescribe'); + var suite = suiteFactory(description); + suite.pend(); + addSpecsToSuite(suite, specDefinitions); + return suite; + }; + + var focusedRunnables = []; + + this.fdescribe = function(description, specDefinitions) { + ensureIsNotNested('fdescribe'); + ensureIsFunction(specDefinitions, 'fdescribe'); + var suite = suiteFactory(description); + suite.isFocused = true; + + focusedRunnables.push(suite.id); + unfocusAncestor(); + addSpecsToSuite(suite, specDefinitions); + + return suite; + }; + + function addSpecsToSuite(suite, specDefinitions) { + var parentSuite = currentDeclarationSuite; + parentSuite.addChild(suite); + currentDeclarationSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch (e) { + declarationError = e; + } + + if (declarationError) { + suite.onException(declarationError); + } + + currentDeclarationSuite = parentSuite; + } + + function findFocusedAncestor(suite) { + while (suite) { + if (suite.isFocused) { + return suite.id; + } + suite = suite.parentSuite; + } + + return null; + } + + function unfocusAncestor() { + var focusedAncestor = findFocusedAncestor(currentDeclarationSuite); + if (focusedAncestor) { + for (var i = 0; i < focusedRunnables.length; i++) { + if (focusedRunnables[i] === focusedAncestor) { + focusedRunnables.splice(i, 1); + break; + } + } + } + } + + var specFactory = function(description, fn, suite, timeout) { + totalSpecsDefined++; + var spec = new j$.Spec({ + id: getNextSpecId(), + beforeAndAfterFns: beforeAndAfterFns(suite), + expectationFactory: expectationFactory, + asyncExpectationFactory: asyncExpectationFactory, + resultCallback: specResultCallback, + getSpecName: function(spec) { + return getSpecName(spec, suite); + }, + onStart: specStarted, + description: description, + expectationResultFactory: expectationResultFactory, + queueRunnerFactory: queueRunnerFactory, + userContext: function() { + return suite.clonedSharedUserContext(); + }, + queueableFn: { + fn: fn, + timeout: timeout || 0 + }, + throwOnExpectationFailure: config.oneFailurePerSpec, + timer: new j$.Timer() + }); + return spec; + + function specResultCallback(result, next) { + clearResourcesForRunnable(spec.id); + currentSpec = null; + + if (result.status === 'failed') { + hasFailures = true; + } + + reporter.specDone(result, next); + } + + function specStarted(spec, next) { + currentSpec = spec; + defaultResourcesForRunnable(spec.id, suite.id); + reporter.specStarted(spec.result, next); + } + }; + + this.it = function(description, fn, timeout) { + ensureIsNotNested('it'); + // it() sometimes doesn't have a fn argument, so only check the type if + // it's given. + if (arguments.length > 1 && typeof fn !== 'undefined') { + ensureIsFunctionOrAsync(fn, 'it'); + } + var spec = specFactory(description, fn, currentDeclarationSuite, timeout); + if (currentDeclarationSuite.markedPending) { + spec.pend(); + } + currentDeclarationSuite.addChild(spec); + return spec; + }; + + this.xit = function(description, fn, timeout) { + ensureIsNotNested('xit'); + // xit(), like it(), doesn't always have a fn argument, so only check the + // type when needed. + if (arguments.length > 1 && typeof fn !== 'undefined') { + ensureIsFunctionOrAsync(fn, 'xit'); + } + var spec = this.it.apply(this, arguments); + spec.pend('Temporarily disabled with xit'); + return spec; + }; + + this.fit = function(description, fn, timeout) { + ensureIsNotNested('fit'); + ensureIsFunctionOrAsync(fn, 'fit'); + var spec = specFactory(description, fn, currentDeclarationSuite, timeout); + currentDeclarationSuite.addChild(spec); + focusedRunnables.push(spec.id); + unfocusAncestor(); + return spec; + }; + + this.expect = function(actual) { + if (!currentRunnable()) { + throw new Error( + "'expect' was used when there was no current spec, this could be because an asynchronous test timed out" + ); + } + + return currentRunnable().expect(actual); + }; + + this.expectAsync = function(actual) { + if (!currentRunnable()) { + throw new Error( + "'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out" + ); + } + + return currentRunnable().expectAsync(actual); + }; + + this.beforeEach = function(beforeEachFunction, timeout) { + ensureIsNotNested('beforeEach'); + ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); + currentDeclarationSuite.beforeEach({ + fn: beforeEachFunction, + timeout: timeout || 0 + }); + }; + + this.beforeAll = function(beforeAllFunction, timeout) { + ensureIsNotNested('beforeAll'); + ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); + currentDeclarationSuite.beforeAll({ + fn: beforeAllFunction, + timeout: timeout || 0 + }); + }; + + this.afterEach = function(afterEachFunction, timeout) { + ensureIsNotNested('afterEach'); + ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); + afterEachFunction.isCleanup = true; + currentDeclarationSuite.afterEach({ + fn: afterEachFunction, + timeout: timeout || 0 + }); + }; + + this.afterAll = function(afterAllFunction, timeout) { + ensureIsNotNested('afterAll'); + ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); + currentDeclarationSuite.afterAll({ + fn: afterAllFunction, + timeout: timeout || 0 + }); + }; + + this.pending = function(message) { + var fullMessage = j$.Spec.pendingSpecExceptionMessage; + if (message) { + fullMessage += message; + } + throw fullMessage; + }; + + this.fail = function(error) { + if (!currentRunnable()) { + throw new Error( + "'fail' was used when there was no current spec, this could be because an asynchronous test timed out" + ); + } + + var message = 'Failed'; + if (error) { + message += ': '; + if (error.message) { + message += error.message; + } else if (j$.isString_(error)) { + message += error; + } else { + // pretty print all kind of objects. This includes arrays. + message += j$.pp(error); + } + } + + currentRunnable().addExpectationResult(false, { + matcherName: '', + passed: false, + expected: '', + actual: '', + message: message, + error: error && error.message ? error : null + }); + + if (config.oneFailurePerSpec) { + throw new Error(message); + } + }; + } + + return Env; +}; + +getJasmineRequireObj().JsApiReporter = function(j$) { + /** + * @name jsApiReporter + * @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object. + * @class + * @hideconstructor + */ + function JsApiReporter(options) { + var timer = options.timer || j$.noopTimer, + status = 'loaded'; + + this.started = false; + this.finished = false; + this.runDetails = {}; + + this.jasmineStarted = function() { + this.started = true; + status = 'started'; + timer.start(); + }; + + var executionTime; + + this.jasmineDone = function(runDetails) { + this.finished = true; + this.runDetails = runDetails; + executionTime = timer.elapsed(); + status = 'done'; + }; + + /** + * Get the current status for the Jasmine environment. + * @name jsApiReporter#status + * @since 2.0.0 + * @function + * @return {String} - One of `loaded`, `started`, or `done` + */ + this.status = function() { + return status; + }; + + var suites = [], + suites_hash = {}; + + this.suiteStarted = function(result) { + suites_hash[result.id] = result; + }; + + this.suiteDone = function(result) { + storeSuite(result); + }; + + /** + * Get the results for a set of suites. + * + * Retrievable in slices for easier serialization. + * @name jsApiReporter#suiteResults + * @since 2.1.0 + * @function + * @param {Number} index - The position in the suites list to start from. + * @param {Number} length - Maximum number of suite results to return. + * @return {SuiteResult[]} + */ + this.suiteResults = function(index, length) { + return suites.slice(index, index + length); + }; + + function storeSuite(result) { + suites.push(result); + suites_hash[result.id] = result; + } + + /** + * Get all of the suites in a single object, with their `id` as the key. + * @name jsApiReporter#suites + * @since 2.0.0 + * @function + * @return {Object} - Map of suite id to {@link SuiteResult} + */ + this.suites = function() { + return suites_hash; + }; + + var specs = []; + + this.specDone = function(result) { + specs.push(result); + }; + + /** + * Get the results for a set of specs. + * + * Retrievable in slices for easier serialization. + * @name jsApiReporter#specResults + * @since 2.0.0 + * @function + * @param {Number} index - The position in the specs list to start from. + * @param {Number} length - Maximum number of specs results to return. + * @return {SpecResult[]} + */ + this.specResults = function(index, length) { + return specs.slice(index, index + length); + }; + + /** + * Get all spec results. + * @name jsApiReporter#specs + * @since 2.0.0 + * @function + * @return {SpecResult[]} + */ + this.specs = function() { + return specs; + }; + + /** + * Get the number of milliseconds it took for the full Jasmine suite to run. + * @name jsApiReporter#executionTime + * @since 2.0.0 + * @function + * @return {Number} + */ + this.executionTime = function() { + return executionTime; + }; + } + + return JsApiReporter; +}; + +getJasmineRequireObj().Any = function(j$) { + + function Any(expectedObject) { + if (typeof expectedObject === 'undefined') { + throw new TypeError( + 'jasmine.any() expects to be passed a constructor function. ' + + 'Please pass one or use jasmine.anything() to match any object.' + ); + } + this.expectedObject = expectedObject; + } + + Any.prototype.asymmetricMatch = function(other) { + if (this.expectedObject == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedObject == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedObject == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedObject == Object) { + return other !== null && typeof other == 'object'; + } + + if (this.expectedObject == Boolean) { + return typeof other == 'boolean'; + } + + /* jshint -W122 */ + /* global Symbol */ + if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) { + return typeof other == 'symbol'; + } + /* jshint +W122 */ + + return other instanceof this.expectedObject; + }; + + Any.prototype.jasmineToString = function() { + return ''; + }; + + return Any; +}; + +getJasmineRequireObj().Anything = function(j$) { + + function Anything() {} + + Anything.prototype.asymmetricMatch = function(other) { + return !j$.util.isUndefined(other) && other !== null; + }; + + Anything.prototype.jasmineToString = function() { + return ''; + }; + + return Anything; +}; + +getJasmineRequireObj().ArrayContaining = function(j$) { + function ArrayContaining(sample) { + this.sample = sample; + } + + ArrayContaining.prototype.asymmetricMatch = function(other, customTesters) { + if (!j$.isArray_(this.sample)) { + throw new Error('You must provide an array to arrayContaining, not ' + j$.pp(this.sample) + '.'); + } + + // If the actual parameter is not an array, we can fail immediately, since it couldn't + // possibly be an "array containing" anything. However, we also want an empty sample + // array to match anything, so we need to double-check we aren't in that case + if (!j$.isArray_(other) && this.sample.length > 0) { + return false; + } + + for (var i = 0; i < this.sample.length; i++) { + var item = this.sample[i]; + if (!j$.matchersUtil.contains(other, item, customTesters)) { + return false; + } + } + + return true; + }; + + ArrayContaining.prototype.jasmineToString = function () { + return ''; + }; + + return ArrayContaining; +}; + +getJasmineRequireObj().ArrayWithExactContents = function(j$) { + + function ArrayWithExactContents(sample) { + this.sample = sample; + } + + ArrayWithExactContents.prototype.asymmetricMatch = function(other, customTesters) { + if (!j$.isArray_(this.sample)) { + throw new Error('You must provide an array to arrayWithExactContents, not ' + j$.pp(this.sample) + '.'); + } + + if (this.sample.length !== other.length) { + return false; + } + + for (var i = 0; i < this.sample.length; i++) { + var item = this.sample[i]; + if (!j$.matchersUtil.contains(other, item, customTesters)) { + return false; + } + } + + return true; + }; + + ArrayWithExactContents.prototype.jasmineToString = function() { + return ''; + }; + + return ArrayWithExactContents; +}; + +getJasmineRequireObj().Empty = function (j$) { + + function Empty() {} + + Empty.prototype.asymmetricMatch = function (other) { + if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { + return other.length === 0; + } + + if (j$.isMap(other) || j$.isSet(other)) { + return other.size === 0; + } + + if (j$.isObject_(other)) { + return Object.keys(other).length === 0; + } + return false; + }; + + Empty.prototype.jasmineToString = function () { + return ''; + }; + + return Empty; +}; + +getJasmineRequireObj().Falsy = function(j$) { + + function Falsy() {} + + Falsy.prototype.asymmetricMatch = function(other) { + return !other; + }; + + Falsy.prototype.jasmineToString = function() { + return ''; + }; + + return Falsy; +}; + +getJasmineRequireObj().MapContaining = function(j$) { + function MapContaining(sample) { + if (!j$.isMap(sample)) { + throw new Error('You must provide a map to `mapContaining`, not ' + j$.pp(sample)); + } + + this.sample = sample; + } + + MapContaining.prototype.asymmetricMatch = function(other, customTesters) { + if (!j$.isMap(other)) return false; + + var hasAllMatches = true; + j$.util.forEachBreakable(this.sample, function(breakLoop, value, key) { + // for each key/value pair in `sample` + // there should be at least one pair in `other` whose key and value both match + var hasMatch = false; + j$.util.forEachBreakable(other, function(oBreakLoop, oValue, oKey) { + if ( + j$.matchersUtil.equals(oKey, key, customTesters) + && j$.matchersUtil.equals(oValue, value, customTesters) + ) { + hasMatch = true; + oBreakLoop(); + } + }); + if (!hasMatch) { + hasAllMatches = false; + breakLoop(); + } + }); + + return hasAllMatches; + }; + + MapContaining.prototype.jasmineToString = function() { + return ''; + }; + + return MapContaining; +}; + +getJasmineRequireObj().NotEmpty = function (j$) { + + function NotEmpty() {} + + NotEmpty.prototype.asymmetricMatch = function (other) { + if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { + return other.length !== 0; + } + + if (j$.isMap(other) || j$.isSet(other)) { + return other.size !== 0; + } + + if (j$.isObject_(other)) { + return Object.keys(other).length !== 0; + } + + return false; + }; + + NotEmpty.prototype.jasmineToString = function () { + return ''; + }; + + return NotEmpty; +}; + +getJasmineRequireObj().ObjectContaining = function(j$) { + + function ObjectContaining(sample) { + this.sample = sample; + } + + function getPrototype(obj) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(obj); + } + + if (obj.constructor.prototype == obj) { + return null; + } + + return obj.constructor.prototype; + } + + function hasProperty(obj, property) { + if (!obj) { + return false; + } + + if (Object.prototype.hasOwnProperty.call(obj, property)) { + return true; + } + + return hasProperty(getPrototype(obj), property); + } + + ObjectContaining.prototype.asymmetricMatch = function(other, customTesters) { + if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } + + for (var property in this.sample) { + if (!hasProperty(other, property) || + !j$.matchersUtil.equals(this.sample[property], other[property], customTesters)) { + return false; + } + } + + return true; + }; + + ObjectContaining.prototype.jasmineToString = function() { + return ''; + }; + + return ObjectContaining; +}; + +getJasmineRequireObj().SetContaining = function(j$) { + function SetContaining(sample) { + if (!j$.isSet(sample)) { + throw new Error('You must provide a set to `setContaining`, not ' + j$.pp(sample)); + } + + this.sample = sample; + } + + SetContaining.prototype.asymmetricMatch = function(other, customTesters) { + if (!j$.isSet(other)) return false; + + var hasAllMatches = true; + j$.util.forEachBreakable(this.sample, function(breakLoop, item) { + // for each item in `sample` there should be at least one matching item in `other` + // (not using `j$.matchersUtil.contains` because it compares set members by reference, + // not by deep value equality) + var hasMatch = false; + j$.util.forEachBreakable(other, function(oBreakLoop, oItem) { + if (j$.matchersUtil.equals(oItem, item, customTesters)) { + hasMatch = true; + oBreakLoop(); + } + }); + if (!hasMatch) { + hasAllMatches = false; + breakLoop(); + } + }); + + return hasAllMatches; + }; + + SetContaining.prototype.jasmineToString = function() { + return ''; + }; + + return SetContaining; +}; + +getJasmineRequireObj().StringMatching = function(j$) { + + function StringMatching(expected) { + if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { + throw new Error('Expected is not a String or a RegExp'); + } + + this.regexp = new RegExp(expected); + } + + StringMatching.prototype.asymmetricMatch = function(other) { + return this.regexp.test(other); + }; + + StringMatching.prototype.jasmineToString = function() { + return ''; + }; + + return StringMatching; +}; + +getJasmineRequireObj().Truthy = function(j$) { + + function Truthy() {} + + Truthy.prototype.asymmetricMatch = function(other) { + return !!other; + }; + + Truthy.prototype.jasmineToString = function() { + return ''; + }; + + return Truthy; +}; + +getJasmineRequireObj().CallTracker = function(j$) { + /** + * @namespace Spy#calls + * @since 2.0.0 + */ + function CallTracker() { + var calls = []; + var opts = {}; + + this.track = function(context) { + if (opts.cloneArgs) { + context.args = j$.util.cloneArgs(context.args); + } + calls.push(context); + }; + + /** + * Check whether this spy has been invoked. + * @name Spy#calls#any + * @since 2.0.0 + * @function + * @return {Boolean} + */ + this.any = function() { + return !!calls.length; + }; + + /** + * Get the number of invocations of this spy. + * @name Spy#calls#count + * @since 2.0.0 + * @function + * @return {Integer} + */ + this.count = function() { + return calls.length; + }; + + /** + * Get the arguments that were passed to a specific invocation of this spy. + * @name Spy#calls#argsFor + * @since 2.0.0 + * @function + * @param {Integer} index The 0-based invocation index. + * @return {Array} + */ + this.argsFor = function(index) { + var call = calls[index]; + return call ? call.args : []; + }; + + /** + * Get the raw calls array for this spy. + * @name Spy#calls#all + * @since 2.0.0 + * @function + * @return {Spy.callData[]} + */ + this.all = function() { + return calls; + }; + + /** + * Get all of the arguments for each invocation of this spy in the order they were received. + * @name Spy#calls#allArgs + * @since 2.0.0 + * @function + * @return {Array} + */ + this.allArgs = function() { + var callArgs = []; + for (var i = 0; i < calls.length; i++) { + callArgs.push(calls[i].args); + } + + return callArgs; + }; + + /** + * Get the first invocation of this spy. + * @name Spy#calls#first + * @since 2.0.0 + * @function + * @return {ObjecSpy.callData} + */ + this.first = function() { + return calls[0]; + }; + + /** + * Get the most recent invocation of this spy. + * @name Spy#calls#mostRecent + * @since 2.0.0 + * @function + * @return {ObjecSpy.callData} + */ + this.mostRecent = function() { + return calls[calls.length - 1]; + }; + + /** + * Reset this spy as if it has never been called. + * @name Spy#calls#reset + * @since 2.0.0 + * @function + */ + this.reset = function() { + calls = []; + }; + + /** + * Set this spy to do a shallow clone of arguments passed to each invocation. + * @name Spy#calls#saveArgumentsByValue + * @since 2.5.0 + * @function + */ + this.saveArgumentsByValue = function() { + opts.cloneArgs = true; + }; + } + + return CallTracker; +}; + +getJasmineRequireObj().clearStack = function(j$) { + var maxInlineCallCount = 10; + + function messageChannelImpl(global, setTimeout) { + var channel = new global.MessageChannel(), + head = {}, + tail = head; + + var taskRunning = false; + channel.port1.onmessage = function() { + head = head.next; + var task = head.task; + delete head.task; + + if (taskRunning) { + global.setTimeout(task, 0); + } else { + try { + taskRunning = true; + task(); + } finally { + taskRunning = false; + } + } + }; + + var currentCallCount = 0; + return function clearStack(fn) { + currentCallCount++; + + if (currentCallCount < maxInlineCallCount) { + tail = tail.next = { task: fn }; + channel.port2.postMessage(0); + } else { + currentCallCount = 0; + setTimeout(fn); + } + }; + } + + function getClearStack(global) { + var currentCallCount = 0; + var realSetTimeout = global.setTimeout; + var setTimeoutImpl = function clearStack(fn) { + Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]); + }; + + if (j$.isFunction_(global.setImmediate)) { + var realSetImmediate = global.setImmediate; + return function(fn) { + currentCallCount++; + + if (currentCallCount < maxInlineCallCount) { + realSetImmediate(fn); + } else { + currentCallCount = 0; + + setTimeoutImpl(fn); + } + }; + } else if (!j$.util.isUndefined(global.MessageChannel)) { + return messageChannelImpl(global, setTimeoutImpl); + } else { + return setTimeoutImpl; + } + } + + return getClearStack; +}; + +getJasmineRequireObj().Clock = function() { + /* global process */ + var NODE_JS = + typeof process !== 'undefined' && + process.versions && + typeof process.versions.node === 'string'; + + /** + * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}. + * @class Clock + * @classdesc Jasmine's mock clock is used when testing time dependent code. + */ + function Clock(global, delayedFunctionSchedulerFactory, mockDate) { + var self = this, + realTimingFunctions = { + setTimeout: global.setTimeout, + clearTimeout: global.clearTimeout, + setInterval: global.setInterval, + clearInterval: global.clearInterval + }, + fakeTimingFunctions = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval + }, + installed = false, + delayedFunctionScheduler, + timer; + + self.FakeTimeout = FakeTimeout; + + /** + * Install the mock clock over the built-in methods. + * @name Clock#install + * @since 2.0.0 + * @function + * @return {Clock} + */ + self.install = function() { + if (!originalTimingFunctionsIntact()) { + throw new Error( + 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?' + ); + } + replace(global, fakeTimingFunctions); + timer = fakeTimingFunctions; + delayedFunctionScheduler = delayedFunctionSchedulerFactory(); + installed = true; + + return self; + }; + + /** + * Uninstall the mock clock, returning the built-in methods to their places. + * @name Clock#uninstall + * @since 2.0.0 + * @function + */ + self.uninstall = function() { + delayedFunctionScheduler = null; + mockDate.uninstall(); + replace(global, realTimingFunctions); + + timer = realTimingFunctions; + installed = false; + }; + + /** + * Execute a function with a mocked Clock + * + * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes. + * @name Clock#withMock + * @since 2.3.0 + * @function + * @param {Function} closure The function to be called. + */ + self.withMock = function(closure) { + this.install(); + try { + closure(); + } finally { + this.uninstall(); + } + }; + + /** + * Instruct the installed Clock to also mock the date returned by `new Date()` + * @name Clock#mockDate + * @since 2.1.0 + * @function + * @param {Date} [initialDate=now] The `Date` to provide. + */ + self.mockDate = function(initialDate) { + mockDate.install(initialDate); + }; + + self.setTimeout = function(fn, delay, params) { + return Function.prototype.apply.apply(timer.setTimeout, [ + global, + arguments + ]); + }; + + self.setInterval = function(fn, delay, params) { + return Function.prototype.apply.apply(timer.setInterval, [ + global, + arguments + ]); + }; + + self.clearTimeout = function(id) { + return Function.prototype.call.apply(timer.clearTimeout, [global, id]); + }; + + self.clearInterval = function(id) { + return Function.prototype.call.apply(timer.clearInterval, [global, id]); + }; + + /** + * Tick the Clock forward, running any enqueued timeouts along the way + * @name Clock#tick + * @since 1.3.0 + * @function + * @param {int} millis The number of milliseconds to tick. + */ + self.tick = function(millis) { + if (installed) { + delayedFunctionScheduler.tick(millis, function(millis) { + mockDate.tick(millis); + }); + } else { + throw new Error( + 'Mock clock is not installed, use jasmine.clock().install()' + ); + } + }; + + return self; + + function originalTimingFunctionsIntact() { + return ( + global.setTimeout === realTimingFunctions.setTimeout && + global.clearTimeout === realTimingFunctions.clearTimeout && + global.setInterval === realTimingFunctions.setInterval && + global.clearInterval === realTimingFunctions.clearInterval + ); + } + + function replace(dest, source) { + for (var prop in source) { + dest[prop] = source[prop]; + } + } + + function setTimeout(fn, delay) { + if (!NODE_JS) { + return delayedFunctionScheduler.scheduleFunction( + fn, + delay, + argSlice(arguments, 2) + ); + } + + var timeout = new FakeTimeout(); + + delayedFunctionScheduler.scheduleFunction( + fn, + delay, + argSlice(arguments, 2), + false, + timeout + ); + + return timeout; + } + + function clearTimeout(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function setInterval(fn, interval) { + if (!NODE_JS) { + return delayedFunctionScheduler.scheduleFunction( + fn, + interval, + argSlice(arguments, 2), + true + ); + } + + var timeout = new FakeTimeout(); + + delayedFunctionScheduler.scheduleFunction( + fn, + interval, + argSlice(arguments, 2), + true, + timeout + ); + + return timeout; + } + + function clearInterval(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function argSlice(argsObj, n) { + return Array.prototype.slice.call(argsObj, n); + } + } + + /** + * Mocks Node.js Timeout class + */ + function FakeTimeout() {} + + FakeTimeout.prototype.ref = function() { + return this; + }; + + FakeTimeout.prototype.unref = function() { + return this; + }; + + return Clock; +}; + +getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { + function DelayedFunctionScheduler() { + var self = this; + var scheduledLookup = []; + var scheduledFunctions = {}; + var currentTime = 0; + var delayedFnCount = 0; + var deletedKeys = []; + + self.tick = function(millis, tickDate) { + millis = millis || 0; + var endTime = currentTime + millis; + + runScheduledFunctions(endTime, tickDate); + currentTime = endTime; + }; + + self.scheduleFunction = function( + funcToCall, + millis, + params, + recurring, + timeoutKey, + runAtMillis + ) { + var f; + if (typeof funcToCall === 'string') { + /* jshint evil: true */ + f = function() { + return eval(funcToCall); + }; + /* jshint evil: false */ + } else { + f = funcToCall; + } + + millis = millis || 0; + timeoutKey = timeoutKey || ++delayedFnCount; + runAtMillis = runAtMillis || currentTime + millis; + + var funcToSchedule = { + runAtMillis: runAtMillis, + funcToCall: f, + recurring: recurring, + params: params, + timeoutKey: timeoutKey, + millis: millis + }; + + if (runAtMillis in scheduledFunctions) { + scheduledFunctions[runAtMillis].push(funcToSchedule); + } else { + scheduledFunctions[runAtMillis] = [funcToSchedule]; + scheduledLookup.push(runAtMillis); + scheduledLookup.sort(function(a, b) { + return a - b; + }); + } + + return timeoutKey; + }; + + self.removeFunctionWithId = function(timeoutKey) { + deletedKeys.push(timeoutKey); + + for (var runAtMillis in scheduledFunctions) { + var funcs = scheduledFunctions[runAtMillis]; + var i = indexOfFirstToPass(funcs, function(func) { + return func.timeoutKey === timeoutKey; + }); + + if (i > -1) { + if (funcs.length === 1) { + delete scheduledFunctions[runAtMillis]; + deleteFromLookup(runAtMillis); + } else { + funcs.splice(i, 1); + } + + // intervals get rescheduled when executed, so there's never more + // than a single scheduled function with a given timeoutKey + break; + } + } + }; + + return self; + + function indexOfFirstToPass(array, testFn) { + var index = -1; + + for (var i = 0; i < array.length; ++i) { + if (testFn(array[i])) { + index = i; + break; + } + } + + return index; + } + + function deleteFromLookup(key) { + var value = Number(key); + var i = indexOfFirstToPass(scheduledLookup, function(millis) { + return millis === value; + }); + + if (i > -1) { + scheduledLookup.splice(i, 1); + } + } + + function reschedule(scheduledFn) { + self.scheduleFunction( + scheduledFn.funcToCall, + scheduledFn.millis, + scheduledFn.params, + true, + scheduledFn.timeoutKey, + scheduledFn.runAtMillis + scheduledFn.millis + ); + } + + function forEachFunction(funcsToRun, callback) { + for (var i = 0; i < funcsToRun.length; ++i) { + callback(funcsToRun[i]); + } + } + + function runScheduledFunctions(endTime, tickDate) { + tickDate = tickDate || function() {}; + if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { + tickDate(endTime - currentTime); + return; + } + + do { + deletedKeys = []; + var newCurrentTime = scheduledLookup.shift(); + tickDate(newCurrentTime - currentTime); + + currentTime = newCurrentTime; + + var funcsToRun = scheduledFunctions[currentTime]; + + delete scheduledFunctions[currentTime]; + + forEachFunction(funcsToRun, function(funcToRun) { + if (funcToRun.recurring) { + reschedule(funcToRun); + } + }); + + forEachFunction(funcsToRun, function(funcToRun) { + if (j$.util.arrayContains(deletedKeys, funcToRun.timeoutKey)) { + // skip a timeoutKey deleted whilst we were running + return; + } + funcToRun.funcToCall.apply(null, funcToRun.params || []); + }); + deletedKeys = []; + } while ( + scheduledLookup.length > 0 && + // checking first if we're out of time prevents setTimeout(0) + // scheduled in a funcToRun from forcing an extra iteration + currentTime !== endTime && + scheduledLookup[0] <= endTime + ); + + // ran out of functions to call, but still time left on the clock + if (currentTime !== endTime) { + tickDate(endTime - currentTime); + } + } + } + + return DelayedFunctionScheduler; +}; + +getJasmineRequireObj().errors = function() { + function ExpectationFailed() {} + + ExpectationFailed.prototype = new Error(); + ExpectationFailed.prototype.constructor = ExpectationFailed; + + return { + ExpectationFailed: ExpectationFailed + }; +}; + +getJasmineRequireObj().ExceptionFormatter = function(j$) { + var ignoredProperties = [ + 'name', + 'message', + 'stack', + 'fileName', + 'sourceURL', + 'line', + 'lineNumber', + 'column', + 'description', + 'jasmineMessage' + ]; + + function ExceptionFormatter(options) { + var jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile(); + this.message = function(error) { + var message = ''; + + if (error.jasmineMessage) { + message += error.jasmineMessage; + } else if (error.name && error.message) { + message += error.name + ': ' + error.message; + } else if (error.message) { + message += error.message; + } else { + message += error.toString() + ' thrown'; + } + + if (error.fileName || error.sourceURL) { + message += ' in ' + (error.fileName || error.sourceURL); + } + + if (error.line || error.lineNumber) { + message += ' (line ' + (error.line || error.lineNumber) + ')'; + } + + return message; + }; + + this.stack = function(error) { + if (!error || !error.stack) { + return null; + } + + var stackTrace = new j$.StackTrace(error); + var lines = filterJasmine(stackTrace); + var result = ''; + + if (stackTrace.message) { + lines.unshift(stackTrace.message); + } + + result += formatProperties(error); + result += lines.join('\n'); + + return result; + }; + + function filterJasmine(stackTrace) { + var result = [], + jasmineMarker = + stackTrace.style === 'webkit' ? '' : ' at '; + + stackTrace.frames.forEach(function(frame) { + if (frame.file && frame.file !== jasmineFile) { + result.push(frame.raw); + } else if (result[result.length - 1] !== jasmineMarker) { + result.push(jasmineMarker); + } + }); + + return result; + } + + function formatProperties(error) { + if (!(error instanceof Object)) { + return; + } + + var result = {}; + var empty = true; + + for (var prop in error) { + if (j$.util.arrayContains(ignoredProperties, prop)) { + continue; + } + result[prop] = error[prop]; + empty = false; + } + + if (!empty) { + return 'error properties: ' + j$.pp(result) + '\n'; + } + + return ''; + } + } + + return ExceptionFormatter; +}; + +getJasmineRequireObj().Expectation = function(j$) { + /** + * Matchers that come with Jasmine out of the box. + * @namespace matchers + */ + function Expectation(options) { + this.expector = new j$.Expector(options); + + var customMatchers = options.customMatchers || {}; + for (var matcherName in customMatchers) { + this[matcherName] = wrapSyncCompare( + matcherName, + customMatchers[matcherName] + ); + } + } + + /** + * Add some context for an {@link expect} + * @function + * @name matchers#withContext + * @since 3.3.0 + * @param {String} message - Additional context to show when the matcher fails + * @return {matchers} + */ + Expectation.prototype.withContext = function withContext(message) { + return addFilter(this, new ContextAddingFilter(message)); + }; + + /** + * Invert the matcher following this {@link expect} + * @member + * @name matchers#not + * @since 1.3.0 + * @type {matchers} + * @example + * expect(something).not.toBe(true); + */ + Object.defineProperty(Expectation.prototype, 'not', { + get: function() { + return addFilter(this, syncNegatingFilter); + } + }); + + /** + * Asynchronous matchers. + * @namespace async-matchers + */ + function AsyncExpectation(options) { + var global = options.global || j$.getGlobal(); + this.expector = new j$.Expector(options); + + if (!global.Promise) { + throw new Error( + 'expectAsync is unavailable because the environment does not support promises.' + ); + } + + var customAsyncMatchers = options.customAsyncMatchers || {}; + for (var matcherName in customAsyncMatchers) { + this[matcherName] = wrapAsyncCompare( + matcherName, + customAsyncMatchers[matcherName] + ); + } + } + + /** + * Add some context for an {@link expectAsync} + * @function + * @name async-matchers#withContext + * @since 3.3.0 + * @param {String} message - Additional context to show when the async matcher fails + * @return {async-matchers} + */ + AsyncExpectation.prototype.withContext = function withContext(message) { + return addFilter(this, new ContextAddingFilter(message)); + }; + + /** + * Invert the matcher following this {@link expectAsync} + * @member + * @name async-matchers#not + * @type {async-matchers} + * @example + * await expectAsync(myPromise).not.toBeResolved(); + * @example + * return expectAsync(myPromise).not.toBeResolved(); + */ + Object.defineProperty(AsyncExpectation.prototype, 'not', { + get: function() { + return addFilter(this, asyncNegatingFilter); + } + }); + + function wrapSyncCompare(name, matcherFactory) { + return function() { + var result = this.expector.compare(name, matcherFactory, arguments); + this.expector.processResult(result); + }; + } + + function wrapAsyncCompare(name, matcherFactory) { + return function() { + var self = this; + + // Capture the call stack here, before we go async, so that it will contain + // frames that are relevant to the user instead of just parts of Jasmine. + var errorForStack = j$.util.errorWithStack(); + + return this.expector + .compare(name, matcherFactory, arguments) + .then(function(result) { + self.expector.processResult(result, errorForStack); + }); + }; + } + + function addCoreMatchers(prototype, matchers, wrapper) { + for (var matcherName in matchers) { + var matcher = matchers[matcherName]; + prototype[matcherName] = wrapper(matcherName, matcher); + } + } + + function addFilter(source, filter) { + var result = Object.create(source); + result.expector = source.expector.addFilter(filter); + return result; + } + + function negatedFailureMessage(result, matcherName, args, util) { + if (result.message) { + if (j$.isFunction_(result.message)) { + return result.message(); + } else { + return result.message; + } + } + + args = args.slice(); + args.unshift(true); + args.unshift(matcherName); + return util.buildFailureMessage.apply(null, args); + } + + function negate(result) { + result.pass = !result.pass; + return result; + } + + var syncNegatingFilter = { + selectComparisonFunc: function(matcher) { + function defaultNegativeCompare() { + return negate(matcher.compare.apply(null, arguments)); + } + + return matcher.negativeCompare || defaultNegativeCompare; + }, + buildFailureMessage: negatedFailureMessage + }; + + var asyncNegatingFilter = { + selectComparisonFunc: function(matcher) { + function defaultNegativeCompare() { + return matcher.compare.apply(this, arguments).then(negate); + } + + return matcher.negativeCompare || defaultNegativeCompare; + }, + buildFailureMessage: negatedFailureMessage + }; + + function ContextAddingFilter(message) { + this.message = message; + } + + ContextAddingFilter.prototype.modifyFailureMessage = function(msg) { + return this.message + ': ' + msg; + }; + + return { + factory: function(options) { + return new Expectation(options || {}); + }, + addCoreMatchers: function(matchers) { + addCoreMatchers(Expectation.prototype, matchers, wrapSyncCompare); + }, + asyncFactory: function(options) { + return new AsyncExpectation(options || {}); + }, + addAsyncCoreMatchers: function(matchers) { + addCoreMatchers(AsyncExpectation.prototype, matchers, wrapAsyncCompare); + } + }; +}; + +getJasmineRequireObj().ExpectationFilterChain = function() { + function ExpectationFilterChain(maybeFilter, prev) { + this.filter_ = maybeFilter; + this.prev_ = prev; + } + + ExpectationFilterChain.prototype.addFilter = function(filter) { + return new ExpectationFilterChain(filter, this); + }; + + ExpectationFilterChain.prototype.selectComparisonFunc = function(matcher) { + return this.callFirst_('selectComparisonFunc', arguments).result; + }; + + ExpectationFilterChain.prototype.buildFailureMessage = function( + result, + matcherName, + args, + util + ) { + return this.callFirst_('buildFailureMessage', arguments).result; + }; + + ExpectationFilterChain.prototype.modifyFailureMessage = function(msg) { + var result = this.callFirst_('modifyFailureMessage', arguments).result; + return result || msg; + }; + + ExpectationFilterChain.prototype.callFirst_ = function(fname, args) { + var prevResult; + + if (this.prev_) { + prevResult = this.prev_.callFirst_(fname, args); + + if (prevResult.found) { + return prevResult; + } + } + + if (this.filter_ && this.filter_[fname]) { + return { + found: true, + result: this.filter_[fname].apply(this.filter_, args) + }; + } + + return { found: false }; + }; + + return ExpectationFilterChain; +}; + +//TODO: expectation result may make more sense as a presentation of an expectation. +getJasmineRequireObj().buildExpectationResult = function() { + function buildExpectationResult(options) { + var messageFormatter = options.messageFormatter || function() {}, + stackFormatter = options.stackFormatter || function() {}; + + /** + * @typedef Expectation + * @property {String} matcherName - The name of the matcher that was executed for this expectation. + * @property {String} message - The failure message for the expectation. + * @property {String} stack - The stack trace for the failure if available. + * @property {Boolean} passed - Whether the expectation passed or failed. + * @property {Object} expected - If the expectation failed, what was the expected value. + * @property {Object} actual - If the expectation failed, what actual value was produced. + */ + var result = { + matcherName: options.matcherName, + message: message(), + stack: stack(), + passed: options.passed + }; + + if (!result.passed) { + result.expected = options.expected; + result.actual = options.actual; + } + + return result; + + function message() { + if (options.passed) { + return 'Passed.'; + } else if (options.message) { + return options.message; + } else if (options.error) { + return messageFormatter(options.error); + } + return ''; + } + + function stack() { + if (options.passed) { + return ''; + } + + var error = options.error; + if (!error) { + if (options.errorForStack) { + error = options.errorForStack; + } else if (options.stack) { + error = options; + } else { + try { + throw new Error(message()); + } catch (e) { + error = e; + } + } + } + return stackFormatter(error); + } + } + + return buildExpectationResult; +}; + +getJasmineRequireObj().Expector = function(j$) { + function Expector(options) { + this.util = options.util || { buildFailureMessage: function() {} }; + this.customEqualityTesters = options.customEqualityTesters || []; + this.actual = options.actual; + this.addExpectationResult = options.addExpectationResult || function() {}; + this.filters = new j$.ExpectationFilterChain(); + } + + Expector.prototype.instantiateMatcher = function( + matcherName, + matcherFactory, + args + ) { + this.matcherName = matcherName; + this.args = Array.prototype.slice.call(args, 0); + this.expected = this.args.slice(0); + + this.args.unshift(this.actual); + + var matcher = matcherFactory(this.util, this.customEqualityTesters); + var comparisonFunc = this.filters.selectComparisonFunc(matcher); + return comparisonFunc || matcher.compare; + }; + + Expector.prototype.buildMessage = function(result) { + var self = this; + + if (result.pass) { + return ''; + } + + var msg = this.filters.buildFailureMessage( + result, + this.matcherName, + this.args, + this.util, + defaultMessage + ); + return this.filters.modifyFailureMessage(msg || defaultMessage()); + + function defaultMessage() { + if (!result.message) { + var args = self.args.slice(); + args.unshift(false); + args.unshift(self.matcherName); + return self.util.buildFailureMessage.apply(null, args); + } else if (j$.isFunction_(result.message)) { + return result.message(); + } else { + return result.message; + } + } + }; + + Expector.prototype.compare = function(matcherName, matcherFactory, args) { + var matcherCompare = this.instantiateMatcher( + matcherName, + matcherFactory, + args + ); + return matcherCompare.apply(null, this.args); + }; + + Expector.prototype.addFilter = function(filter) { + var result = Object.create(this); + result.filters = this.filters.addFilter(filter); + return result; + }; + + Expector.prototype.processResult = function(result, errorForStack) { + var message = this.buildMessage(result); + + if (this.expected.length === 1) { + this.expected = this.expected[0]; + } + + this.addExpectationResult(result.pass, { + matcherName: this.matcherName, + passed: result.pass, + message: message, + error: errorForStack ? undefined : result.error, + errorForStack: errorForStack || undefined, + actual: this.actual, + expected: this.expected // TODO: this may need to be arrayified/sliced + }); + }; + + return Expector; +}; + +getJasmineRequireObj().formatErrorMsg = function() { + function generateErrorMsg(domain, usage) { + var usageDefinition = usage ? '\nUsage: ' + usage : ''; + + return function errorMsg(msg) { + return domain + ' : ' + msg + usageDefinition; + }; + } + + return generateErrorMsg; +}; + +getJasmineRequireObj().GlobalErrors = function(j$) { + function GlobalErrors(global) { + var handlers = []; + global = global || j$.getGlobal(); + + var onerror = function onerror() { + var handler = handlers[handlers.length - 1]; + + if (handler) { + handler.apply(null, Array.prototype.slice.call(arguments, 0)); + } else { + throw arguments[0]; + } + }; + + this.originalHandlers = {}; + this.jasmineHandlers = {}; + this.installOne_ = function installOne_(errorType, jasmineMessage) { + function taggedOnError(error) { + error.jasmineMessage = jasmineMessage + ': ' + error; + + var handler = handlers[handlers.length - 1]; + + if (handler) { + handler(error); + } else { + throw error; + } + } + + this.originalHandlers[errorType] = global.process.listeners(errorType); + this.jasmineHandlers[errorType] = taggedOnError; + + global.process.removeAllListeners(errorType); + global.process.on(errorType, taggedOnError); + + this.uninstall = function uninstall() { + var errorTypes = Object.keys(this.originalHandlers); + for (var iType = 0; iType < errorTypes.length; iType++) { + var errorType = errorTypes[iType]; + global.process.removeListener( + errorType, + this.jasmineHandlers[errorType] + ); + for (var i = 0; i < this.originalHandlers[errorType].length; i++) { + global.process.on(errorType, this.originalHandlers[errorType][i]); + } + delete this.originalHandlers[errorType]; + delete this.jasmineHandlers[errorType]; + } + }; + }; + + this.install = function install() { + if ( + global.process && + global.process.listeners && + j$.isFunction_(global.process.on) + ) { + this.installOne_('uncaughtException', 'Uncaught exception'); + this.installOne_('unhandledRejection', 'Unhandled promise rejection'); + } else { + var originalHandler = global.onerror; + global.onerror = onerror; + + this.uninstall = function uninstall() { + global.onerror = originalHandler; + }; + } + }; + + this.pushListener = function pushListener(listener) { + handlers.push(listener); + }; + + this.popListener = function popListener() { + handlers.pop(); + }; + } + + return GlobalErrors; +}; + +getJasmineRequireObj().toBeRejected = function(j$) { + /** + * Expect a promise to be rejected. + * @function + * @async + * @name async-matchers#toBeRejected + * @since 3.1.0 + * @example + * await expectAsync(aPromise).toBeRejected(); + * @example + * return expectAsync(aPromise).toBeRejected(); + */ + return function toBeRejected(util) { + return { + compare: function(actual) { + if (!j$.isPromiseLike(actual)) { + throw new Error('Expected toBeRejected to be called on a promise.'); + } + return actual.then( + function() { return {pass: false}; }, + function() { return {pass: true}; } + ); + } + }; + }; +}; + +getJasmineRequireObj().toBeRejectedWith = function(j$) { + /** + * Expect a promise to be rejected with a value equal to the expected, using deep equality comparison. + * @function + * @async + * @name async-matchers#toBeRejectedWith + * @since 3.3.0 + * @param {Object} expected - Value that the promise is expected to be rejected with + * @example + * await expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); + * @example + * return expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); + */ + return function toBeRejectedWith(util, customEqualityTesters) { + return { + compare: function(actualPromise, expectedValue) { + if (!j$.isPromiseLike(actualPromise)) { + throw new Error('Expected toBeRejectedWith to be called on a promise.'); + } + + function prefix(passed) { + return 'Expected a promise ' + + (passed ? 'not ' : '') + + 'to be rejected with ' + j$.pp(expectedValue); + } + + return actualPromise.then( + function() { + return { + pass: false, + message: prefix(false) + ' but it was resolved.' + }; + }, + function(actualValue) { + if (util.equals(actualValue, expectedValue, customEqualityTesters)) { + return { + pass: true, + message: prefix(true) + '.' + }; + } else { + return { + pass: false, + message: prefix(false) + ' but it was rejected with ' + j$.pp(actualValue) + '.' + }; + } + } + ); + } + }; + }; +}; + +getJasmineRequireObj().toBeRejectedWithError = function(j$) { + /** + * Expect a promise to be rejected with a value matched to the expected + * @function + * @async + * @name async-matchers#toBeRejectedWithError + * @since 3.5.0 + * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. + * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` + * @example + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message'); + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/); + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError); + * await expectAsync(aPromise).toBeRejectedWithError('Error message'); + * return expectAsync(aPromise).toBeRejectedWithError(/Error message/); + */ + return function toBeRejectedWithError() { + return { + compare: function(actualPromise, arg1, arg2) { + if (!j$.isPromiseLike(actualPromise)) { + throw new Error('Expected toBeRejectedWithError to be called on a promise.'); + } + + var expected = getExpectedFromArgs(arg1, arg2); + + return actualPromise.then( + function() { + return { + pass: false, + message: 'Expected a promise to be rejected but it was resolved.' + }; + }, + function(actualValue) { return matchError(actualValue, expected); } + ); + } + }; + }; + + function matchError(actual, expected) { + if (!j$.isError_(actual)) { + return fail(expected, 'rejected with ' + j$.pp(actual)); + } + + if (!(actual instanceof expected.error)) { + return fail(expected, 'rejected with type ' + j$.fnNameFor(actual.constructor)); + } + + var actualMessage = actual.message; + + if (actualMessage === expected.message || typeof expected.message === 'undefined') { + return pass(expected); + } + + if (expected.message instanceof RegExp && expected.message.test(actualMessage)) { + return pass(expected); + } + + return fail(expected, 'rejected with ' + j$.pp(actual)); + } + + function pass(expected) { + return { + pass: true, + message: 'Expected a promise not to be rejected with ' + expected.printValue + ', but it was.' + }; + } + + function fail(expected, message) { + return { + pass: false, + message: 'Expected a promise to be rejected with ' + expected.printValue + ' but it was ' + message + '.' + }; + } + + + function getExpectedFromArgs(arg1, arg2) { + var error, message; + + if (isErrorConstructor(arg1)) { + error = arg1; + message = arg2; + } else { + error = Error; + message = arg1; + } + + return { + error: error, + message: message, + printValue: j$.fnNameFor(error) + (typeof message === 'undefined' ? '' : ': ' + j$.pp(message)) + }; + } + + function isErrorConstructor(value) { + return typeof value === 'function' && (value === Error || j$.isError_(value.prototype)); + } +}; + +getJasmineRequireObj().toBeResolved = function(j$) { + /** + * Expect a promise to be resolved. + * @function + * @async + * @name async-matchers#toBeResolved + * @since 3.1.0 + * @example + * await expectAsync(aPromise).toBeResolved(); + * @example + * return expectAsync(aPromise).toBeResolved(); + */ + return function toBeResolved(util) { + return { + compare: function(actual) { + if (!j$.isPromiseLike(actual)) { + throw new Error('Expected toBeResolved to be called on a promise.'); + } + + return actual.then( + function() { return {pass: true}; }, + function() { return {pass: false}; } + ); + } + }; + }; +}; + +getJasmineRequireObj().toBeResolvedTo = function(j$) { + /** + * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. + * @function + * @async + * @name async-matchers#toBeResolvedTo + * @since 3.1.0 + * @param {Object} expected - Value that the promise is expected to resolve to + * @example + * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); + * @example + * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); + */ + return function toBeResolvedTo(util, customEqualityTesters) { + return { + compare: function(actualPromise, expectedValue) { + if (!j$.isPromiseLike(actualPromise)) { + throw new Error('Expected toBeResolvedTo to be called on a promise.'); + } + + function prefix(passed) { + return 'Expected a promise ' + + (passed ? 'not ' : '') + + 'to be resolved to ' + j$.pp(expectedValue); + } + + return actualPromise.then( + function(actualValue) { + if (util.equals(actualValue, expectedValue, customEqualityTesters)) { + return { + pass: true, + message: prefix(true) + '.' + }; + } else { + return { + pass: false, + message: prefix(false) + ' but it was resolved to ' + j$.pp(actualValue) + '.' + }; + } + }, + function() { + return { + pass: false, + message: prefix(false) + ' but it was rejected.' + }; + } + ); + } + }; + }; +}; + +getJasmineRequireObj().DiffBuilder = function(j$) { + return function DiffBuilder() { + var path = new j$.ObjectPath(), + mismatches = []; + + return { + record: function (actual, expected, formatter) { + formatter = formatter || defaultFormatter; + mismatches.push(formatter(actual, expected, path)); + }, + + getMessage: function () { + return mismatches.join('\n'); + }, + + withPath: function (pathComponent, block) { + var oldPath = path; + path = path.add(pathComponent); + block(); + path = oldPath; + } + }; + + function defaultFormatter (actual, expected, path) { + return 'Expected ' + + path + (path.depth() ? ' = ' : '') + + j$.pp(actual) + + ' to equal ' + + j$.pp(expected) + + '.'; + } + }; +}; + +getJasmineRequireObj().matchersUtil = function(j$) { + // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? + + return { + equals: equals, + + contains: function(haystack, needle, customTesters) { + customTesters = customTesters || []; + + if (j$.isSet(haystack)) { + return haystack.has(needle); + } + + if ((Object.prototype.toString.apply(haystack) === '[object Array]') || + (!!haystack && !haystack.indexOf)) + { + for (var i = 0; i < haystack.length; i++) { + if (equals(haystack[i], needle, customTesters)) { + return true; + } + } + return false; + } + + return !!haystack && haystack.indexOf(needle) >= 0; + }, + + buildFailureMessage: function() { + var args = Array.prototype.slice.call(arguments, 0), + matcherName = args[0], + isNot = args[1], + actual = args[2], + expected = args.slice(3), + englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + + var message = 'Expected ' + + j$.pp(actual) + + (isNot ? ' not ' : ' ') + + englishyPredicate; + + if (expected.length > 0) { + for (var i = 0; i < expected.length; i++) { + if (i > 0) { + message += ','; + } + message += ' ' + j$.pp(expected[i]); + } + } + + return message + '.'; + } + }; + + function isAsymmetric(obj) { + return obj && j$.isA_('Function', obj.asymmetricMatch); + } + + function asymmetricMatch(a, b, customTesters, diffBuilder) { + var asymmetricA = isAsymmetric(a), + asymmetricB = isAsymmetric(b), + result; + + if (asymmetricA && asymmetricB) { + return undefined; + } + + if (asymmetricA) { + result = a.asymmetricMatch(b, customTesters); + if (!result) { + diffBuilder.record(a, b); + } + return result; + } + + if (asymmetricB) { + result = b.asymmetricMatch(a, customTesters); + if (!result) { + diffBuilder.record(a, b); + } + return result; + } + } + + function equals(a, b, customTesters, diffBuilder) { + customTesters = customTesters || []; + diffBuilder = diffBuilder || j$.NullDiffBuilder(); + + return eq(a, b, [], [], customTesters, diffBuilder); + } + + // Equality function lovingly adapted from isEqual in + // [Underscore](http://underscorejs.org) + function eq(a, b, aStack, bStack, customTesters, diffBuilder) { + var result = true, i; + + var asymmetricResult = asymmetricMatch(a, b, customTesters, diffBuilder); + if (!j$.util.isUndefined(asymmetricResult)) { + return asymmetricResult; + } + + for (i = 0; i < customTesters.length; i++) { + var customTesterResult = customTesters[i](a, b); + if (!j$.util.isUndefined(customTesterResult)) { + if (!customTesterResult) { + diffBuilder.record(a, b); + } + return customTesterResult; + } + } + + if (a instanceof Error && b instanceof Error) { + result = a.message == b.message; + if (!result) { + diffBuilder.record(a, b); + } + return result; + } + + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) { + result = a !== 0 || 1 / a == 1 / b; + if (!result) { + diffBuilder.record(a, b); + } + return result; + } + // A strict comparison is necessary because `null == undefined`. + if (a === null || b === null) { + result = a === b; + if (!result) { + diffBuilder.record(a, b); + } + return result; + } + var className = Object.prototype.toString.call(a); + if (className != Object.prototype.toString.call(b)) { + diffBuilder.record(a, b); + return false; + } + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + result = a == String(b); + if (!result) { + diffBuilder.record(a, b); + } + return result; + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); + if (!result) { + diffBuilder.record(a, b); + } + return result; + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + result = +a == +b; + if (!result) { + diffBuilder.record(a, b); + } + return result; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { + diffBuilder.record(a, b); + return false; + } + + var aIsDomNode = j$.isDomNode(a); + var bIsDomNode = j$.isDomNode(b); + if (aIsDomNode && bIsDomNode) { + // At first try to use DOM3 method isEqualNode + result = a.isEqualNode(b); + if (!result) { + diffBuilder.record(a, b); + } + return result; + } + if (aIsDomNode || bIsDomNode) { + diffBuilder.record(a, b); + return false; + } + + var aIsPromise = j$.isPromise(a); + var bIsPromise = j$.isPromise(b); + if (aIsPromise && bIsPromise) { + return a === b; + } + + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) { return bStack[length] == b; } + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0; + // Recursively compare objects and arrays. + // Compare array lengths to determine if a deep comparison is necessary. + if (className == '[object Array]') { + var aLength = a.length; + var bLength = b.length; + + diffBuilder.withPath('length', function() { + if (aLength !== bLength) { + diffBuilder.record(aLength, bLength); + result = false; + } + }); + + for (i = 0; i < aLength || i < bLength; i++) { + diffBuilder.withPath(i, function() { + if (i >= bLength) { + diffBuilder.record(a[i], void 0, actualArrayIsLongerFormatter); + result = false; + } else { + result = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result; + } + }); + } + if (!result) { + return false; + } + } else if (j$.isMap(a) && j$.isMap(b)) { + if (a.size != b.size) { + diffBuilder.record(a, b); + return false; + } + + var keysA = []; + var keysB = []; + a.forEach( function( valueA, keyA ) { + keysA.push( keyA ); + }); + b.forEach( function( valueB, keyB ) { + keysB.push( keyB ); + }); + + // For both sets of keys, check they map to equal values in both maps. + // Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys. + var mapKeys = [keysA, keysB]; + var cmpKeys = [keysB, keysA]; + var mapIter, mapKey, mapValueA, mapValueB; + var cmpIter, cmpKey; + for (i = 0; result && i < mapKeys.length; i++) { + mapIter = mapKeys[i]; + cmpIter = cmpKeys[i]; + + for (var j = 0; result && j < mapIter.length; j++) { + mapKey = mapIter[j]; + cmpKey = cmpIter[j]; + mapValueA = a.get(mapKey); + + // Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches, + // otherwise explicitly look up the mapKey in the other Map since we want keys with unique + // obj identity (that are otherwise equal) to not match. + if (isAsymmetric(mapKey) || isAsymmetric(cmpKey) && + eq(mapKey, cmpKey, aStack, bStack, customTesters, j$.NullDiffBuilder())) { + mapValueB = b.get(cmpKey); + } else { + mapValueB = b.get(mapKey); + } + result = eq(mapValueA, mapValueB, aStack, bStack, customTesters, j$.NullDiffBuilder()); + } + } + + if (!result) { + diffBuilder.record(a, b); + return false; + } + } else if (j$.isSet(a) && j$.isSet(b)) { + if (a.size != b.size) { + diffBuilder.record(a, b); + return false; + } + + var valuesA = []; + a.forEach( function( valueA ) { + valuesA.push( valueA ); + }); + var valuesB = []; + b.forEach( function( valueB ) { + valuesB.push( valueB ); + }); + + // For both sets, check they are all contained in the other set + var setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; + var stackPairs = [[aStack, bStack], [bStack, aStack]]; + var baseValues, baseValue, baseStack; + var otherValues, otherValue, otherStack; + var found; + var prevStackSize; + for (i = 0; result && i < setPairs.length; i++) { + baseValues = setPairs[i][0]; + otherValues = setPairs[i][1]; + baseStack = stackPairs[i][0]; + otherStack = stackPairs[i][1]; + // For each value in the base set... + for (var k = 0; result && k < baseValues.length; k++) { + baseValue = baseValues[k]; + found = false; + // ... test that it is present in the other set + for (var l = 0; !found && l < otherValues.length; l++) { + otherValue = otherValues[l]; + prevStackSize = baseStack.length; + // compare by value equality + found = eq(baseValue, otherValue, baseStack, otherStack, customTesters, j$.NullDiffBuilder()); + if (!found && prevStackSize !== baseStack.length) { + baseStack.splice(prevStackSize); + otherStack.splice(prevStackSize); + } + } + result = result && found; + } + } + + if (!result) { + diffBuilder.record(a, b); + return false; + } + } else { + + // Objects with different constructors are not equivalent, but `Object`s + // or `Array`s from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && + isFunction(aCtor) && isFunction(bCtor) && + a instanceof aCtor && b instanceof bCtor && + !(aCtor instanceof aCtor && bCtor instanceof bCtor)) { + + diffBuilder.record(a, b, constructorsAreDifferentFormatter); + return false; + } + } + + // Deep compare objects. + var aKeys = keys(a, className == '[object Array]'), key; + size = aKeys.length; + + // Ensure that both objects contain the same number of properties before comparing deep equality. + if (keys(b, className == '[object Array]').length !== size) { + diffBuilder.record(a, b, objectKeysAreDifferentFormatter); + return false; + } + + for (i = 0; i < size; i++) { + key = aKeys[i]; + // Deep compare each member + if (!j$.util.has(b, key)) { + diffBuilder.record(a, b, objectKeysAreDifferentFormatter); + result = false; + continue; + } + + diffBuilder.withPath(key, function() { + if(!eq(a[key], b[key], aStack, bStack, customTesters, diffBuilder)) { + result = false; + } + }); + } + + if (!result) { + return false; + } + + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + + return result; + } + + function keys(obj, isArray) { + var allKeys = Object.keys ? Object.keys(obj) : + (function(o) { + var keys = []; + for (var key in o) { + if (j$.util.has(o, key)) { + keys.push(key); + } + } + return keys; + })(obj); + + if (!isArray) { + return allKeys; + } + + if (allKeys.length === 0) { + return allKeys; + } + + var extraKeys = []; + for (var i = 0; i < allKeys.length; i++) { + if (!/^[0-9]+$/.test(allKeys[i])) { + extraKeys.push(allKeys[i]); + } + } + + return extraKeys; + } + + function isFunction(obj) { + return typeof obj === 'function'; + } + + function objectKeysAreDifferentFormatter(actual, expected, path) { + var missingProperties = j$.util.objectDifference(expected, actual), + extraProperties = j$.util.objectDifference(actual, expected), + missingPropertiesMessage = formatKeyValuePairs(missingProperties), + extraPropertiesMessage = formatKeyValuePairs(extraProperties), + messages = []; + + if (!path.depth()) { + path = 'object'; + } + + if (missingPropertiesMessage.length) { + messages.push('Expected ' + path + ' to have properties' + missingPropertiesMessage); + } + + if (extraPropertiesMessage.length) { + messages.push('Expected ' + path + ' not to have properties' + extraPropertiesMessage); + } + + return messages.join('\n'); + } + + function constructorsAreDifferentFormatter(actual, expected, path) { + if (!path.depth()) { + path = 'object'; + } + + return 'Expected ' + + path + ' to be a kind of ' + + j$.fnNameFor(expected.constructor) + + ', but was ' + j$.pp(actual) + '.'; + } + + function actualArrayIsLongerFormatter(actual, expected, path) { + return 'Unexpected ' + + path + (path.depth() ? ' = ' : '') + + j$.pp(actual) + + ' in array.'; + } + + function formatKeyValuePairs(obj) { + var formatted = ''; + for (var key in obj) { + formatted += '\n ' + key + ': ' + j$.pp(obj[key]); + } + return formatted; + } +}; + +getJasmineRequireObj().nothing = function() { + /** + * {@link expect} nothing explicitly. + * @function + * @name matchers#nothing + * @since 2.8.0 + * @example + * expect().nothing(); + */ + function nothing() { + return { + compare: function() { + return { + pass: true + }; + } + }; + } + + return nothing; +}; + +getJasmineRequireObj().NullDiffBuilder = function(j$) { + return function() { + return { + withPath: function(_, block) { + block(); + }, + record: function() {} + }; + }; +}; + +getJasmineRequireObj().ObjectPath = function(j$) { + function ObjectPath(components) { + this.components = components || []; + } + + ObjectPath.prototype.toString = function() { + if (this.components.length) { + return '$' + map(this.components, formatPropertyAccess).join(''); + } else { + return ''; + } + }; + + ObjectPath.prototype.add = function(component) { + return new ObjectPath(this.components.concat([component])); + }; + + ObjectPath.prototype.depth = function() { + return this.components.length; + }; + + function formatPropertyAccess(prop) { + if (typeof prop === 'number') { + return '[' + prop + ']'; + } + + if (isValidIdentifier(prop)) { + return '.' + prop; + } + + return '[\'' + prop + '\']'; + } + + function map(array, fn) { + var results = []; + for (var i = 0; i < array.length; i++) { + results.push(fn(array[i])); + } + return results; + } + + function isValidIdentifier(string) { + return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string); + } + + return ObjectPath; +}; + +getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { + var availableMatchers = [ + 'toBeResolved', + 'toBeRejected', + 'toBeResolvedTo', + 'toBeRejectedWith', + 'toBeRejectedWithError' + ], + matchers = {}; + + for (var i = 0; i < availableMatchers.length; i++) { + var name = availableMatchers[i]; + matchers[name] = jRequire[name](j$); + } + + return matchers; +}; + +getJasmineRequireObj().toBe = function(j$) { + /** + * {@link expect} the actual value to be `===` to the expected value. + * @function + * @name matchers#toBe + * @since 1.3.0 + * @param {Object} expected - The expected value to compare against. + * @example + * expect(thing).toBe(realThing); + */ + function toBe(util) { + var tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().'; + + return { + compare: function(actual, expected) { + var result = { + pass: actual === expected + }; + + if (typeof expected === 'object') { + result.message = util.buildFailureMessage('toBe', result.pass, actual, expected) + tip; + } + + return result; + } + }; + } + + return toBe; +}; + +getJasmineRequireObj().toBeCloseTo = function() { + /** + * {@link expect} the actual value to be within a specified precision of the expected value. + * @function + * @name matchers#toBeCloseTo + * @since 1.3.0 + * @param {Object} expected - The expected value to compare against. + * @param {Number} [precision=2] - The number of decimal points to check. + * @example + * expect(number).toBeCloseTo(42.2, 3); + */ + function toBeCloseTo() { + return { + compare: function(actual, expected, precision) { + if (precision !== 0) { + precision = precision || 2; + } + + if (expected === null || actual === null) { + throw new Error('Cannot use toBeCloseTo with null. Arguments evaluated to: ' + + 'expect(' + actual + ').toBeCloseTo(' + expected + ').' + ); + } + + var pow = Math.pow(10, precision + 1); + var delta = Math.abs(expected - actual); + var maxDelta = Math.pow(10, -precision) / 2; + + return { + pass: Math.round(delta * pow) <= maxDelta * pow + }; + } + }; + } + + return toBeCloseTo; +}; + +getJasmineRequireObj().toBeDefined = function() { + /** + * {@link expect} the actual value to be defined. (Not `undefined`) + * @function + * @name matchers#toBeDefined + * @since 1.3.0 + * @example + * expect(result).toBeDefined(); + */ + function toBeDefined() { + return { + compare: function(actual) { + return { + pass: (void 0 !== actual) + }; + } + }; + } + + return toBeDefined; +}; + +getJasmineRequireObj().toBeFalse = function() { + /** + * {@link expect} the actual value to be `false`. + * @function + * @name matchers#toBeFalse + * @since 3.5.0 + * @example + * expect(result).toBeFalse(); + */ + function toBeFalse() { + return { + compare: function(actual) { + return { + pass: actual === false + }; + } + }; + } + + return toBeFalse; +}; + +getJasmineRequireObj().toBeFalsy = function() { + /** + * {@link expect} the actual value to be falsy + * @function + * @name matchers#toBeFalsy + * @since 2.0.0 + * @example + * expect(result).toBeFalsy(); + */ + function toBeFalsy() { + return { + compare: function(actual) { + return { + pass: !actual + }; + } + }; + } + + return toBeFalsy; +}; + +getJasmineRequireObj().toBeGreaterThan = function() { + /** + * {@link expect} the actual value to be greater than the expected value. + * @function + * @name matchers#toBeGreaterThan + * @since 2.0.0 + * @param {Number} expected - The value to compare against. + * @example + * expect(result).toBeGreaterThan(3); + */ + function toBeGreaterThan() { + return { + compare: function(actual, expected) { + return { + pass: actual > expected + }; + } + }; + } + + return toBeGreaterThan; +}; + + +getJasmineRequireObj().toBeGreaterThanOrEqual = function() { + /** + * {@link expect} the actual value to be greater than or equal to the expected value. + * @function + * @name matchers#toBeGreaterThanOrEqual + * @since 2.0.0 + * @param {Number} expected - The expected value to compare against. + * @example + * expect(result).toBeGreaterThanOrEqual(25); + */ + function toBeGreaterThanOrEqual() { + return { + compare: function(actual, expected) { + return { + pass: actual >= expected + }; + } + }; + } + + return toBeGreaterThanOrEqual; +}; + +getJasmineRequireObj().toBeInstanceOf = function(j$) { + var usageError = j$.formatErrorMsg('', 'expect(value).toBeInstanceOf()'); + + /** + * {@link expect} the actual to be an instance of the expected class + * @function + * @name matchers#toBeInstanceOf + * @since 3.5.0 + * @param {Object} expected - The class or constructor function to check for + * @example + * expect('foo').toBeInstanceOf(String); + * expect(3).toBeInstanceOf(Number); + * expect(new Error()).toBeInstanceOf(Error); + */ + function toBeInstanceOf(util, customEqualityTesters) { + return { + compare: function(actual, expected) { + var actualType = actual && actual.constructor ? j$.fnNameFor(actual.constructor) : j$.pp(actual), + expectedType = expected ? j$.fnNameFor(expected) : j$.pp(expected), + expectedMatcher, + pass; + + try { + expectedMatcher = new j$.Any(expected); + pass = expectedMatcher.asymmetricMatch(actual); + } catch (error) { + throw new Error(usageError('Expected value is not a constructor function')); + } + + if (pass) { + return { + pass: true, + message: 'Expected instance of ' + actualType + ' not to be an instance of ' + expectedType + }; + } else { + return { + pass: false, + message: 'Expected instance of ' + actualType + ' to be an instance of ' + expectedType + }; + } + } + }; + } + + return toBeInstanceOf; +}; + +getJasmineRequireObj().toBeLessThan = function() { + /** + * {@link expect} the actual value to be less than the expected value. + * @function + * @name matchers#toBeLessThan + * @since 2.0.0 + * @param {Number} expected - The expected value to compare against. + * @example + * expect(result).toBeLessThan(0); + */ + function toBeLessThan() { + return { + + compare: function(actual, expected) { + return { + pass: actual < expected + }; + } + }; + } + + return toBeLessThan; +}; + +getJasmineRequireObj().toBeLessThanOrEqual = function() { + /** + * {@link expect} the actual value to be less than or equal to the expected value. + * @function + * @name matchers#toBeLessThanOrEqual + * @since 2.0.0 + * @param {Number} expected - The expected value to compare against. + * @example + * expect(result).toBeLessThanOrEqual(123); + */ + function toBeLessThanOrEqual() { + return { + + compare: function(actual, expected) { + return { + pass: actual <= expected + }; + } + }; + } + + return toBeLessThanOrEqual; +}; + +getJasmineRequireObj().toBeNaN = function(j$) { + /** + * {@link expect} the actual value to be `NaN` (Not a Number). + * @function + * @name matchers#toBeNaN + * @since 1.3.0 + * @example + * expect(thing).toBeNaN(); + */ + function toBeNaN() { + return { + compare: function(actual) { + var result = { + pass: (actual !== actual) + }; + + if (result.pass) { + result.message = 'Expected actual not to be NaN.'; + } else { + result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; }; + } + + return result; + } + }; + } + + return toBeNaN; +}; + +getJasmineRequireObj().toBeNegativeInfinity = function(j$) { + /** + * {@link expect} the actual value to be `-Infinity` (-infinity). + * @function + * @name matchers#toBeNegativeInfinity + * @since 2.6.0 + * @example + * expect(thing).toBeNegativeInfinity(); + */ + function toBeNegativeInfinity() { + return { + compare: function(actual) { + var result = { + pass: (actual === Number.NEGATIVE_INFINITY) + }; + + if (result.pass) { + result.message = 'Expected actual not to be -Infinity.'; + } else { + result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be -Infinity.'; }; + } + + return result; + } + }; + } + + return toBeNegativeInfinity; +}; + +getJasmineRequireObj().toBeNull = function() { + /** + * {@link expect} the actual value to be `null`. + * @function + * @name matchers#toBeNull + * @since 1.3.0 + * @example + * expect(result).toBeNull(); + */ + function toBeNull() { + return { + compare: function(actual) { + return { + pass: actual === null + }; + } + }; + } + + return toBeNull; +}; + +getJasmineRequireObj().toBePositiveInfinity = function(j$) { + /** + * {@link expect} the actual value to be `Infinity` (infinity). + * @function + * @name matchers#toBePositiveInfinity + * @since 2.6.0 + * @example + * expect(thing).toBePositiveInfinity(); + */ + function toBePositiveInfinity() { + return { + compare: function(actual) { + var result = { + pass: (actual === Number.POSITIVE_INFINITY) + }; + + if (result.pass) { + result.message = 'Expected actual not to be Infinity.'; + } else { + result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be Infinity.'; }; + } + + return result; + } + }; + } + + return toBePositiveInfinity; +}; + +getJasmineRequireObj().toBeTrue = function() { + /** + * {@link expect} the actual value to be `true`. + * @function + * @name matchers#toBeTrue + * @since 3.5.0 + * @example + * expect(result).toBeTrue(); + */ + function toBeTrue() { + return { + compare: function(actual) { + return { + pass: actual === true + }; + } + }; + } + + return toBeTrue; +}; + +getJasmineRequireObj().toBeTruthy = function() { + /** + * {@link expect} the actual value to be truthy. + * @function + * @name matchers#toBeTruthy + * @since 2.0.0 + * @example + * expect(thing).toBeTruthy(); + */ + function toBeTruthy() { + return { + compare: function(actual) { + return { + pass: !!actual + }; + } + }; + } + + return toBeTruthy; +}; + +getJasmineRequireObj().toBeUndefined = function() { + /** + * {@link expect} the actual value to be `undefined`. + * @function + * @name matchers#toBeUndefined + * @since 1.3.0 + * @example + * expect(result).toBeUndefined(): + */ + function toBeUndefined() { + return { + compare: function(actual) { + return { + pass: void 0 === actual + }; + } + }; + } + + return toBeUndefined; +}; + +getJasmineRequireObj().toContain = function() { + /** + * {@link expect} the actual value to contain a specific value. + * @function + * @name matchers#toContain + * @since 2.0.0 + * @param {Object} expected - The value to look for. + * @example + * expect(array).toContain(anElement); + * expect(string).toContain(substring); + */ + function toContain(util, customEqualityTesters) { + customEqualityTesters = customEqualityTesters || []; + + return { + compare: function(actual, expected) { + + return { + pass: util.contains(actual, expected, customEqualityTesters) + }; + } + }; + } + + return toContain; +}; + +getJasmineRequireObj().toEqual = function(j$) { + /** + * {@link expect} the actual value to be equal to the expected, using deep equality comparison. + * @function + * @name matchers#toEqual + * @since 1.3.0 + * @param {Object} expected - Expected value + * @example + * expect(bigObject).toEqual({"foo": ['bar', 'baz']}); + */ + function toEqual(util, customEqualityTesters) { + customEqualityTesters = customEqualityTesters || []; + + return { + compare: function(actual, expected) { + var result = { + pass: false + }, + diffBuilder = j$.DiffBuilder(); + + result.pass = util.equals(actual, expected, customEqualityTesters, diffBuilder); + + // TODO: only set error message if test fails + result.message = diffBuilder.getMessage(); + + return result; + } + }; + } + + return toEqual; +}; + +getJasmineRequireObj().toHaveBeenCalled = function(j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalled()'); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called. + * @function + * @name matchers#toHaveBeenCalled + * @since 1.3.0 + * @example + * expect(mySpy).toHaveBeenCalled(); + * expect(mySpy).not.toHaveBeenCalled(); + */ + function toHaveBeenCalled() { + return { + compare: function(actual) { + var result = {}; + + if (!j$.isSpy(actual)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); + } + + if (arguments.length > 1) { + throw new Error(getErrorMsg('Does not take arguments, use toHaveBeenCalledWith')); + } + + result.pass = actual.calls.any(); + + result.message = result.pass ? + 'Expected spy ' + actual.and.identity + ' not to have been called.' : + 'Expected spy ' + actual.and.identity + ' to have been called.'; + + return result; + } + }; + } + + return toHaveBeenCalled; +}; + +getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledBefore()'); + + /** + * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}. + * @function + * @name matchers#toHaveBeenCalledBefore + * @since 2.6.0 + * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}. + * @example + * expect(mySpy).toHaveBeenCalledBefore(otherSpy); + */ + function toHaveBeenCalledBefore() { + return { + compare: function(firstSpy, latterSpy) { + if (!j$.isSpy(firstSpy)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(firstSpy) + '.')); + } + if (!j$.isSpy(latterSpy)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(latterSpy) + '.')); + } + + var result = { pass: false }; + + if (!firstSpy.calls.count()) { + result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called.'; + return result; + } + if (!latterSpy.calls.count()) { + result.message = 'Expected spy ' + latterSpy.and.identity + ' to have been called.'; + return result; + } + + var latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder; + var first2ndSpyCall = latterSpy.calls.first().invocationOrder; + + result.pass = latest1stSpyCall < first2ndSpyCall; + + if (result.pass) { + result.message = 'Expected spy ' + firstSpy.and.identity + ' to not have been called before spy ' + latterSpy.and.identity + ', but it was'; + } else { + var first1stSpyCall = firstSpy.calls.first().invocationOrder; + var latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder; + + if(first1stSpyCall < first2ndSpyCall) { + result.message = 'Expected latest call to spy ' + firstSpy.and.identity + ' to have been called before first call to spy ' + latterSpy.and.identity + ' (no interleaved calls)'; + } else if (latest2ndSpyCall > latest1stSpyCall) { + result.message = 'Expected first call to spy ' + latterSpy.and.identity + ' to have been called after latest call to spy ' + firstSpy.and.identity + ' (no interleaved calls)'; + } else { + result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called before spy ' + latterSpy.and.identity; + } + } + + return result; + } + }; + } + + return toHaveBeenCalledBefore; +}; + +getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledTimes()'); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times. + * @function + * @name matchers#toHaveBeenCalledTimes + * @since 2.4.0 + * @param {Number} expected - The number of invocations to look for. + * @example + * expect(mySpy).toHaveBeenCalledTimes(3); + */ + function toHaveBeenCalledTimes() { + return { + compare: function(actual, expected) { + if (!j$.isSpy(actual)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); + } + + var args = Array.prototype.slice.call(arguments, 0), + result = { pass: false }; + + if (!j$.isNumber_(expected)) { + throw new Error(getErrorMsg('The expected times failed is a required argument and must be a number.')); + } + + actual = args[0]; + var calls = actual.calls.count(); + var timesMessage = expected === 1 ? 'once' : expected + ' times'; + result.pass = calls === expected; + result.message = result.pass ? + 'Expected spy ' + actual.and.identity + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' : + 'Expected spy ' + actual.and.identity + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.'; + return result; + } + }; + } + + return toHaveBeenCalledTimes; +}; + +getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledWith(...arguments)'); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once. + * @function + * @name matchers#toHaveBeenCalledWith + * @since 1.3.0 + * @param {...Object} - The arguments to look for + * @example + * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2); + */ + function toHaveBeenCalledWith(util, customEqualityTesters) { + return { + compare: function() { + var args = Array.prototype.slice.call(arguments, 0), + actual = args[0], + expectedArgs = args.slice(1), + result = { pass: false }; + + if (!j$.isSpy(actual)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); + } + + if (!actual.calls.any()) { + result.message = function() { + return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + + ' ' + j$.pp(expectedArgs) + + '\nbut it was never called.'; + }; + return result; + } + + if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) { + result.pass = true; + result.message = function() { + return 'Expected spy ' + actual.and.identity + ' not to have been called with:\n' + + ' ' + j$.pp(expectedArgs) + + '\nbut it was.'; + }; + } else { + result.message = function() { + var prettyPrintedCalls = actual.calls.allArgs().map(function(argsForCall) { + return ' ' + j$.pp(argsForCall); + }); + + var diffs = actual.calls.allArgs().map(function(argsForCall, callIx) { + var diffBuilder = new j$.DiffBuilder(); + util.equals(argsForCall, expectedArgs, customEqualityTesters, diffBuilder); + return 'Call ' + callIx + ':\n' + + diffBuilder.getMessage().replace(/^/mg, ' '); + }); + + return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + + ' ' + j$.pp(expectedArgs) + '\n' + '' + + 'but actual calls were:\n' + + prettyPrintedCalls.join(',\n') + '.\n\n' + + diffs.join('\n'); + }; + } + + return result; + } + }; + } + + return toHaveBeenCalledWith; +}; + +getJasmineRequireObj().toHaveClass = function(j$) { + /** + * {@link expect} the actual value to be a DOM element that has the expected class + * @function + * @name matchers#toHaveClass + * @since 3.0.0 + * @param {Object} expected - The class name to test for + * @example + * var el = document.createElement('div'); + * el.className = 'foo bar baz'; + * expect(el).toHaveClass('bar'); + */ + function toHaveClass(util, customEqualityTesters) { + return { + compare: function(actual, expected) { + if (!isElement(actual)) { + throw new Error(j$.pp(actual) + ' is not a DOM element'); + } + + return { + pass: actual.classList.contains(expected) + }; + } + }; + } + + function isElement(maybeEl) { + return maybeEl && + maybeEl.classList && + j$.isFunction_(maybeEl.classList.contains); + } + + return toHaveClass; +}; + +getJasmineRequireObj().toMatch = function(j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toMatch( || )'); + + /** + * {@link expect} the actual value to match a regular expression + * @function + * @name matchers#toMatch + * @since 1.3.0 + * @param {RegExp|String} expected - Value to look for in the string. + * @example + * expect("my string").toMatch(/string$/); + * expect("other string").toMatch("her"); + */ + function toMatch() { + return { + compare: function(actual, expected) { + if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { + throw new Error(getErrorMsg('Expected is not a String or a RegExp')); + } + + var regexp = new RegExp(expected); + + return { + pass: regexp.test(actual) + }; + } + }; + } + + return toMatch; +}; + +getJasmineRequireObj().toThrow = function(j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrow()'); + + /** + * {@link expect} a function to `throw` something. + * @function + * @name matchers#toThrow + * @since 2.0.0 + * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked. + * @example + * expect(function() { return 'things'; }).toThrow('foo'); + * expect(function() { return 'stuff'; }).toThrow(); + */ + function toThrow(util) { + return { + compare: function(actual, expected) { + var result = { pass: false }, + threw = false, + thrown; + + if (typeof actual != 'function') { + throw new Error(getErrorMsg('Actual is not a Function')); + } + + try { + actual(); + } catch (e) { + threw = true; + thrown = e; + } + + if (!threw) { + result.message = 'Expected function to throw an exception.'; + return result; + } + + if (arguments.length == 1) { + result.pass = true; + result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; }; + + return result; + } + + if (util.equals(thrown, expected)) { + result.pass = true; + result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; }; + } else { + result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; }; + } + + return result; + } + }; + } + + return toThrow; +}; + +getJasmineRequireObj().toThrowError = function(j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrowError(, )'); + + /** + * {@link expect} a function to `throw` an `Error`. + * @function + * @name matchers#toThrowError + * @since 2.0.0 + * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. + * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` + * @example + * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message'); + * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/); + * expect(function() { return 'stuff'; }).toThrowError(MyCustomError); + * expect(function() { return 'other'; }).toThrowError(/foo/); + * expect(function() { return 'other'; }).toThrowError(); + */ + function toThrowError () { + return { + compare: function(actual) { + var errorMatcher = getMatcher.apply(null, arguments), + thrown; + + if (typeof actual != 'function') { + throw new Error(getErrorMsg('Actual is not a Function')); + } + + try { + actual(); + return fail('Expected function to throw an Error.'); + } catch (e) { + thrown = e; + } + + if (!j$.isError_(thrown)) { + return fail(function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; }); + } + + return errorMatcher.match(thrown); + } + }; + + function getMatcher() { + var expected, errorType; + + if (arguments[2]) { + errorType = arguments[1]; + expected = arguments[2]; + if (!isAnErrorType(errorType)) { + throw new Error(getErrorMsg('Expected error type is not an Error.')); + } + + return exactMatcher(expected, errorType); + } else if (arguments[1]) { + expected = arguments[1]; + + if (isAnErrorType(arguments[1])) { + return exactMatcher(null, arguments[1]); + } else { + return exactMatcher(arguments[1], null); + } + } else { + return anyMatcher(); + } + } + + function anyMatcher() { + return { + match: function(error) { + return pass('Expected function not to throw an Error, but it threw ' + j$.fnNameFor(error) + '.'); + } + }; + } + + function exactMatcher(expected, errorType) { + if (expected && !isStringOrRegExp(expected)) { + if (errorType) { + throw new Error(getErrorMsg('Expected error message is not a string or RegExp.')); + } else { + throw new Error(getErrorMsg('Expected is not an Error, string, or RegExp.')); + } + } + + function messageMatch(message) { + if (typeof expected == 'string') { + return expected == message; + } else { + return expected.test(message); + } + } + + var errorTypeDescription = errorType ? j$.fnNameFor(errorType) : 'an exception'; + + function thrownDescription(thrown) { + var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception', + thrownMessage = ''; + + if (expected) { + thrownMessage = ' with message ' + j$.pp(thrown.message); + } + + return thrownName + thrownMessage; + } + + function messageDescription() { + if (expected === null) { + return ''; + } else if (expected instanceof RegExp) { + return ' with a message matching ' + j$.pp(expected); + } else { + return ' with message ' + j$.pp(expected); + } + } + + function matches(error) { + return (errorType === null || error instanceof errorType) && + (expected === null || messageMatch(error.message)); + } + + return { + match: function(thrown) { + if (matches(thrown)) { + return pass(function() { + return 'Expected function not to throw ' + errorTypeDescription + messageDescription() + '.'; + }); + } else { + return fail(function() { + return 'Expected function to throw ' + errorTypeDescription + messageDescription() + + ', but it threw ' + thrownDescription(thrown) + '.'; + }); + } + } + }; + } + + function isStringOrRegExp(potential) { + return potential instanceof RegExp || (typeof potential == 'string'); + } + + function isAnErrorType(type) { + if (typeof type !== 'function') { + return false; + } + + var Surrogate = function() {}; + Surrogate.prototype = type.prototype; + return j$.isError_(new Surrogate()); + } + } + + function pass(message) { + return { + pass: true, + message: message + }; + } + + function fail(message) { + return { + pass: false, + message: message + }; + } + + return toThrowError; +}; + +getJasmineRequireObj().toThrowMatching = function(j$) { + var usageError = j$.formatErrorMsg('', 'expect(function() {}).toThrowMatching()'); + + /** + * {@link expect} a function to `throw` something matching a predicate. + * @function + * @name matchers#toThrowMatching + * @since 3.0.0 + * @param {Function} predicate - A function that takes the thrown exception as its parameter and returns true if it matches. + * @example + * expect(function() { throw new Error('nope'); }).toThrowMatching(function(thrown) { return thrown.message === 'nope'; }); + */ + function toThrowMatching() { + return { + compare: function(actual, predicate) { + var thrown; + + if (typeof actual !== 'function') { + throw new Error(usageError('Actual is not a Function')); + } + + if (typeof predicate !== 'function') { + throw new Error(usageError('Predicate is not a Function')); + } + + try { + actual(); + return fail('Expected function to throw an exception.'); + } catch (e) { + thrown = e; + } + + if (predicate(thrown)) { + return pass('Expected function not to throw an exception matching a predicate.'); + } else { + return fail(function() { + return 'Expected function to throw an exception matching a predicate, ' + + 'but it threw ' + thrownDescription(thrown) + '.'; + }); + } + } + }; + } + + function thrownDescription(thrown) { + if (thrown && thrown.constructor) { + return j$.fnNameFor(thrown.constructor) + ' with message ' + + j$.pp(thrown.message); + } else { + return j$.pp(thrown); + } + } + + function pass(message) { + return { + pass: true, + message: message + }; + } + + function fail(message) { + return { + pass: false, + message: message + }; + } + + return toThrowMatching; +}; + +getJasmineRequireObj().MockDate = function() { + function MockDate(global) { + var self = this; + var currentTime = 0; + + if (!global || !global.Date) { + self.install = function() {}; + self.tick = function() {}; + self.uninstall = function() {}; + return self; + } + + var GlobalDate = global.Date; + + self.install = function(mockDate) { + if (mockDate instanceof GlobalDate) { + currentTime = mockDate.getTime(); + } else { + currentTime = new GlobalDate().getTime(); + } + + global.Date = FakeDate; + }; + + self.tick = function(millis) { + millis = millis || 0; + currentTime = currentTime + millis; + }; + + self.uninstall = function() { + currentTime = 0; + global.Date = GlobalDate; + }; + + createDateProperties(); + + return self; + + function FakeDate() { + switch (arguments.length) { + case 0: + return new GlobalDate(currentTime); + case 1: + return new GlobalDate(arguments[0]); + case 2: + return new GlobalDate(arguments[0], arguments[1]); + case 3: + return new GlobalDate(arguments[0], arguments[1], arguments[2]); + case 4: + return new GlobalDate( + arguments[0], + arguments[1], + arguments[2], + arguments[3] + ); + case 5: + return new GlobalDate( + arguments[0], + arguments[1], + arguments[2], + arguments[3], + arguments[4] + ); + case 6: + return new GlobalDate( + arguments[0], + arguments[1], + arguments[2], + arguments[3], + arguments[4], + arguments[5] + ); + default: + return new GlobalDate( + arguments[0], + arguments[1], + arguments[2], + arguments[3], + arguments[4], + arguments[5], + arguments[6] + ); + } + } + + function createDateProperties() { + FakeDate.prototype = GlobalDate.prototype; + + FakeDate.now = function() { + if (GlobalDate.now) { + return currentTime; + } else { + throw new Error('Browser does not support Date.now()'); + } + }; + + FakeDate.toSource = GlobalDate.toSource; + FakeDate.toString = GlobalDate.toString; + FakeDate.parse = GlobalDate.parse; + FakeDate.UTC = GlobalDate.UTC; + } + } + + return MockDate; +}; + +getJasmineRequireObj().pp = function(j$) { + function PrettyPrinter() { + this.ppNestLevel_ = 0; + this.seen = []; + this.length = 0; + this.stringParts = []; + } + + function hasCustomToString(value) { + // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g. + // iframe, web worker) + try { + return ( + j$.isFunction_(value.toString) && + value.toString !== Object.prototype.toString && + value.toString() !== Object.prototype.toString.call(value) + ); + } catch (e) { + // The custom toString() threw. + return true; + } + } + + PrettyPrinter.prototype.format = function(value) { + this.ppNestLevel_++; + try { + if (j$.util.isUndefined(value)) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === 0 && 1 / value === -Infinity) { + this.emitScalar('-0'); + } else if (value === j$.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (j$.isSpy(value)) { + this.emitScalar('spy on ' + value.and.identity); + } else if (j$.isSpy(value.toString)) { + this.emitScalar('spy on ' + value.toString.and.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (j$.isDomNode(value)) { + if (value.tagName) { + this.emitDomElement(value); + } else { + this.emitScalar('HTMLNode'); + } + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (j$.isSet(value)) { + this.emitSet(value); + } else if (j$.isMap(value)) { + this.emitMap(value); + } else if (j$.isTypedArray_(value)) { + this.emitTypedArray(value); + } else if ( + value.toString && + typeof value === 'object' && + !j$.isArray_(value) && + hasCustomToString(value) + ) { + try { + this.emitScalar(value.toString()); + } catch (e) { + this.emitScalar('has-invalid-toString-method'); + } + } else if (j$.util.arrayContains(this.seen, value)) { + this.emitScalar( + '' + ); + } else if (j$.isArray_(value) || j$.isA_('Object', value)) { + this.seen.push(value); + if (j$.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + this.seen.pop(); + } else { + this.emitScalar(value.toString()); + } + } catch (e) { + if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { + throw e; + } + } finally { + this.ppNestLevel_--; + } + }; + + PrettyPrinter.prototype.iterateObject = function(obj, fn) { + var objKeys = keys(obj, j$.isArray_(obj)); + var isGetter = function isGetter(prop) {}; + + if (obj.__lookupGetter__) { + isGetter = function isGetter(prop) { + var getter = obj.__lookupGetter__(prop); + return !j$.util.isUndefined(getter) && getter !== null; + }; + } + var length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + for (var i = 0; i < length; i++) { + var property = objKeys[i]; + fn(property, isGetter(property)); + } + + return objKeys.length > length; + }; + + PrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); + }; + + PrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); + }; + + PrettyPrinter.prototype.emitArray = function(array) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Array'); + return; + } + var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + this.append('[ '); + for (var i = 0; i < length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + if (array.length > length) { + this.append(', ...'); + } + + var self = this; + var first = array.length === 0; + var truncated = this.iterateObject(array, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.formatProperty(array, property, isGetter); + }); + + if (truncated) { + this.append(', ...'); + } + + this.append(' ]'); + }; + + PrettyPrinter.prototype.emitSet = function(set) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Set'); + return; + } + this.append('Set( '); + var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + var i = 0; + set.forEach(function(value, key) { + if (i >= size) { + return; + } + if (i > 0) { + this.append(', '); + } + this.format(value); + + i++; + }, this); + if (set.size > size) { + this.append(', ...'); + } + this.append(' )'); + }; + + PrettyPrinter.prototype.emitMap = function(map) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Map'); + return; + } + this.append('Map( '); + var size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + var i = 0; + map.forEach(function(value, key) { + if (i >= size) { + return; + } + if (i > 0) { + this.append(', '); + } + this.format([key, value]); + + i++; + }, this); + if (map.size > size) { + this.append(', ...'); + } + this.append(' )'); + }; + + PrettyPrinter.prototype.emitObject = function(obj) { + var ctor = obj.constructor, + constructorName; + + constructorName = + typeof ctor === 'function' && obj instanceof ctor + ? j$.fnNameFor(obj.constructor) + : 'null'; + + this.append(constructorName); + + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + return; + } + + var self = this; + this.append('({ '); + var first = true; + + var truncated = this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.formatProperty(obj, property, isGetter); + }); + + if (truncated) { + this.append(', ...'); + } + + this.append(' })'); + }; + + PrettyPrinter.prototype.emitTypedArray = function(arr) { + var constructorName = j$.fnNameFor(arr.constructor), + limitedArray = Array.prototype.slice.call( + arr, + 0, + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH + ), + itemsString = Array.prototype.join.call(limitedArray, ', '); + + if (limitedArray.length !== arr.length) { + itemsString += ', ...'; + } + + this.append(constructorName + ' [ ' + itemsString + ' ]'); + }; + + PrettyPrinter.prototype.emitDomElement = function(el) { + var tagName = el.tagName.toLowerCase(), + attrs = el.attributes, + i, + len = attrs.length, + out = '<' + tagName, + attr; + + for (i = 0; i < len; i++) { + attr = attrs[i]; + out += ' ' + attr.name; + + if (attr.value !== '') { + out += '="' + attr.value + '"'; + } + } + + out += '>'; + + if (el.childElementCount !== 0 || el.textContent !== '') { + out += '...'; + } + + this.append(out); + }; + + PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { + this.append(property); + this.append(': '); + if (isGetter) { + this.append(''); + } else { + this.format(obj[property]); + } + }; + + PrettyPrinter.prototype.append = function(value) { + // This check protects us from the rare case where an object has overriden + // `toString()` with an invalid implementation (returning a non-string). + if (typeof value !== 'string') { + value = Object.prototype.toString.call(value); + } + + var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); + this.length += result.value.length; + this.stringParts.push(result.value); + + if (result.truncated) { + throw new MaxCharsReachedError(); + } + }; + + function truncate(s, maxlen) { + if (s.length <= maxlen) { + return { value: s, truncated: false }; + } + + s = s.substring(0, maxlen - 4) + ' ...'; + return { value: s, truncated: true }; + } + + function MaxCharsReachedError() { + this.message = + 'Exceeded ' + + j$.MAX_PRETTY_PRINT_CHARS + + ' characters while pretty-printing a value'; + } + + MaxCharsReachedError.prototype = new Error(); + + function keys(obj, isArray) { + var allKeys = Object.keys + ? Object.keys(obj) + : (function(o) { + var keys = []; + for (var key in o) { + if (j$.util.has(o, key)) { + keys.push(key); + } + } + return keys; + })(obj); + + if (!isArray) { + return allKeys; + } + + if (allKeys.length === 0) { + return allKeys; + } + + var extraKeys = []; + for (var i = 0; i < allKeys.length; i++) { + if (!/^[0-9]+$/.test(allKeys[i])) { + extraKeys.push(allKeys[i]); + } + } + + return extraKeys; + } + return function(value) { + var prettyPrinter = new PrettyPrinter(); + prettyPrinter.format(value); + return prettyPrinter.stringParts.join(''); + }; +}; + +getJasmineRequireObj().QueueRunner = function(j$) { + function StopExecutionError() {} + StopExecutionError.prototype = new Error(); + j$.StopExecutionError = StopExecutionError; + + function once(fn) { + var called = false; + return function(arg) { + if (!called) { + called = true; + // Direct call using single parameter, because cleanup/next does not need more + fn(arg); + } + return null; + }; + } + + function emptyFn() {} + + function QueueRunner(attrs) { + var queueableFns = attrs.queueableFns || []; + this.queueableFns = queueableFns.concat(attrs.cleanupFns || []); + this.firstCleanupIx = queueableFns.length; + this.onComplete = attrs.onComplete || emptyFn; + this.clearStack = + attrs.clearStack || + function(fn) { + fn(); + }; + this.onException = attrs.onException || emptyFn; + this.userContext = attrs.userContext || new j$.UserContext(); + this.timeout = attrs.timeout || { + setTimeout: setTimeout, + clearTimeout: clearTimeout + }; + this.fail = attrs.fail || emptyFn; + this.globalErrors = attrs.globalErrors || { + pushListener: emptyFn, + popListener: emptyFn + }; + this.completeOnFirstError = !!attrs.completeOnFirstError; + this.errored = false; + + if (typeof this.onComplete !== 'function') { + throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); + } + this.deprecated = attrs.deprecated; + } + + QueueRunner.prototype.execute = function() { + var self = this; + this.handleFinalError = function(message, source, lineno, colno, error) { + // Older browsers would send the error as the first parameter. HTML5 + // specifies the the five parameters above. The error instance should + // be preffered, otherwise the call stack would get lost. + self.onException(error || message); + }; + this.globalErrors.pushListener(this.handleFinalError); + this.run(0); + }; + + QueueRunner.prototype.skipToCleanup = function(lastRanIndex) { + if (lastRanIndex < this.firstCleanupIx) { + this.run(this.firstCleanupIx); + } else { + this.run(lastRanIndex + 1); + } + }; + + QueueRunner.prototype.clearTimeout = function(timeoutId) { + Function.prototype.apply.apply(this.timeout.clearTimeout, [ + j$.getGlobal(), + [timeoutId] + ]); + }; + + QueueRunner.prototype.setTimeout = function(fn, timeout) { + return Function.prototype.apply.apply(this.timeout.setTimeout, [ + j$.getGlobal(), + [fn, timeout] + ]); + }; + + QueueRunner.prototype.attempt = function attempt(iterativeIndex) { + var self = this, + completedSynchronously = true, + handleError = function handleError(error) { + onException(error); + next(error); + }, + cleanup = once(function cleanup() { + if (timeoutId !== void 0) { + self.clearTimeout(timeoutId); + } + self.globalErrors.popListener(handleError); + }), + next = once(function next(err) { + cleanup(); + + if (j$.isError_(err)) { + if (!(err instanceof StopExecutionError) && !err.jasmineMessage) { + self.fail(err); + } + self.errored = errored = true; + } + + function runNext() { + if (self.completeOnFirstError && errored) { + self.skipToCleanup(iterativeIndex); + } else { + self.run(iterativeIndex + 1); + } + } + + if (completedSynchronously) { + self.setTimeout(runNext); + } else { + runNext(); + } + }), + errored = false, + queueableFn = self.queueableFns[iterativeIndex], + timeoutId; + + next.fail = function nextFail() { + self.fail.apply(null, arguments); + self.errored = errored = true; + next(); + }; + + self.globalErrors.pushListener(handleError); + + if (queueableFn.timeout !== undefined) { + var timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; + timeoutId = self.setTimeout(function() { + var error = new Error( + 'Timeout - Async function did not complete within ' + + timeoutInterval + + 'ms ' + + (queueableFn.timeout + ? '(custom timeout)' + : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') + ); + onException(error); + next(); + }, timeoutInterval); + } + + try { + if (queueableFn.fn.length === 0) { + var maybeThenable = queueableFn.fn.call(self.userContext); + + if (maybeThenable && j$.isFunction_(maybeThenable.then)) { + maybeThenable.then(next, onPromiseRejection); + completedSynchronously = false; + return { completedSynchronously: false }; + } + } else { + queueableFn.fn.call(self.userContext, next); + completedSynchronously = false; + return { completedSynchronously: false }; + } + } catch (e) { + onException(e); + self.errored = errored = true; + } + + cleanup(); + return { completedSynchronously: true, errored: errored }; + + function onException(e) { + self.onException(e); + self.errored = errored = true; + } + + function onPromiseRejection(e) { + onException(e); + next(); + } + }; + + QueueRunner.prototype.run = function(recursiveIndex) { + var length = this.queueableFns.length, + self = this, + iterativeIndex; + + for ( + iterativeIndex = recursiveIndex; + iterativeIndex < length; + iterativeIndex++ + ) { + var result = this.attempt(iterativeIndex); + + if (!result.completedSynchronously) { + return; + } + + self.errored = self.errored || result.errored; + + if (this.completeOnFirstError && result.errored) { + this.skipToCleanup(iterativeIndex); + return; + } + } + + this.clearStack(function() { + self.globalErrors.popListener(self.handleFinalError); + self.onComplete(self.errored && new StopExecutionError()); + }); + }; + + return QueueRunner; +}; + +getJasmineRequireObj().ReportDispatcher = function(j$) { + function ReportDispatcher(methods, queueRunnerFactory) { + var dispatchedMethods = methods || []; + + for (var i = 0; i < dispatchedMethods.length; i++) { + var method = dispatchedMethods[i]; + this[method] = (function(m) { + return function() { + dispatch(m, arguments); + }; + })(method); + } + + var reporters = []; + var fallbackReporter = null; + + this.addReporter = function(reporter) { + reporters.push(reporter); + }; + + this.provideFallbackReporter = function(reporter) { + fallbackReporter = reporter; + }; + + this.clearReporters = function() { + reporters = []; + }; + + return this; + + function dispatch(method, args) { + if (reporters.length === 0 && fallbackReporter !== null) { + reporters.push(fallbackReporter); + } + var onComplete = args[args.length - 1]; + args = j$.util.argsToArray(args).splice(0, args.length - 1); + var fns = []; + for (var i = 0; i < reporters.length; i++) { + var reporter = reporters[i]; + addFn(fns, reporter, method, args); + } + + queueRunnerFactory({ + queueableFns: fns, + onComplete: onComplete, + isReporter: true + }); + } + + function addFn(fns, reporter, method, args) { + var fn = reporter[method]; + if (!fn) { + return; + } + + var thisArgs = j$.util.cloneArgs(args); + if (fn.length <= 1) { + fns.push({ + fn: function() { + return fn.apply(reporter, thisArgs); + } + }); + } else { + fns.push({ + fn: function(done) { + return fn.apply(reporter, thisArgs.concat([done])); + } + }); + } + } + } + + return ReportDispatcher; +}; + +getJasmineRequireObj().interface = function(jasmine, env) { + var jasmineInterface = { + /** + * Callback passed to parts of the Jasmine base interface. + * + * By default Jasmine assumes this function completes synchronously. + * If you have code that you need to test asynchronously, you can declare that you receive a `done` callback, return a Promise, or use the `async` keyword if it is supported in your environment. + * @callback implementationCallback + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + */ + + /** + * Create a group of specs (often called a suite). + * + * Calls to `describe` can be nested within other calls to compose your suite as a tree. + * @name describe + * @since 1.3.0 + * @function + * @global + * @param {String} description Textual description of the group + * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs + */ + describe: function(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + /** + * A temporarily disabled [`describe`]{@link describe} + * + * Specs within an `xdescribe` will be marked pending and not executed + * @name xdescribe + * @since 1.3.0 + * @function + * @global + * @param {String} description Textual description of the group + * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs + */ + xdescribe: function(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + /** + * A focused [`describe`]{@link describe} + * + * If suites or specs are focused, only those that are focused will be executed + * @see fit + * @name fdescribe + * @since 2.1.0 + * @function + * @global + * @param {String} description Textual description of the group + * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs + */ + fdescribe: function(description, specDefinitions) { + return env.fdescribe(description, specDefinitions); + }, + + /** + * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code. + * + * A spec whose expectations all succeed will be passing and a spec with any failures will fail. + * @name it + * @since 1.3.0 + * @function + * @global + * @param {String} description Textual description of what this spec is checking + * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. + * @see async + */ + it: function() { + return env.it.apply(env, arguments); + }, + + /** + * A temporarily disabled [`it`]{@link it} + * + * The spec will report as `pending` and will not be executed. + * @name xit + * @since 1.3.0 + * @function + * @global + * @param {String} description Textual description of what this spec is checking. + * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. + */ + xit: function() { + return env.xit.apply(env, arguments); + }, + + /** + * A focused [`it`]{@link it} + * + * If suites or specs are focused, only those that are focused will be executed. + * @name fit + * @since 2.1.0 + * @function + * @global + * @param {String} description Textual description of what this spec is checking. + * @param {implementationCallback} testFunction Function that contains the code of your test. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. + * @see async + */ + fit: function() { + return env.fit.apply(env, arguments); + }, + + /** + * Run some shared setup before each of the specs in the {@link describe} in which it is called. + * @name beforeEach + * @since 1.3.0 + * @function + * @global + * @param {implementationCallback} [function] Function that contains the code to setup your specs. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. + * @see async + */ + beforeEach: function() { + return env.beforeEach.apply(env, arguments); + }, + + /** + * Run some shared teardown after each of the specs in the {@link describe} in which it is called. + * @name afterEach + * @since 1.3.0 + * @function + * @global + * @param {implementationCallback} [function] Function that contains the code to teardown your specs. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. + * @see async + */ + afterEach: function() { + return env.afterEach.apply(env, arguments); + }, + + /** + * Run some shared setup once before all of the specs in the {@link describe} are run. + * + * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. + * @name beforeAll + * @since 2.1.0 + * @function + * @global + * @param {implementationCallback} [function] Function that contains the code to setup your specs. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. + * @see async + */ + beforeAll: function() { + return env.beforeAll.apply(env, arguments); + }, + + /** + * Run some shared teardown once after all of the specs in the {@link describe} are run. + * + * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. + * @name afterAll + * @since 2.1.0 + * @function + * @global + * @param {implementationCallback} [function] Function that contains the code to teardown your specs. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. + * @see async + */ + afterAll: function() { + return env.afterAll.apply(env, arguments); + }, + + /** + * Create an expectation for a spec. + * @name expect + * @since 1.3.0 + * @function + * @global + * @param {Object} actual - Actual computed value to test expectations against. + * @return {matchers} + */ + expect: function(actual) { + return env.expect(actual); + }, + + /** + * Create an asynchronous expectation for a spec. Note that the matchers + * that are provided by an asynchronous expectation all return promises + * which must be either returned from the spec or waited for using `await` + * in order for Jasmine to associate them with the correct spec. + * @name expectAsync + * @since 3.3.0 + * @function + * @global + * @param {Object} actual - Actual computed value to test expectations against. + * @return {async-matchers} + * @example + * await expectAsync(somePromise).toBeResolved(); + * @example + * return expectAsync(somePromise).toBeResolved(); + */ + expectAsync: function(actual) { + return env.expectAsync(actual); + }, + + /** + * Mark a spec as pending, expectation results will be ignored. + * @name pending + * @since 2.0.0 + * @function + * @global + * @param {String} [message] - Reason the spec is pending. + */ + pending: function() { + return env.pending.apply(env, arguments); + }, + + /** + * Explicitly mark a spec as failed. + * @name fail + * @since 2.1.0 + * @function + * @global + * @param {String|Error} [error] - Reason for the failure. + */ + fail: function() { + return env.fail.apply(env, arguments); + }, + + /** + * Install a spy onto an existing object. + * @name spyOn + * @since 1.3.0 + * @function + * @global + * @param {Object} obj - The object upon which to install the {@link Spy}. + * @param {String} methodName - The name of the method to replace with a {@link Spy}. + * @returns {Spy} + */ + spyOn: function(obj, methodName) { + return env.spyOn(obj, methodName); + }, + + /** + * Install a spy on a property installed with `Object.defineProperty` onto an existing object. + * @name spyOnProperty + * @since 2.6.0 + * @function + * @global + * @param {Object} obj - The object upon which to install the {@link Spy} + * @param {String} propertyName - The name of the property to replace with a {@link Spy}. + * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. + * @returns {Spy} + */ + spyOnProperty: function(obj, methodName, accessType) { + return env.spyOnProperty(obj, methodName, accessType); + }, + + /** + * Installs spies on all writable and configurable properties of an object. + * @name spyOnAllFunctions + * @since 3.2.1 + * @function + * @global + * @param {Object} obj - The object upon which to install the {@link Spy}s + * @returns {Object} the spied object + */ + spyOnAllFunctions: function(obj) { + return env.spyOnAllFunctions(obj); + }, + + jsApiReporter: new jasmine.JsApiReporter({ + timer: new jasmine.Timer() + }), + + /** + * @namespace jasmine + */ + jasmine: jasmine + }; + + /** + * Add a custom equality tester for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addCustomEqualityTester + * @since 2.0.0 + * @function + * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise. + * @see custom_equality + */ + jasmine.addCustomEqualityTester = function(tester) { + env.addCustomEqualityTester(tester); + }; + + /** + * Add custom matchers for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addMatchers + * @since 2.0.0 + * @function + * @param {Object} matchers - Keys from this object will be the new matcher names. + * @see custom_matcher + */ + jasmine.addMatchers = function(matchers) { + return env.addMatchers(matchers); + }; + + /** + * Add custom async matchers for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addAsyncMatchers + * @since 3.5.0 + * @function + * @param {Object} matchers - Keys from this object will be the new async matcher names. + * @see custom_matcher + */ + jasmine.addAsyncMatchers = function(matchers) { + return env.addAsyncMatchers(matchers); + }; + + /** + * Get the currently booted mock {Clock} for this Jasmine environment. + * @name jasmine.clock + * @since 2.0.0 + * @function + * @returns {Clock} + */ + jasmine.clock = function() { + return env.clock; + }; + + /** + * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. + * @name jasmine.createSpy + * @since 1.3.0 + * @function + * @param {String} [name] - Name to give the spy. This will be displayed in failure messages. + * @param {Function} [originalFn] - Function to act as the real implementation. + * @return {Spy} + */ + jasmine.createSpy = function(name, originalFn) { + return env.createSpy(name, originalFn); + }; + + /** + * Create an object with multiple {@link Spy}s as its members. + * @name jasmine.createSpyObj + * @since 1.3.0 + * @function + * @param {String} [baseName] - Base name for the spies in the object. + * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}. + * @param {String[]|Object} [propertyNames] - Array of property names to create spies for, or Object whose keys will be propertynames and values the {@link Spy#and#returnValue|returnValue}. + * @return {Object} + */ + jasmine.createSpyObj = function(baseName, methodNames, propertyNames) { + return env.createSpyObj(baseName, methodNames, propertyNames); + }; + + /** + * Add a custom spy strategy for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addSpyStrategy + * @since 3.5.0 + * @function + * @param {String} name - The name of the strategy (i.e. what you call from `and`) + * @param {Function} factory - Factory function that returns the plan to be executed. + */ + jasmine.addSpyStrategy = function(name, factory) { + return env.addSpyStrategy(name, factory); + }; + + /** + * Set the default spy strategy for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.setDefaultSpyStrategy + * @function + * @param {Function} defaultStrategyFn - a function that assigns a strategy + * @example + * beforeEach(function() { + * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); + * }); + */ + jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { + return env.setDefaultSpyStrategy(defaultStrategyFn); + }; + + return jasmineInterface; +}; + +getJasmineRequireObj().Spy = function(j$) { + var nextOrder = (function() { + var order = 0; + + return function() { + return order++; + }; + })(); + + /** + * _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj} + * @constructor + * @name Spy + */ + function Spy( + name, + originalFn, + customStrategies, + defaultStrategyFn, + getPromise + ) { + var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, + wrapper = makeFunc(numArgs, function() { + return spy.apply(this, Array.prototype.slice.call(arguments)); + }), + strategyDispatcher = new SpyStrategyDispatcher({ + name: name, + fn: originalFn, + getSpy: function() { + return wrapper; + }, + customStrategies: customStrategies, + getPromise: getPromise + }), + callTracker = new j$.CallTracker(), + spy = function() { + /** + * @name Spy.callData + * @property {object} object - `this` context for the invocation. + * @property {number} invocationOrder - Order of the invocation. + * @property {Array} args - The arguments passed for this invocation. + */ + var callData = { + object: this, + invocationOrder: nextOrder(), + args: Array.prototype.slice.apply(arguments) + }; + + callTracker.track(callData); + var returnValue = strategyDispatcher.exec(this, arguments); + callData.returnValue = returnValue; + + return returnValue; + }; + + function makeFunc(length, fn) { + switch (length) { + case 1: + return function(a) { + return fn.apply(this, arguments); + }; + case 2: + return function(a, b) { + return fn.apply(this, arguments); + }; + case 3: + return function(a, b, c) { + return fn.apply(this, arguments); + }; + case 4: + return function(a, b, c, d) { + return fn.apply(this, arguments); + }; + case 5: + return function(a, b, c, d, e) { + return fn.apply(this, arguments); + }; + case 6: + return function(a, b, c, d, e, f) { + return fn.apply(this, arguments); + }; + case 7: + return function(a, b, c, d, e, f, g) { + return fn.apply(this, arguments); + }; + case 8: + return function(a, b, c, d, e, f, g, h) { + return fn.apply(this, arguments); + }; + case 9: + return function(a, b, c, d, e, f, g, h, i) { + return fn.apply(this, arguments); + }; + default: + return function() { + return fn.apply(this, arguments); + }; + } + } + + for (var prop in originalFn) { + if (prop === 'and' || prop === 'calls') { + throw new Error( + "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon" + ); + } + + wrapper[prop] = originalFn[prop]; + } + + /** + * @member {SpyStrategy} - Accesses the default strategy for the spy. This strategy will be used + * whenever the spy is called with arguments that don't match any strategy + * created with {@link Spy#withArgs}. + * @name Spy#and + * @since 2.0.0 + * @example + * spyOn(someObj, 'func').and.returnValue(42); + */ + wrapper.and = strategyDispatcher.and; + /** + * Specifies a strategy to be used for calls to the spy that have the + * specified arguments. + * @name Spy#withArgs + * @since 3.0.0 + * @function + * @param {...*} args - The arguments to match + * @type {SpyStrategy} + * @example + * spyOn(someObj, 'func').withArgs(1, 2, 3).and.returnValue(42); + * someObj.func(1, 2, 3); // returns 42 + */ + wrapper.withArgs = function() { + return strategyDispatcher.withArgs.apply(strategyDispatcher, arguments); + }; + wrapper.calls = callTracker; + + if (defaultStrategyFn) { + defaultStrategyFn(wrapper.and); + } + + return wrapper; + } + + function SpyStrategyDispatcher(strategyArgs) { + var baseStrategy = new j$.SpyStrategy(strategyArgs); + var argsStrategies = new StrategyDict(function() { + return new j$.SpyStrategy(strategyArgs); + }); + + this.and = baseStrategy; + + this.exec = function(spy, args) { + var strategy = argsStrategies.get(args); + + if (!strategy) { + if (argsStrategies.any() && !baseStrategy.isConfigured()) { + throw new Error( + "Spy '" + + strategyArgs.name + + "' received a call with arguments " + + j$.pp(Array.prototype.slice.call(args)) + + ' but all configured strategies specify other arguments.' + ); + } else { + strategy = baseStrategy; + } + } + + return strategy.exec(spy, args); + }; + + this.withArgs = function() { + return { and: argsStrategies.getOrCreate(arguments) }; + }; + } + + function StrategyDict(strategyFactory) { + this.strategies = []; + this.strategyFactory = strategyFactory; + } + + StrategyDict.prototype.any = function() { + return this.strategies.length > 0; + }; + + StrategyDict.prototype.getOrCreate = function(args) { + var strategy = this.get(args); + + if (!strategy) { + strategy = this.strategyFactory(); + this.strategies.push({ + args: args, + strategy: strategy + }); + } + + return strategy; + }; + + StrategyDict.prototype.get = function(args) { + var i; + + for (i = 0; i < this.strategies.length; i++) { + if (j$.matchersUtil.equals(args, this.strategies[i].args)) { + return this.strategies[i].strategy; + } + } + }; + + return Spy; +}; + +getJasmineRequireObj().SpyFactory = function(j$) { + function SpyFactory(getCustomStrategies, getDefaultStrategyFn, getPromise) { + var self = this; + + this.createSpy = function(name, originalFn) { + return j$.Spy( + name, + originalFn, + getCustomStrategies(), + getDefaultStrategyFn(), + getPromise + ); + }; + + this.createSpyObj = function(baseName, methodNames, propertyNames) { + var baseNameIsCollection = + j$.isObject_(baseName) || j$.isArray_(baseName); + + if (baseNameIsCollection) { + propertyNames = methodNames; + methodNames = baseName; + baseName = 'unknown'; + } + + var obj = {}; + var spy, descriptor; + + var methods = normalizeKeyValues(methodNames); + for (var i = 0; i < methods.length; i++) { + spy = obj[methods[i][0]] = self.createSpy( + baseName + '.' + methods[i][0] + ); + if (methods[i].length > 1) { + spy.and.returnValue(methods[i][1]); + } + } + + var properties = normalizeKeyValues(propertyNames); + for (var i = 0; i < properties.length; i++) { + descriptor = { + get: self.createSpy(baseName + '.' + properties[i][0] + '.get'), + set: self.createSpy(baseName + '.' + properties[i][0] + '.set') + }; + if (properties[i].length > 1) { + descriptor.get.and.returnValue(properties[i][1]); + descriptor.set.and.returnValue(properties[i][1]); + } + Object.defineProperty(obj, properties[i][0], descriptor); + } + + if (methods.length === 0 && properties.length === 0) { + throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; + } + + return obj; + }; + } + + function normalizeKeyValues(object) { + var result = []; + if (j$.isArray_(object)) { + for (var i = 0; i < object.length; i++) { + result.push([object[i]]); + } + } else if (j$.isObject_(object)) { + for (var key in object) { + if (object.hasOwnProperty(key)) { + result.push([key, object[key]]); + } + } + } + return result; + } + + return SpyFactory; +}; + +getJasmineRequireObj().SpyRegistry = function(j$) { + var spyOnMsg = j$.formatErrorMsg('', 'spyOn(, )'); + var spyOnPropertyMsg = j$.formatErrorMsg( + '', + 'spyOnProperty(, , [accessType])' + ); + + function SpyRegistry(options) { + options = options || {}; + var global = options.global || j$.getGlobal(); + var createSpy = options.createSpy; + var currentSpies = + options.currentSpies || + function() { + return []; + }; + + this.allowRespy = function(allow) { + this.respy = allow; + }; + + this.spyOn = function(obj, methodName) { + var getErrorMsg = spyOnMsg; + + if (j$.util.isUndefined(obj) || obj === null) { + throw new Error( + getErrorMsg( + 'could not find an object to spy upon for ' + methodName + '()' + ) + ); + } + + if (j$.util.isUndefined(methodName) || methodName === null) { + throw new Error(getErrorMsg('No method name supplied')); + } + + if (j$.util.isUndefined(obj[methodName])) { + throw new Error(getErrorMsg(methodName + '() method does not exist')); + } + + if (obj[methodName] && j$.isSpy(obj[methodName])) { + if (this.respy) { + return obj[methodName]; + } else { + throw new Error( + getErrorMsg(methodName + ' has already been spied upon') + ); + } + } + + var descriptor = Object.getOwnPropertyDescriptor(obj, methodName); + + if (descriptor && !(descriptor.writable || descriptor.set)) { + throw new Error( + getErrorMsg(methodName + ' is not declared writable or has no setter') + ); + } + + var originalMethod = obj[methodName], + spiedMethod = createSpy(methodName, originalMethod), + restoreStrategy; + + if ( + Object.prototype.hasOwnProperty.call(obj, methodName) || + (obj === global && methodName === 'onerror') + ) { + restoreStrategy = function() { + obj[methodName] = originalMethod; + }; + } else { + restoreStrategy = function() { + if (!delete obj[methodName]) { + obj[methodName] = originalMethod; + } + }; + } + + currentSpies().push({ + restoreObjectToOriginalState: restoreStrategy + }); + + obj[methodName] = spiedMethod; + + return spiedMethod; + }; + + this.spyOnProperty = function(obj, propertyName, accessType) { + var getErrorMsg = spyOnPropertyMsg; + + accessType = accessType || 'get'; + + if (j$.util.isUndefined(obj)) { + throw new Error( + getErrorMsg( + 'spyOn could not find an object to spy upon for ' + + propertyName + + '' + ) + ); + } + + if (j$.util.isUndefined(propertyName)) { + throw new Error(getErrorMsg('No property name supplied')); + } + + var descriptor = j$.util.getPropertyDescriptor(obj, propertyName); + + if (!descriptor) { + throw new Error(getErrorMsg(propertyName + ' property does not exist')); + } + + if (!descriptor.configurable) { + throw new Error( + getErrorMsg(propertyName + ' is not declared configurable') + ); + } + + if (!descriptor[accessType]) { + throw new Error( + getErrorMsg( + 'Property ' + + propertyName + + ' does not have access type ' + + accessType + ) + ); + } + + if (j$.isSpy(descriptor[accessType])) { + if (this.respy) { + return descriptor[accessType]; + } else { + throw new Error( + getErrorMsg( + propertyName + '#' + accessType + ' has already been spied upon' + ) + ); + } + } + + var originalDescriptor = j$.util.clone(descriptor), + spy = createSpy(propertyName, descriptor[accessType]), + restoreStrategy; + + if (Object.prototype.hasOwnProperty.call(obj, propertyName)) { + restoreStrategy = function() { + Object.defineProperty(obj, propertyName, originalDescriptor); + }; + } else { + restoreStrategy = function() { + delete obj[propertyName]; + }; + } + + currentSpies().push({ + restoreObjectToOriginalState: restoreStrategy + }); + + descriptor[accessType] = spy; + + Object.defineProperty(obj, propertyName, descriptor); + + return spy; + }; + + this.spyOnAllFunctions = function(obj) { + if (j$.util.isUndefined(obj)) { + throw new Error( + 'spyOnAllFunctions could not find an object to spy upon' + ); + } + + var pointer = obj, + props = [], + prop, + descriptor; + + while (pointer) { + for (prop in pointer) { + if ( + Object.prototype.hasOwnProperty.call(pointer, prop) && + pointer[prop] instanceof Function + ) { + descriptor = Object.getOwnPropertyDescriptor(pointer, prop); + if ( + (descriptor.writable || descriptor.set) && + descriptor.configurable + ) { + props.push(prop); + } + } + } + pointer = Object.getPrototypeOf(pointer); + } + + for (var i = 0; i < props.length; i++) { + this.spyOn(obj, props[i]); + } + + return obj; + }; + + this.clearSpies = function() { + var spies = currentSpies(); + for (var i = spies.length - 1; i >= 0; i--) { + var spyEntry = spies[i]; + spyEntry.restoreObjectToOriginalState(); + } + }; + } + + return SpyRegistry; +}; + +getJasmineRequireObj().SpyStrategy = function(j$) { + /** + * @interface SpyStrategy + */ + function SpyStrategy(options) { + options = options || {}; + + var self = this; + + /** + * Get the identifying information for the spy. + * @name SpyStrategy#identity + * @since 3.0.0 + * @member + * @type {String} + */ + this.identity = options.name || 'unknown'; + this.originalFn = options.fn || function() {}; + this.getSpy = options.getSpy || function() {}; + this.plan = this._defaultPlan = function() {}; + + var k, + cs = options.customStrategies || {}; + for (k in cs) { + if (j$.util.has(cs, k) && !this[k]) { + this[k] = createCustomPlan(cs[k]); + } + } + + var getPromise = + typeof options.getPromise === 'function' + ? options.getPromise + : function() {}; + + var requirePromise = function(name) { + var Promise = getPromise(); + + if (!Promise) { + throw new Error( + name + + ' requires global Promise, or `Promise` configured with `jasmine.getEnv().configure()`' + ); + } + + return Promise; + }; + + /** + * Tell the spy to return a promise resolving to the specified value when invoked. + * @name SpyStrategy#resolveTo + * @since 3.5.0 + * @function + * @param {*} value The value to return. + */ + this.resolveTo = function(value) { + var Promise = requirePromise('resolveTo'); + self.plan = function() { + return Promise.resolve(value); + }; + return self.getSpy(); + }; + + /** + * Tell the spy to return a promise rejecting with the specified value when invoked. + * @name SpyStrategy#rejectWith + * @since 3.5.0 + * @function + * @param {*} value The value to return. + */ + this.rejectWith = function(value) { + var Promise = requirePromise('rejectWith'); + + self.plan = function() { + return Promise.reject(value); + }; + return self.getSpy(); + }; + } + + function createCustomPlan(factory) { + return function() { + var plan = factory.apply(null, arguments); + + if (!j$.isFunction_(plan)) { + throw new Error('Spy strategy must return a function'); + } + + this.plan = plan; + return this.getSpy(); + }; + } + + /** + * Execute the current spy strategy. + * @name SpyStrategy#exec + * @since 2.0.0 + * @function + */ + SpyStrategy.prototype.exec = function(context, args) { + return this.plan.apply(context, args); + }; + + /** + * Tell the spy to call through to the real implementation when invoked. + * @name SpyStrategy#callThrough + * @since 2.0.0 + * @function + */ + SpyStrategy.prototype.callThrough = function() { + this.plan = this.originalFn; + return this.getSpy(); + }; + + /** + * Tell the spy to return the value when invoked. + * @name SpyStrategy#returnValue + * @since 2.0.0 + * @function + * @param {*} value The value to return. + */ + SpyStrategy.prototype.returnValue = function(value) { + this.plan = function() { + return value; + }; + return this.getSpy(); + }; + + /** + * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked. + * @name SpyStrategy#returnValues + * @since 2.1.0 + * @function + * @param {...*} values - Values to be returned on subsequent calls to the spy. + */ + SpyStrategy.prototype.returnValues = function() { + var values = Array.prototype.slice.call(arguments); + this.plan = function() { + return values.shift(); + }; + return this.getSpy(); + }; + + /** + * Tell the spy to throw an error when invoked. + * @name SpyStrategy#throwError + * @since 2.0.0 + * @function + * @param {Error|String} something Thing to throw + */ + SpyStrategy.prototype.throwError = function(something) { + var error = something instanceof Error ? something : new Error(something); + this.plan = function() { + throw error; + }; + return this.getSpy(); + }; + + /** + * Tell the spy to call a fake implementation when invoked. + * @name SpyStrategy#callFake + * @since 2.0.0 + * @function + * @param {Function} fn The function to invoke with the passed parameters. + */ + SpyStrategy.prototype.callFake = function(fn) { + if (!(j$.isFunction_(fn) || j$.isAsyncFunction_(fn))) { + throw new Error( + 'Argument passed to callFake should be a function, got ' + fn + ); + } + this.plan = fn; + return this.getSpy(); + }; + + /** + * Tell the spy to do nothing when invoked. This is the default. + * @name SpyStrategy#stub + * @since 2.0.0 + * @function + */ + SpyStrategy.prototype.stub = function(fn) { + this.plan = function() {}; + return this.getSpy(); + }; + + SpyStrategy.prototype.isConfigured = function() { + return this.plan !== this._defaultPlan; + }; + + return SpyStrategy; +}; + +getJasmineRequireObj().StackTrace = function(j$) { + function StackTrace(error) { + var lines = error.stack.split('\n').filter(function(line) { + return line !== ''; + }); + + var extractResult = extractMessage(error.message, lines); + + if (extractResult) { + this.message = extractResult.message; + lines = extractResult.remainder; + } + + var parseResult = tryParseFrames(lines); + this.frames = parseResult.frames; + this.style = parseResult.style; + } + + var framePatterns = [ + // PhantomJS on Linux, Node, Chrome, IE, Edge + // e.g. " at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)" + // Note that the "function name" can include a surprisingly large set of + // characters, including angle brackets and square brackets. + { + re: /^\s*at ([^\)]+) \(([^\)]+)\)$/, + fnIx: 1, + fileLineColIx: 2, + style: 'v8' + }, + + // NodeJS alternate form, often mixed in with the Chrome style + // e.g. " at /some/path:4320:20 + { re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' }, + + // PhantomJS on OS X, Safari, Firefox + // e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27" + // or "http://localhost:8888/__jasmine__/jasmine.js:4320:27" + { + re: /^(([^@\s]+)@)?([^\s]+)$/, + fnIx: 2, + fileLineColIx: 3, + style: 'webkit' + } + ]; + + // regexes should capture the function name (if any) as group 1 + // and the file, line, and column as group 2. + function tryParseFrames(lines) { + var style = null; + var frames = lines.map(function(line) { + var convertedLine = first(framePatterns, function(pattern) { + var overallMatch = line.match(pattern.re), + fileLineColMatch; + if (!overallMatch) { + return null; + } + + fileLineColMatch = overallMatch[pattern.fileLineColIx].match( + /^(.*):(\d+):\d+$/ + ); + if (!fileLineColMatch) { + return null; + } + + style = style || pattern.style; + return { + raw: line, + file: fileLineColMatch[1], + line: parseInt(fileLineColMatch[2], 10), + func: overallMatch[pattern.fnIx] + }; + }); + + return convertedLine || { raw: line }; + }); + + return { + style: style, + frames: frames + }; + } + + function first(items, fn) { + var i, result; + + for (i = 0; i < items.length; i++) { + result = fn(items[i]); + + if (result) { + return result; + } + } + } + + function extractMessage(message, stackLines) { + var len = messagePrefixLength(message, stackLines); + + if (len > 0) { + return { + message: stackLines.slice(0, len).join('\n'), + remainder: stackLines.slice(len) + }; + } + } + + function messagePrefixLength(message, stackLines) { + if (!stackLines[0].match(/^Error/)) { + return 0; + } + + var messageLines = message.split('\n'); + var i; + + for (i = 1; i < messageLines.length; i++) { + if (messageLines[i] !== stackLines[i]) { + return 0; + } + } + + return messageLines.length; + } + + return StackTrace; +}; + +getJasmineRequireObj().Suite = function(j$) { + function Suite(attrs) { + this.env = attrs.env; + this.id = attrs.id; + this.parentSuite = attrs.parentSuite; + this.description = attrs.description; + this.expectationFactory = attrs.expectationFactory; + this.asyncExpectationFactory = attrs.asyncExpectationFactory; + this.expectationResultFactory = attrs.expectationResultFactory; + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; + + this.beforeFns = []; + this.afterFns = []; + this.beforeAllFns = []; + this.afterAllFns = []; + + this.timer = attrs.timer || j$.noopTimer; + + this.children = []; + + /** + * @typedef SuiteResult + * @property {Int} id - The unique id of this suite. + * @property {String} description - The description text passed to the {@link describe} that made this suite. + * @property {String} fullName - The full description including all ancestors of this suite. + * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. + * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. + * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. + * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. + */ + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + failedExpectations: [], + deprecationWarnings: [], + duration: null + }; + } + + Suite.prototype.expect = function(actual) { + return this.expectationFactory(actual, this); + }; + + Suite.prototype.expectAsync = function(actual) { + return this.asyncExpectationFactory(actual, this); + }; + + Suite.prototype.getFullName = function() { + var fullName = []; + for ( + var parentSuite = this; + parentSuite; + parentSuite = parentSuite.parentSuite + ) { + if (parentSuite.parentSuite) { + fullName.unshift(parentSuite.description); + } + } + return fullName.join(' '); + }; + + Suite.prototype.pend = function() { + this.markedPending = true; + }; + + Suite.prototype.beforeEach = function(fn) { + this.beforeFns.unshift(fn); + }; + + Suite.prototype.beforeAll = function(fn) { + this.beforeAllFns.push(fn); + }; + + Suite.prototype.afterEach = function(fn) { + this.afterFns.unshift(fn); + }; + + Suite.prototype.afterAll = function(fn) { + this.afterAllFns.unshift(fn); + }; + + Suite.prototype.startTimer = function() { + this.timer.start(); + }; + + Suite.prototype.endTimer = function() { + this.result.duration = this.timer.elapsed(); + }; + + function removeFns(queueableFns) { + for (var i = 0; i < queueableFns.length; i++) { + queueableFns[i].fn = null; + } + } + + Suite.prototype.cleanupBeforeAfter = function() { + removeFns(this.beforeAllFns); + removeFns(this.afterAllFns); + removeFns(this.beforeFns); + removeFns(this.afterFns); + }; + + Suite.prototype.addChild = function(child) { + this.children.push(child); + }; + + Suite.prototype.status = function() { + if (this.markedPending) { + return 'pending'; + } + + if (this.result.failedExpectations.length > 0) { + return 'failed'; + } else { + return 'passed'; + } + }; + + Suite.prototype.canBeReentered = function() { + return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; + }; + + Suite.prototype.getResult = function() { + this.result.status = this.status(); + return this.result; + }; + + Suite.prototype.sharedUserContext = function() { + if (!this.sharedContext) { + this.sharedContext = this.parentSuite + ? this.parentSuite.clonedSharedUserContext() + : new j$.UserContext(); + } + + return this.sharedContext; + }; + + Suite.prototype.clonedSharedUserContext = function() { + return j$.UserContext.fromExisting(this.sharedUserContext()); + }; + + Suite.prototype.onException = function() { + if (arguments[0] instanceof j$.errors.ExpectationFailed) { + return; + } + + var data = { + matcherName: '', + passed: false, + expected: '', + actual: '', + error: arguments[0] + }; + var failedExpectation = this.expectationResultFactory(data); + + if (!this.parentSuite) { + failedExpectation.globalErrorType = 'afterAll'; + } + + this.result.failedExpectations.push(failedExpectation); + }; + + Suite.prototype.addExpectationResult = function() { + if (isFailure(arguments)) { + var data = arguments[1]; + this.result.failedExpectations.push(this.expectationResultFactory(data)); + if (this.throwOnExpectationFailure) { + throw new j$.errors.ExpectationFailed(); + } + } + }; + + Suite.prototype.addDeprecationWarning = function(deprecation) { + if (typeof deprecation === 'string') { + deprecation = { message: deprecation }; + } + this.result.deprecationWarnings.push( + this.expectationResultFactory(deprecation) + ); + }; + + function isFailure(args) { + return !args[0]; + } + + return Suite; +}; + +if (typeof window == void 0 && typeof exports == 'object') { + /* globals exports */ + exports.Suite = jasmineRequire.Suite; +} + +getJasmineRequireObj().Timer = function() { + var defaultNow = (function(Date) { + return function() { + return new Date().getTime(); + }; + })(Date); + + function Timer(options) { + options = options || {}; + + var now = options.now || defaultNow, + startTime; + + this.start = function() { + startTime = now(); + }; + + this.elapsed = function() { + return now() - startTime; + }; + } + + return Timer; +}; + +getJasmineRequireObj().noopTimer = function() { + return { + start: function() {}, + elapsed: function() { + return 0; + } + }; +}; + +getJasmineRequireObj().TreeProcessor = function() { + function TreeProcessor(attrs) { + var tree = attrs.tree, + runnableIds = attrs.runnableIds, + queueRunnerFactory = attrs.queueRunnerFactory, + nodeStart = attrs.nodeStart || function() {}, + nodeComplete = attrs.nodeComplete || function() {}, + failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations, + orderChildren = + attrs.orderChildren || + function(node) { + return node.children; + }, + excludeNode = + attrs.excludeNode || + function(node) { + return false; + }, + stats = { valid: true }, + processed = false, + defaultMin = Infinity, + defaultMax = 1 - Infinity; + + this.processTree = function() { + processNode(tree, true); + processed = true; + return stats; + }; + + this.execute = function(done) { + if (!processed) { + this.processTree(); + } + + if (!stats.valid) { + throw 'invalid order'; + } + + var childFns = wrapChildren(tree, 0); + + queueRunnerFactory({ + queueableFns: childFns, + userContext: tree.sharedUserContext(), + onException: function() { + tree.onException.apply(tree, arguments); + }, + onComplete: done + }); + }; + + function runnableIndex(id) { + for (var i = 0; i < runnableIds.length; i++) { + if (runnableIds[i] === id) { + return i; + } + } + } + + function processNode(node, parentExcluded) { + var executableIndex = runnableIndex(node.id); + + if (executableIndex !== undefined) { + parentExcluded = false; + } + + if (!node.children) { + var excluded = parentExcluded || excludeNode(node); + stats[node.id] = { + excluded: excluded, + willExecute: !excluded && !node.markedPending, + segments: [ + { + index: 0, + owner: node, + nodes: [node], + min: startingMin(executableIndex), + max: startingMax(executableIndex) + } + ] + }; + } else { + var hasExecutableChild = false; + + var orderedChildren = orderChildren(node); + + for (var i = 0; i < orderedChildren.length; i++) { + var child = orderedChildren[i]; + + processNode(child, parentExcluded); + + if (!stats.valid) { + return; + } + + var childStats = stats[child.id]; + + hasExecutableChild = hasExecutableChild || childStats.willExecute; + } + + stats[node.id] = { + excluded: parentExcluded, + willExecute: hasExecutableChild + }; + + segmentChildren(node, orderedChildren, stats[node.id], executableIndex); + + if (!node.canBeReentered() && stats[node.id].segments.length > 1) { + stats = { valid: false }; + } + } + } + + function startingMin(executableIndex) { + return executableIndex === undefined ? defaultMin : executableIndex; + } + + function startingMax(executableIndex) { + return executableIndex === undefined ? defaultMax : executableIndex; + } + + function segmentChildren( + node, + orderedChildren, + nodeStats, + executableIndex + ) { + var currentSegment = { + index: 0, + owner: node, + nodes: [], + min: startingMin(executableIndex), + max: startingMax(executableIndex) + }, + result = [currentSegment], + lastMax = defaultMax, + orderedChildSegments = orderChildSegments(orderedChildren); + + function isSegmentBoundary(minIndex) { + return ( + lastMax !== defaultMax && + minIndex !== defaultMin && + lastMax < minIndex - 1 + ); + } + + for (var i = 0; i < orderedChildSegments.length; i++) { + var childSegment = orderedChildSegments[i], + maxIndex = childSegment.max, + minIndex = childSegment.min; + + if (isSegmentBoundary(minIndex)) { + currentSegment = { + index: result.length, + owner: node, + nodes: [], + min: defaultMin, + max: defaultMax + }; + result.push(currentSegment); + } + + currentSegment.nodes.push(childSegment); + currentSegment.min = Math.min(currentSegment.min, minIndex); + currentSegment.max = Math.max(currentSegment.max, maxIndex); + lastMax = maxIndex; + } + + nodeStats.segments = result; + } + + function orderChildSegments(children) { + var specifiedOrder = [], + unspecifiedOrder = []; + + for (var i = 0; i < children.length; i++) { + var child = children[i], + segments = stats[child.id].segments; + + for (var j = 0; j < segments.length; j++) { + var seg = segments[j]; + + if (seg.min === defaultMin) { + unspecifiedOrder.push(seg); + } else { + specifiedOrder.push(seg); + } + } + } + + specifiedOrder.sort(function(a, b) { + return a.min - b.min; + }); + + return specifiedOrder.concat(unspecifiedOrder); + } + + function executeNode(node, segmentNumber) { + if (node.children) { + return { + fn: function(done) { + var onStart = { + fn: function(next) { + nodeStart(node, next); + } + }; + + queueRunnerFactory({ + onComplete: function() { + var args = Array.prototype.slice.call(arguments, [0]); + node.cleanupBeforeAfter(); + nodeComplete(node, node.getResult(), function() { + done.apply(undefined, args); + }); + }, + queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), + userContext: node.sharedUserContext(), + onException: function() { + node.onException.apply(node, arguments); + } + }); + } + }; + } else { + return { + fn: function(done) { + node.execute( + done, + stats[node.id].excluded, + failSpecWithNoExpectations + ); + } + }; + } + } + + function wrapChildren(node, segmentNumber) { + var result = [], + segmentChildren = stats[node.id].segments[segmentNumber].nodes; + + for (var i = 0; i < segmentChildren.length; i++) { + result.push( + executeNode(segmentChildren[i].owner, segmentChildren[i].index) + ); + } + + if (!stats[node.id].willExecute) { + return result; + } + + return node.beforeAllFns.concat(result).concat(node.afterAllFns); + } + } + + return TreeProcessor; +}; + +getJasmineRequireObj().UserContext = function(j$) { + function UserContext() {} + + UserContext.fromExisting = function(oldContext) { + var context = new UserContext(); + + for (var prop in oldContext) { + if (oldContext.hasOwnProperty(prop)) { + context[prop] = oldContext[prop]; + } + } + + return context; + }; + + return UserContext; +}; + +getJasmineRequireObj().version = function() { + return '3.5.0'; +}; diff --git a/Chutzpah/ChutzpahTestHarnessFiles/Jasmine/v3/jasmine_favicon.png b/Chutzpah/ChutzpahTestHarnessFiles/Jasmine/v3/jasmine_favicon.png new file mode 100644 index 00000000..3b84583b Binary files /dev/null and b/Chutzpah/ChutzpahTestHarnessFiles/Jasmine/v3/jasmine_favicon.png differ diff --git a/Chutzpah/FrameworkDefinitions/JasmineDefinition.cs b/Chutzpah/FrameworkDefinitions/JasmineDefinition.cs index aae6f705..17e64962 100644 --- a/Chutzpah/FrameworkDefinitions/JasmineDefinition.cs +++ b/Chutzpah/FrameworkDefinitions/JasmineDefinition.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using Chutzpah.FileProcessors; +using Chutzpah.Exceptions; namespace Chutzpah.FrameworkDefinitions { @@ -25,9 +26,9 @@ public JasmineDefinition(IEnumerable fileProces fileDependencies["1"] = new[] { "chutzpah_boot.js", - @"jasmine\v1\jasmine.css", - @"jasmine\v1\jasmine.js", - @"jasmine\v1\jasmine-html.js", + @"jasmine\v1\jasmine.css", + @"jasmine\v1\jasmine.js", + @"jasmine\v1\jasmine-html.js", @"jasmine\v1\jasmine_favicon.png", @"jasmine\v1\jasmine-ddescribe-iit.js" }; @@ -35,18 +36,30 @@ public JasmineDefinition(IEnumerable fileProces fileDependencies["2"] = new[] { "chutzpah_boot.js", - @"jasmine\v2\jasmine.css", - @"jasmine\v2\jasmine.js", - @"jasmine\v2\jasmine-html.js", - @"jasmine\v2\boot.js", + @"jasmine\v2\jasmine.css", + @"jasmine\v2\jasmine.js", + @"jasmine\v2\jasmine-html.js", + @"jasmine\v2\boot.js", @"jasmine\v2\jasmine_favicon.png" }; + fileDependencies["3"] = new[] + { + "chutzpah_boot.js", + @"jasmine\v3\jasmine.css", + @"jasmine\v3\jasmine.js", + @"jasmine\v3\jasmine-html.js", + @"jasmine\v3\boot.js", + @"jasmine\v3\jasmine_favicon.png" + }; + testHarness["1"] = @"jasmine\v1\jasmine.html"; testHarness["2"] = @"jasmine\v2\jasmine.html"; + testHarness["3"] = @"jasmine\v3\jasmine.html"; testRunner["1"] = @"jasmineRunnerV1.js"; testRunner["2"] = @"jasmineRunnerV2.js"; + testRunner["3"] = @"jasmineRunnerV3.js"; } /// @@ -65,7 +78,14 @@ public override string GetTestHarness(ChutzpahTestSettingsFile chutzpahTestSetti public override string GetTestRunner(ChutzpahTestSettingsFile chutzpahTestSettings, TestOptions options) { - var runnerName = testRunner[GetVersion(chutzpahTestSettings)]; + var engine = options.Engine ?? chutzpahTestSettings.Engine; + var version = GetVersion(chutzpahTestSettings); + if (version == "3" && engine == Engine.Phantom) + { + throw new ChutzpahException("Jasmine 3 or greater requires using either the JSDom or Chrome engines."); + } + var runnerName = testRunner[version]; + return BuildTestRunnerPath(chutzpahTestSettings, options, runnerName); } @@ -121,12 +141,19 @@ protected override IEnumerable FileProcessors private string GetVersion(ChutzpahTestSettingsFile testSettingsFile) { - if (!string.IsNullOrEmpty(testSettingsFile.FrameworkVersion) + if (!string.IsNullOrEmpty(testSettingsFile.FrameworkVersion) && (testSettingsFile.FrameworkVersion == "1" || testSettingsFile.FrameworkVersion.StartsWith("1."))) { return "1"; } + if (!string.IsNullOrEmpty(testSettingsFile.FrameworkVersion) + && (testSettingsFile.FrameworkVersion == "3" || testSettingsFile.FrameworkVersion.StartsWith("3."))) + { + + return "3"; + } + return "2"; } } diff --git a/Facts.Integration/JS/Test/TestSettings/FrameworkVersion/Jasmine_V3/chutzpah.json b/Facts.Integration/JS/Test/TestSettings/FrameworkVersion/Jasmine_V3/chutzpah.json new file mode 100644 index 00000000..c066bbbe --- /dev/null +++ b/Facts.Integration/JS/Test/TestSettings/FrameworkVersion/Jasmine_V3/chutzpah.json @@ -0,0 +1,3 @@ +{ + "FrameworkVersion": "3" +} \ No newline at end of file diff --git a/Facts.Integration/JS/Test/TestSettings/FrameworkVersion/Jasmine_V3/versionTest.js b/Facts.Integration/JS/Test/TestSettings/FrameworkVersion/Jasmine_V3/versionTest.js new file mode 100644 index 00000000..8e2f9ee4 --- /dev/null +++ b/Facts.Integration/JS/Test/TestSettings/FrameworkVersion/Jasmine_V3/versionTest.js @@ -0,0 +1,6 @@ + +describe("jasmine version two", function () { + it("Assert version 2", function () { + expect(jasmine.version[0]).toEqual("2"); + }); +}); \ No newline at end of file diff --git a/chutzpah.nuspec b/chutzpah.nuspec index 9831036c..66c75208 100644 --- a/chutzpah.nuspec +++ b/chutzpah.nuspec @@ -14,11 +14,7 @@ false testing javascript tdd qunit jasmine mocha coffeescript typescript - * Update QUnit to 2.6.2 - * Update Jasmine to 2.9 - * Support latest VS and remove support for VS 2012, 2013 - * Add beta support for running tests with JSDom - * Add beta support for running tests with Chrome + * Add Jasmine 3.5 (requires using Chrome or JsDom engine) \ No newline at end of file