diff options
Diffstat (limited to 'vendor/blueimp/jquery-file-upload/test')
-rw-r--r-- | vendor/blueimp/jquery-file-upload/test/unit.js | 297 | ||||
-rw-r--r-- | vendor/blueimp/jquery-file-upload/test/vendor/mocha.css | 1 | ||||
-rw-r--r-- | vendor/blueimp/jquery-file-upload/test/vendor/mocha.js | 862 |
3 files changed, 531 insertions, 629 deletions
diff --git a/vendor/blueimp/jquery-file-upload/test/unit.js b/vendor/blueimp/jquery-file-upload/test/unit.js index d9d9fffb9..c70d35dd6 100644 --- a/vendor/blueimp/jquery-file-upload/test/unit.js +++ b/vendor/blueimp/jquery-file-upload/test/unit.js @@ -12,7 +12,7 @@ /* global beforeEach, afterEach, describe, it */ /* eslint-disable new-cap */ -(function (expect, $) { +(function(expect, $) { 'use strict'; var canCreateBlob = !!window.dataURLtoBlob; @@ -73,29 +73,29 @@ */ function deleteFiles(files, callback) { $.when( - files.map(function (file) { + files.map(function(file) { return $.ajax({ type: file.deleteType, url: file.deleteUrl }); }) - ).always(function () { + ).always(function() { callback(); }); } - beforeEach(function () { + beforeEach(function() { fileGIF = new File([blobGIF], 'example.gif', { type: 'image/gif' }); fileJPEG = new File([blobJPEG], 'example.jpg', { type: 'image/jpeg' }); files = [fileGIF, fileJPEG]; items = [ { - getAsFile: function () { + getAsFile: function() { return files[0]; } }, { - getAsFile: function () { + getAsFile: function() { return files[1]; } } @@ -108,71 +108,71 @@ }; }); - afterEach(function (done) { - $.getJSON(uploadURL).then(function (result) { + afterEach(function(done) { + $.getJSON(uploadURL).then(function(result) { deleteFiles(result.files, done); }); }); - describe('Initialization', function () { + describe('Initialization', function() { var form; - beforeEach(function () { + beforeEach(function() { form = createFileuploadForm(); }); - afterEach(function () { + afterEach(function() { form.remove(); }); - it('widget', function () { + it('widget', function() { form.fileupload(); expect(form.data('blueimp-fileupload')).to.be.an('object'); }); - it('file input', function () { + it('file input', function() { form.fileupload(); expect(form.fileupload('option', 'fileInput').length).to.equal(1); }); - it('drop zone', function () { + it('drop zone', function() { form.fileupload(); expect(form.fileupload('option', 'dropZone').length).to.equal(1); }); - it('paste zone', function () { + it('paste zone', function() { form.fileupload({ pasteZone: document }); expect(form.fileupload('option', 'pasteZone').length).to.equal(1); }); - it('data attributes', function () { + it('data attributes', function() { form.attr('data-url', 'https://example.org'); form.fileupload(); expect(form.fileupload('option', 'url')).to.equal('https://example.org'); expect(form.data('blueimp-fileupload')).to.be.an('object'); }); - it('event listeners', function () { + it('event listeners', function() { var eventsData = {}; form.fileupload({ autoUpload: false, pasteZone: document, - dragover: function () { + dragover: function() { eventsData.dragover = true; }, - dragenter: function () { + dragenter: function() { eventsData.dragenter = true; }, - dragleave: function () { + dragleave: function() { eventsData.dragleave = true; }, - drop: function (e, data) { + drop: function(e, data) { eventsData.drop = data; }, - paste: function (e, data) { + paste: function(e, data) { eventsData.paste = data; }, - change: function () { + change: function() { eventsData.change = true; } }); @@ -197,40 +197,40 @@ }); }); - describe('API', function () { + describe('API', function() { var form; - beforeEach(function () { + beforeEach(function() { form = createFileuploadForm().fileupload({ dataType: 'json', autoUpload: false }); }); - afterEach(function () { + afterEach(function() { form.remove(); }); - it('destroy', function () { + it('destroy', function() { var eventsData = {}; form.fileupload('option', { pasteZone: document, - dragover: function () { + dragover: function() { eventsData.dragover = true; }, - dragenter: function () { + dragenter: function() { eventsData.dragenter = true; }, - dragleave: function () { + dragleave: function() { eventsData.dragleave = true; }, - drop: function (e, data) { + drop: function(e, data) { eventsData.drop = data; }, - paste: function (e, data) { + paste: function(e, data) { eventsData.paste = data; }, - change: function () { + change: function() { eventsData.change = true; } }); @@ -254,26 +254,26 @@ expect(eventsData.paste).to.equal(); }); - it('disable', function () { + it('disable', function() { var eventsData = {}; form.fileupload('option', { pasteZone: document, - dragover: function () { + dragover: function() { eventsData.dragover = true; }, - dragenter: function () { + dragenter: function() { eventsData.dragenter = true; }, - dragleave: function () { + dragleave: function() { eventsData.dragleave = true; }, - drop: function (e, data) { + drop: function(e, data) { eventsData.drop = data; }, - paste: function (e, data) { + paste: function(e, data) { eventsData.paste = data; }, - change: function () { + change: function() { eventsData.change = true; } }); @@ -298,26 +298,26 @@ expect(eventsData.paste).to.equal(); }); - it('enable', function () { + it('enable', function() { var eventsData = {}; form.fileupload('option', { pasteZone: document, - dragover: function () { + dragover: function() { eventsData.dragover = true; }, - dragenter: function () { + dragenter: function() { eventsData.dragenter = true; }, - dragleave: function () { + dragleave: function() { eventsData.dragleave = true; }, - drop: function (e, data) { + drop: function(e, data) { eventsData.drop = data; }, - paste: function (e, data) { + paste: function(e, data) { eventsData.paste = data; }, - change: function () { + change: function() { eventsData.change = true; } }); @@ -343,9 +343,9 @@ expect(eventsData.paste.files).to.deep.equal(files); }); - it('option', function () { + it('option', function() { var eventsData = {}; - form.fileupload('option', 'drop', function (e, data) { + form.fileupload('option', 'drop', function(e, data) { eventsData.drop = data; }); var dropZone = form @@ -363,9 +363,9 @@ expect(eventsData.drop.files).to.deep.equal(files); }); - it('add', function () { + it('add', function() { var eventData = []; - form.fileupload('option', 'add', function (e, data) { + form.fileupload('option', 'add', function(e, data) { eventData.push(data); }); form.fileupload('add', { files: files }); @@ -374,9 +374,9 @@ expect(eventData[1].files[0]).to.equal(files[1]); }); - it('send', function (done) { + it('send', function(done) { this.slow(200); - form.fileupload('send', { files: files }).complete(function (result) { + form.fileupload('send', { files: files }).complete(function(result) { var uploadedFiles = result.responseJSON.files; expect(uploadedFiles.length).to.equal(2); expect(uploadedFiles[0].type).to.equal(files[0].type); @@ -388,20 +388,20 @@ }); }); - describe('Callbacks', function () { + describe('Callbacks', function() { var form; - beforeEach(function () { + beforeEach(function() { form = createFileuploadForm().fileupload({ dataType: 'json' }); }); - afterEach(function () { + afterEach(function() { form.remove(); }); - it('add', function () { + it('add', function() { var eventData = []; - form.fileupload('option', 'add', function (e, data) { + form.fileupload('option', 'add', function(e, data) { eventData.push(data); }); form.fileupload('add', { files: files }); @@ -410,14 +410,14 @@ expect(eventData[1].files[0]).to.equal(files[1]); }); - it('submit', function (done) { + it('submit', function(done) { this.slow(200); var eventData = []; form.fileupload('option', { - submit: function (e, data) { + submit: function(e, data) { eventData.push(data); }, - stop: function () { + stop: function() { if (eventData.length < 2) return; expect(eventData[0].files[0]).to.equal(files[0]); expect(eventData[1].files[0]).to.equal(files[1]); @@ -427,14 +427,14 @@ form.fileupload('add', { files: files }); }); - it('send', function (done) { + it('send', function(done) { this.slow(200); var eventData = []; form.fileupload('option', { - send: function (e, data) { + send: function(e, data) { eventData.push(data); }, - stop: function () { + stop: function() { expect(eventData.length).to.equal(1); expect(eventData[0].files).to.deep.equal(files); done(); @@ -443,14 +443,14 @@ form.fileupload('send', { files: files }); }); - it('done', function (done) { + it('done', function(done) { this.slow(200); var eventData = []; form.fileupload('option', { - done: function (e, data) { + done: function(e, data) { eventData.push(data); }, - stop: function () { + stop: function() { if (eventData.length < 2) return; expect(eventData[0].result.files.length).to.equal(1); expect(eventData[1].result.files.length).to.equal(1); @@ -460,15 +460,15 @@ form.fileupload('add', { files: files }); }); - it('fail', function (done) { + it('fail', function(done) { this.slow(200); var eventData = []; form.fileupload('option', { url: uploadURL + '404', - fail: function (e, data) { + fail: function(e, data) { eventData.push(data); }, - stop: function () { + stop: function() { if (eventData.length < 2) return; expect(eventData[0].result).to.equal(); expect(eventData[1].result).to.equal(); @@ -478,14 +478,14 @@ form.fileupload('add', { files: files }); }); - it('always', function (done) { + it('always', function(done) { this.slow(200); var eventData = []; form.fileupload('option', { - always: function (e, data) { + always: function(e, data) { eventData.push(data); }, - stop: function () { + stop: function() { if (eventData.length < 2) { expect(eventData[0].result).to.equal(); form.fileupload('add', { files: [fileGIF] }); @@ -498,17 +498,17 @@ form.fileupload('add', { files: [fileGIF], url: uploadURL + '404' }); }); - it('progress', function (done) { + it('progress', function(done) { this.slow(200); var loaded; var total; form.fileupload('option', { - progress: function (e, data) { + progress: function(e, data) { loaded = data.loaded; total = data.total; expect(loaded).to.be.at.most(total); }, - stop: function () { + stop: function() { expect(loaded).to.equal(total); done(); } @@ -516,21 +516,21 @@ form.fileupload('add', { files: [fileGIF] }); }); - it('progressall', function (done) { + it('progressall', function(done) { this.slow(200); var loaded; var total; var completed = 0; form.fileupload('option', { - progressall: function (e, data) { + progressall: function(e, data) { loaded = data.loaded; total = data.total; expect(loaded).to.be.at.most(total); }, - always: function () { + always: function() { completed++; }, - stop: function () { + stop: function() { if (completed < 2) return; expect(loaded).to.equal(total); done(); @@ -539,14 +539,14 @@ form.fileupload('add', { files: files }); }); - it('start', function (done) { + it('start', function(done) { this.slow(200); var started; form.fileupload('option', { - start: function () { + start: function() { started = true; }, - stop: function () { + stop: function() { expect(started).to.equal(true); done(); } @@ -554,21 +554,21 @@ form.fileupload('add', { files: [fileGIF] }); }); - it('stop', function (done) { + it('stop', function(done) { this.slow(200); form.fileupload('option', { - stop: function () { + stop: function() { done(); } }); form.fileupload('add', { files: [fileGIF] }); }); - it('dragover', function () { + it('dragover', function() { var eventsData = {}; form.fileupload('option', { autoUpload: false, - dragover: function () { + dragover: function() { eventsData.dragover = true; } }); @@ -578,11 +578,11 @@ expect(eventsData.dragover).to.equal(true); }); - it('dragenter', function () { + it('dragenter', function() { var eventsData = {}; form.fileupload('option', { autoUpload: false, - dragenter: function () { + dragenter: function() { eventsData.dragenter = true; } }); @@ -592,11 +592,11 @@ expect(eventsData.dragenter).to.equal(true); }); - it('dragleave', function () { + it('dragleave', function() { var eventsData = {}; form.fileupload('option', { autoUpload: false, - dragleave: function () { + dragleave: function() { eventsData.dragleave = true; } }); @@ -606,11 +606,11 @@ expect(eventsData.dragleave).to.equal(true); }); - it('drop', function () { + it('drop', function() { var eventsData = {}; form.fileupload('option', { autoUpload: false, - drop: function (e, data) { + drop: function(e, data) { eventsData.drop = data; } }); @@ -620,12 +620,12 @@ expect(eventsData.drop.files).to.deep.equal(files); }); - it('paste', function () { + it('paste', function() { var eventsData = {}; form.fileupload('option', { autoUpload: false, pasteZone: document, - paste: function (e, data) { + paste: function(e, data) { eventsData.paste = data; } }); @@ -635,11 +635,11 @@ expect(eventsData.paste.files).to.deep.equal(files); }); - it('change', function () { + it('change', function() { var eventsData = {}; form.fileupload('option', { autoUpload: false, - change: function () { + change: function() { eventsData.change = true; } }); @@ -650,20 +650,20 @@ }); }); - describe('Options', function () { + describe('Options', function() { var form; - beforeEach(function () { + beforeEach(function() { form = createFileuploadForm(); }); - afterEach(function () { + afterEach(function() { form.remove(); }); - it('paramName', function (done) { + it('paramName', function(done) { form.fileupload({ - send: function (e, data) { + send: function(e, data) { expect(data.paramName[0]).to.equal( form.fileupload('option', 'fileInput').prop('name') ); @@ -674,9 +674,9 @@ form.fileupload('add', { files: [fileGIF] }); }); - it('url', function (done) { + it('url', function(done) { form.fileupload({ - send: function (e, data) { + send: function(e, data) { expect(data.url).to.equal(form.prop('action')); done(); return false; @@ -685,10 +685,10 @@ form.fileupload('add', { files: [fileGIF] }); }); - it('type', function (done) { + it('type', function(done) { form.fileupload({ type: 'PUT', - send: function (e, data) { + send: function(e, data) { expect(data.type).to.equal('PUT'); done(); return false; @@ -697,7 +697,7 @@ form.fileupload('add', { files: [fileGIF] }); }); - it('replaceFileInput', function () { + it('replaceFileInput', function() { form.fileupload(); var fileInput = form.fileupload('option', 'fileInput'); fileInput.trigger($.Event('change', eventObject)); @@ -710,10 +710,10 @@ expect(form.fileupload('option', 'fileInput')[0]).to.equal(fileInput[0]); }); - it('forceIframeTransport', function (done) { + it('forceIframeTransport', function(done) { form.fileupload({ forceIframeTransport: 'PUT', - send: function (e, data) { + send: function(e, data) { expect(data.dataType.substr(0, 6)).to.equal('iframe'); done(); return false; @@ -722,10 +722,10 @@ form.fileupload('add', { files: [fileGIF] }); }); - it('singleFileUploads', function (done) { + it('singleFileUploads', function(done) { form.fileupload({ singleFileUploads: false, - send: function (e, data) { + send: function(e, data) { expect(data.files).to.deep.equal(files); done(); return false; @@ -734,12 +734,12 @@ form.fileupload('add', { files: files }); }); - it('limitMultiFileUploads', function (done) { + it('limitMultiFileUploads', function(done) { var completed = 0; form.fileupload({ singleFileUploads: false, limitMultiFileUploads: 2, - send: function (e, data) { + send: function(e, data) { expect(data.files).to.deep.equal(files); completed++; if (completed < 2) return; @@ -750,13 +750,13 @@ form.fileupload('add', { files: files.concat(files) }); }); - it('limitMultiFileUploadSize', function (done) { + it('limitMultiFileUploadSize', function(done) { var completed = 0; form.fileupload({ singleFileUploads: false, limitMultiFileUploadSize: files[0].size + files[1].size, limitMultiFileUploadSizeOverhead: 0, - send: function (e, data) { + send: function(e, data) { expect(data.files).to.deep.equal(files); completed++; if (completed < 2) return; @@ -767,21 +767,21 @@ form.fileupload('add', { files: files.concat(files) }); }); - it('sequentialUploads', function (done) { + it('sequentialUploads', function(done) { this.slow(400); var completed = 0; var events = []; form.fileupload({ sequentialUploads: true, dataType: 'json', - send: function () { + send: function() { events.push('send'); }, - always: function () { + always: function() { events.push('complete'); completed++; }, - stop: function () { + stop: function() { if (completed === 4) { expect(events.join(',')).to.equal( [ @@ -802,36 +802,39 @@ form.fileupload('add', { files: files.concat(files) }); }); - it('limitConcurrentUploads', function (done) { + it('limitConcurrentUploads', function(done) { this.slow(800); var completed = 0; var loadCount = 0; form.fileupload({ limitConcurrentUploads: 2, dataType: 'json', - send: function () { + send: function() { loadCount++; expect(loadCount).to.be.at.most(2); }, - always: function () { + always: function() { completed++; loadCount--; }, - stop: function () { + stop: function() { if (completed === 8) { done(); } } }); form.fileupload('add', { - files: files.concat(files).concat(files).concat(files) + files: files + .concat(files) + .concat(files) + .concat(files) }); }); - it('multipart', function (done) { + it('multipart', function(done) { form.fileupload({ multipart: false, - send: function (e, data) { + send: function(e, data) { expect(data.contentType).to.equal(fileGIF.type); expect(data.headers['Content-Disposition']).to.equal( 'attachment; filename="' + fileGIF.name + '"' @@ -843,10 +846,10 @@ form.fileupload('add', { files: [fileGIF] }); }); - it('uniqueFilenames', function (done) { + it('uniqueFilenames', function(done) { form.fileupload({ uniqueFilenames: {}, - send: function (e, data) { + send: function(e, data) { var formFiles = data.data.getAll('files[]'); expect(formFiles[0].name).to.equal(fileGIF.name); expect(formFiles[1].name).to.equal( @@ -862,27 +865,27 @@ form.fileupload('send', { files: [fileGIF, fileGIF, fileGIF] }); }); - it('maxChunkSize', function (done) { + it('maxChunkSize', function(done) { this.slow(400); var events = []; form.fileupload({ maxChunkSize: 32, dataType: 'json', - chunkbeforesend: function () { + chunkbeforesend: function() { events.push('chunkbeforesend'); }, - chunksend: function () { + chunksend: function() { events.push('chunksend'); }, - chunkdone: function () { + chunkdone: function() { events.push('chunkdone'); }, - done: function (e, data) { + done: function(e, data) { var uploadedFile = data.result.files[0]; expect(uploadedFile.type).to.equal(fileGIF.type); expect(uploadedFile.size).to.equal(fileGIF.size); }, - stop: function () { + stop: function() { expect(events.join(',')).to.equal( [ 'chunkbeforesend', @@ -905,15 +908,15 @@ form.fileupload('send', { files: [fileGIF] }); }); - it('acceptFileTypes', function (done) { + it('acceptFileTypes', function(done) { var processData; form.fileupload({ acceptFileTypes: /^image\/gif$/, singleFileUploads: false, - processalways: function (e, data) { + processalways: function(e, data) { processData = data; }, - processstop: function () { + processstop: function() { expect(processData.files[0].error).to.equal(); expect(processData.files[1].error).to.equal( form.fileupload('option').i18n('acceptFileTypes') @@ -924,15 +927,15 @@ form.fileupload('add', { files: files }); }); - it('maxFileSize', function (done) { + it('maxFileSize', function(done) { var processData; form.fileupload({ maxFileSize: 200, singleFileUploads: false, - processalways: function (e, data) { + processalways: function(e, data) { processData = data; }, - processstop: function () { + processstop: function() { expect(processData.files[0].error).to.equal(); expect(processData.files[1].error).to.equal( form.fileupload('option').i18n('maxFileSize') @@ -943,15 +946,15 @@ form.fileupload('add', { files: files }); }); - it('minFileSize', function (done) { + it('minFileSize', function(done) { var processData; form.fileupload({ minFileSize: 200, singleFileUploads: false, - processalways: function (e, data) { + processalways: function(e, data) { processData = data; }, - processstop: function () { + processstop: function() { expect(processData.files[0].error).to.equal( form.fileupload('option').i18n('minFileSize') ); @@ -962,18 +965,18 @@ form.fileupload('add', { files: files }); }); - it('maxNumberOfFiles', function (done) { + it('maxNumberOfFiles', function(done) { var processData; form.fileupload({ maxNumberOfFiles: 2, - getNumberOfFiles: function () { + getNumberOfFiles: function() { return 2; }, singleFileUploads: false, - processalways: function (e, data) { + processalways: function(e, data) { processData = data; }, - processstop: function () { + processstop: function() { expect(processData.files[0].error).to.equal( form.fileupload('option').i18n('maxNumberOfFiles') ); diff --git a/vendor/blueimp/jquery-file-upload/test/vendor/mocha.css b/vendor/blueimp/jquery-file-upload/test/vendor/mocha.css index 4ca8fcb89..ec96b003c 100644 --- a/vendor/blueimp/jquery-file-upload/test/vendor/mocha.css +++ b/vendor/blueimp/jquery-file-upload/test/vendor/mocha.css @@ -139,6 +139,7 @@ body { #mocha .test .html-error { overflow: auto; color: black; + line-height: 1.5; display: block; float: left; clear: left; diff --git a/vendor/blueimp/jquery-file-upload/test/vendor/mocha.js b/vendor/blueimp/jquery-file-upload/test/vendor/mocha.js index 5be2b9eef..508a306db 100644 --- a/vendor/blueimp/jquery-file-upload/test/vendor/mocha.js +++ b/vendor/blueimp/jquery-file-upload/test/vendor/mocha.js @@ -62,7 +62,7 @@ process.on = function(e, fn) { if (e === 'uncaughtException') { global.onerror = function(err, url, line) { fn(new Error(err + ' (' + url + ':' + line + ')')); - return !mocha.options.allowUncaught; + return !mocha.allowUncaught; }; uncaughtExceptionHandlers.push(fn); } @@ -131,7 +131,7 @@ mocha.setup = function(opts) { opts = {ui: opts}; } for (var opt in opts) { - if (Object.prototype.hasOwnProperty.call(opts, opt)) { + if (opts.hasOwnProperty(opt)) { this[opt](opts[opt]); } } @@ -1408,7 +1408,6 @@ var utils = require('./utils'); var mocharc = require('./mocharc.json'); var errors = require('./errors'); var Suite = require('./suite'); -var esmUtils = utils.supportsEsModules() ? require('./esm-utils') : undefined; var createStatsCollector = require('./stats-collector'); var createInvalidReporterError = errors.createInvalidReporterError; var createInvalidInterfaceError = errors.createInvalidInterfaceError; @@ -1464,26 +1463,28 @@ exports.Test = require('./test'); * @param {boolean} [options.allowUncaught] - Propagate uncaught errors? * @param {boolean} [options.asyncOnly] - Force `done` callback or promise? * @param {boolean} [options.bail] - Bail after first test failure? - * @param {boolean} [options.checkLeaks] - Check for global variable leaks? - * @param {boolean} [options.color] - Color TTY output from reporter? + * @param {boolean} [options.checkLeaks] - If true, check leaks. * @param {boolean} [options.delay] - Delay root suite execution? - * @param {boolean} [options.diff] - Show diff on failure? + * @param {boolean} [options.enableTimeouts] - Enable timeouts? * @param {string} [options.fgrep] - Test filter given string. * @param {boolean} [options.forbidOnly] - Tests marked `only` fail the suite? * @param {boolean} [options.forbidPending] - Pending tests fail the suite? - * @param {boolean} [options.fullTrace] - Full stacktrace upon failure? + * @param {boolean} [options.fullStackTrace] - Full stacktrace upon failure? * @param {string[]} [options.global] - Variables expected in global scope. * @param {RegExp|string} [options.grep] - Test filter given regular expression. * @param {boolean} [options.growl] - Enable desktop notifications? - * @param {boolean} [options.inlineDiffs] - Display inline diffs? + * @param {boolean} [options.hideDiff] - Suppress diffs from failures? + * @param {boolean} [options.ignoreLeaks] - Ignore global leaks? * @param {boolean} [options.invert] - Invert test filter matches? * @param {boolean} [options.noHighlighting] - Disable syntax highlighting? - * @param {string|constructor} [options.reporter] - Reporter name or constructor. + * @param {string} [options.reporter] - Reporter name. * @param {Object} [options.reporterOption] - Reporter settings object. * @param {number} [options.retries] - Number of times to retry failed tests. * @param {number} [options.slow] - Slow threshold value. * @param {number|string} [options.timeout] - Timeout threshold value. * @param {string} [options.ui] - Interface name. + * @param {boolean} [options.color] - Color TTY output from reporter? + * @param {boolean} [options.useInlineDiffs] - Use inline diffs? */ function Mocha(options) { options = utils.assign({}, mocharc, options || {}); @@ -1492,15 +1493,31 @@ function Mocha(options) { // root suite this.suite = new exports.Suite('', new exports.Context(), true); + if ('useColors' in options) { + utils.deprecate( + 'useColors is DEPRECATED and will be removed from a future version of Mocha. Instead, use the "color" option' + ); + options.color = 'color' in options ? options.color : options.useColors; + } + this.grep(options.grep) .fgrep(options.fgrep) .ui(options.ui) - .reporter( - options.reporter, - options.reporterOption || options.reporterOptions // reporterOptions was previously the only way to specify options to reporter - ) + .bail(options.bail) + .reporter(options.reporter, options.reporterOptions) + .useColors(options.color) .slow(options.slow) - .global(options.global); + .useInlineDiffs(options.inlineDiffs) + .globals(options.globals); + + if ('enableTimeouts' in options) { + utils.deprecate( + 'enableTimeouts is DEPRECATED and will be removed from a future version of Mocha. Instead, use "timeout: false" to disable timeouts.' + ); + if (options.enableTimeouts === false) { + this.timeout(0); + } + } // this guard exists because Suite#timeout does not consider `undefined` to be valid input if (typeof options.timeout !== 'undefined') { @@ -1511,19 +1528,19 @@ function Mocha(options) { this.retries(options.retries); } + if ('diff' in options) { + this.hideDiff(!options.diff); + } + [ 'allowUncaught', 'asyncOnly', - 'bail', 'checkLeaks', - 'color', 'delay', - 'diff', 'forbidOnly', 'forbidPending', 'fullTrace', 'growl', - 'inlineDiffs', 'invert' ].forEach(function(opt) { if (options[opt]) { @@ -1536,13 +1553,16 @@ function Mocha(options) { * Enables or disables bailing on the first failure. * * @public - * @see [CLI option](../#-bail-b) + * @see {@link https://mochajs.org/#-b---bail|CLI option} * @param {boolean} [bail=true] - Whether to bail on first error. * @returns {Mocha} this * @chainable */ Mocha.prototype.bail = function(bail) { - this.suite.bail(bail !== false); + if (!arguments.length) { + bail = true; + } + this.suite.bail(bail); return this; }; @@ -1554,7 +1574,7 @@ Mocha.prototype.bail = function(bail) { * Useful for generic setup code that must be included within test suite. * * @public - * @see [CLI option](../#-file-filedirectoryglob) + * @see {@link https://mochajs.org/#--file-file|CLI option} * @param {string} file - Pathname of file to be loaded. * @returns {Mocha} this * @chainable @@ -1568,8 +1588,8 @@ Mocha.prototype.addFile = function(file) { * Sets reporter to `reporter`, defaults to "spec". * * @public - * @see [CLI option](../#-reporter-name-r-name) - * @see [Reporters](../#reporters) + * @see {@link https://mochajs.org/#-r---reporter-name|CLI option} + * @see {@link https://mochajs.org/#reporters|Reporters} * @param {String|Function} reporter - Reporter name or constructor. * @param {Object} [reporterOptions] - Options used to configure the reporter. * @returns {Mocha} this @@ -1627,8 +1647,6 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { } this._reporter = _reporter; } - this.options.reporterOption = reporterOptions; - // alias option name is used in public reporters xunit/tap/progress this.options.reporterOptions = reporterOptions; return this; }; @@ -1637,8 +1655,8 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { * Sets test UI `name`, defaults to "bdd". * * @public - * @see [CLI option](../#-ui-name-u-name) - * @see [Interface DSLs](../#interfaces) + * @see {@link https://mochajs.org/#-u---ui-name|CLI option} + * @see {@link https://mochajs.org/#interfaces|Interface DSLs} * @param {string|Function} [ui=bdd] - Interface name or class. * @returns {Mocha} this * @chainable @@ -1685,18 +1703,16 @@ Mocha.prototype.ui = function(ui) { }; /** - * Loads `files` prior to execution. Does not support ES Modules. + * Loads `files` prior to execution. * * @description * The implementation relies on Node's `require` to execute * the test interface functions and will be subject to its cache. - * Supports only CommonJS modules. To load ES modules, use Mocha#loadFilesAsync. * * @private * @see {@link Mocha#addFile} * @see {@link Mocha#run} * @see {@link Mocha#unloadFiles} - * @see {@link Mocha#loadFilesAsync} * @param {Function} [fn] - Callback invoked upon completion. */ Mocha.prototype.loadFiles = function(fn) { @@ -1712,49 +1728,6 @@ Mocha.prototype.loadFiles = function(fn) { }; /** - * Loads `files` prior to execution. Supports Node ES Modules. - * - * @description - * The implementation relies on Node's `require` and `import` to execute - * the test interface functions and will be subject to its cache. - * Supports both CJS and ESM modules. - * - * @public - * @see {@link Mocha#addFile} - * @see {@link Mocha#run} - * @see {@link Mocha#unloadFiles} - * @returns {Promise} - * @example - * - * // loads ESM (and CJS) test files asynchronously, then runs root suite - * mocha.loadFilesAsync() - * .then(() => mocha.run(failures => process.exitCode = failures ? 1 : 0)) - * .catch(() => process.exitCode = 1); - */ -Mocha.prototype.loadFilesAsync = function() { - var self = this; - var suite = this.suite; - this.loadAsync = true; - - if (!esmUtils) { - return new Promise(function(resolve) { - self.loadFiles(resolve); - }); - } - - return esmUtils.loadFilesAsync( - this.files, - function(file) { - suite.emit(EVENT_FILE_PRE_REQUIRE, global, file, self); - }, - function(file, resultModule) { - suite.emit(EVENT_FILE_REQUIRE, resultModule, file, self); - suite.emit(EVENT_FILE_POST_REQUIRE, global, file, self); - } - ); -}; - -/** * Removes a previously loaded file from Node's `require` cache. * * @private @@ -1770,13 +1743,14 @@ Mocha.unloadFile = function(file) { * Unloads `files` from Node's `require` cache. * * @description - * This allows required files to be "freshly" reloaded, providing the ability + * This allows files to be "freshly" reloaded, providing the ability * to reuse a Mocha instance programmatically. - * Note: does not clear ESM module files from the cache * * <strong>Intended for consumers — not used internally</strong> * * @public + * @see {@link Mocha.unloadFile} + * @see {@link Mocha#loadFiles} * @see {@link Mocha#run} * @returns {Mocha} this * @chainable @@ -1820,7 +1794,7 @@ Mocha.prototype.fgrep = function(str) { * <strong>Previous filter value will be overwritten on each call!</strong> * * @public - * @see [CLI option](../#-grep-regexp-g-regexp) + * @see {@link https://mochajs.org/#-g---grep-pattern|CLI option} * @see {@link Mocha#fgrep} * @see {@link Mocha#invert} * @param {RegExp|String} re - Regular expression used to select tests. @@ -1871,32 +1845,32 @@ Mocha.prototype.invert = function() { /** * Enables or disables ignoring global leaks. * - * @deprecated since v7.0.0 * @public * @see {@link Mocha#checkLeaks} - * @param {boolean} [ignoreLeaks=false] - Whether to ignore global leaks. + * @param {boolean} ignoreLeaks - Whether to ignore global leaks. * @return {Mocha} this * @chainable + * @example + * + * // Ignore global leaks + * mocha.ignoreLeaks(true); */ Mocha.prototype.ignoreLeaks = function(ignoreLeaks) { - utils.deprecate( - '"ignoreLeaks()" is DEPRECATED, please use "checkLeaks()" instead.' - ); - this.options.checkLeaks = !ignoreLeaks; + this.options.ignoreLeaks = Boolean(ignoreLeaks); return this; }; /** - * Enables or disables checking for global variables leaked while running tests. + * Enables checking for global variables leaked while running tests. * * @public - * @see [CLI option](../#-check-leaks) - * @param {boolean} [checkLeaks=true] - Whether to check for global variable leaks. + * @see {@link https://mochajs.org/#--check-leaks|CLI option} + * @see {@link Mocha#ignoreLeaks} * @return {Mocha} this * @chainable */ -Mocha.prototype.checkLeaks = function(checkLeaks) { - this.options.checkLeaks = checkLeaks !== false; +Mocha.prototype.checkLeaks = function() { + this.options.ignoreLeaks = false; return this; }; @@ -1904,13 +1878,11 @@ Mocha.prototype.checkLeaks = function(checkLeaks) { * Displays full stack trace upon test failure. * * @public - * @see [CLI option](../#-full-trace) - * @param {boolean} [fullTrace=true] - Whether to print full stacktrace upon failure. * @return {Mocha} this * @chainable */ -Mocha.prototype.fullTrace = function(fullTrace) { - this.options.fullTrace = fullTrace !== false; +Mocha.prototype.fullTrace = function() { + this.options.fullStackTrace = true; return this; }; @@ -1918,7 +1890,8 @@ Mocha.prototype.fullTrace = function(fullTrace) { * Enables desktop notification support if prerequisite software installed. * * @public - * @see [CLI option](../#-growl-g) + * @see {@link Mocha#isGrowlCapable} + * @see {@link Mocha#_growl} * @return {Mocha} this * @chainable */ @@ -1961,121 +1934,62 @@ Mocha.prototype._growl = growl.notify; * Specifies whitelist of variable names to be expected in global scope. * * @public - * @see [CLI option](../#-global-variable-name) + * @see {@link https://mochajs.org/#--globals-names|CLI option} * @see {@link Mocha#checkLeaks} - * @param {String[]|String} global - Accepted global variable name(s). + * @param {String[]|String} globals - Accepted global variable name(s). * @return {Mocha} this * @chainable * @example * * // Specify variables to be expected in global scope - * mocha.global(['jQuery', 'MyLib']); - */ -Mocha.prototype.global = function(global) { - this.options.global = (this.options.global || []) - .concat(global) - .filter(Boolean) - .filter(function(elt, idx, arr) { - return arr.indexOf(elt) === idx; - }); + * mocha.globals(['jQuery', 'MyLib']); + */ +Mocha.prototype.globals = function(globals) { + this.options.globals = (this.options.globals || []) + .concat(globals) + .filter(Boolean); return this; }; -// for backwards compability, 'globals' is an alias of 'global' -Mocha.prototype.globals = Mocha.prototype.global; /** * Enables or disables TTY color output by screen-oriented reporters. * - * @deprecated since v7.0.0 * @public - * @see {@link Mocha#color} * @param {boolean} colors - Whether to enable color output. * @return {Mocha} this * @chainable */ Mocha.prototype.useColors = function(colors) { - utils.deprecate('"useColors()" is DEPRECATED, please use "color()" instead.'); if (colors !== undefined) { - this.options.color = colors; + this.options.useColors = colors; } return this; }; /** - * Enables or disables TTY color output by screen-oriented reporters. - * - * @public - * @see [CLI option](../#-color-c-colors) - * @param {boolean} [color=true] - Whether to enable color output. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.color = function(color) { - this.options.color = color !== false; - return this; -}; - -/** * Determines if reporter should use inline diffs (rather than +/-) * in test failure output. * - * @deprecated since v7.0.0 * @public - * @see {@link Mocha#inlineDiffs} - * @param {boolean} [inlineDiffs=false] - Whether to use inline diffs. + * @param {boolean} inlineDiffs - Whether to use inline diffs. * @return {Mocha} this * @chainable */ Mocha.prototype.useInlineDiffs = function(inlineDiffs) { - utils.deprecate( - '"useInlineDiffs()" is DEPRECATED, please use "inlineDiffs()" instead.' - ); - this.options.inlineDiffs = inlineDiffs !== undefined && inlineDiffs; - return this; -}; - -/** - * Enables or disables reporter to use inline diffs (rather than +/-) - * in test failure output. - * - * @public - * @see [CLI option](../#-inline-diffs) - * @param {boolean} [inlineDiffs=true] - Whether to use inline diffs. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.inlineDiffs = function(inlineDiffs) { - this.options.inlineDiffs = inlineDiffs !== false; + this.options.useInlineDiffs = inlineDiffs !== undefined && inlineDiffs; return this; }; /** * Determines if reporter should include diffs in test failure output. * - * @deprecated since v7.0.0 * @public - * @see {@link Mocha#diff} - * @param {boolean} [hideDiff=false] - Whether to hide diffs. + * @param {boolean} hideDiff - Whether to hide diffs. * @return {Mocha} this * @chainable */ Mocha.prototype.hideDiff = function(hideDiff) { - utils.deprecate('"hideDiff()" is DEPRECATED, please use "diff()" instead.'); - this.options.diff = !(hideDiff === true); - return this; -}; - -/** - * Enables or disables reporter to include diff in test failure output. - * - * @public - * @see [CLI option](../#-diff) - * @param {boolean} [diff=true] - Whether to show diff on failure. - * @return {Mocha} this - * @chainable - */ -Mocha.prototype.diff = function(diff) { - this.options.diff = diff !== false; + this.options.hideDiff = hideDiff !== undefined && hideDiff; return this; }; @@ -2088,8 +2002,9 @@ Mocha.prototype.diff = function(diff) { * If the value is `0`, timeouts will be disabled. * * @public - * @see [CLI option](../#-timeout-ms-t-ms) - * @see [Timeouts](../#timeouts) + * @see {@link https://mochajs.org/#-t---timeout-ms|CLI option} + * @see {@link https://mochajs.org/#--no-timeouts|CLI option} + * @see {@link https://mochajs.org/#timeouts|Timeouts} * @see {@link Mocha#enableTimeouts} * @param {number|string} msecs - Timeout threshold value. * @return {Mocha} this @@ -2112,8 +2027,7 @@ Mocha.prototype.timeout = function(msecs) { * Sets the number of times to retry failed tests. * * @public - * @see [CLI option](../#-retries-n) - * @see [Retry Tests](../#retry-tests) + * @see {@link https://mochajs.org/#retry-tests|Retry Tests} * @param {number} retry - Number of times to retry failed tests. * @return {Mocha} this * @chainable @@ -2131,7 +2045,7 @@ Mocha.prototype.retries = function(n) { * Sets slowness threshold value. * * @public - * @see [CLI option](../#-slow-ms-s-ms) + * @see {@link https://mochajs.org/#-s---slow-ms|CLI option} * @param {number} msecs - Slowness threshold value. * @return {Mocha} this * @chainable @@ -2153,7 +2067,8 @@ Mocha.prototype.slow = function(msecs) { * Enables or disables timeouts. * * @public - * @see [CLI option](../#-timeout-ms-t-ms) + * @see {@link https://mochajs.org/#-t---timeout-ms|CLI option} + * @see {@link https://mochajs.org/#--no-timeouts|CLI option} * @param {boolean} enableTimeouts - Whether to enable timeouts. * @return {Mocha} this * @chainable @@ -2169,13 +2084,11 @@ Mocha.prototype.enableTimeouts = function(enableTimeouts) { * Forces all tests to either accept a `done` callback or return a promise. * * @public - * @see [CLI option](../#-async-only-a) - * @param {boolean} [asyncOnly=true] - Wether to force `done` callback or promise. * @return {Mocha} this * @chainable */ -Mocha.prototype.asyncOnly = function(asyncOnly) { - this.options.asyncOnly = asyncOnly !== false; +Mocha.prototype.asyncOnly = function() { + this.options.asyncOnly = true; return this; }; @@ -2192,16 +2105,14 @@ Mocha.prototype.noHighlighting = function() { }; /** - * Enables or disables uncaught errors to propagate. + * Enables uncaught errors to propagate (in browser). * * @public - * @see [CLI option](../#-allow-uncaught) - * @param {boolean} [allowUncaught=true] - Whether to propagate uncaught errors. * @return {Mocha} this * @chainable */ -Mocha.prototype.allowUncaught = function(allowUncaught) { - this.options.allowUncaught = allowUncaught !== false; +Mocha.prototype.allowUncaught = function() { + this.options.allowUncaught = true; return this; }; @@ -2213,7 +2124,7 @@ Mocha.prototype.allowUncaught = function(allowUncaught) { * Used to perform asynch operations before any suites are run. * * @public - * @see [delayed root suite](../#delayed-root-suite) + * @see {@link https://mochajs.org/#delayed-root-suite|delayed root suite} * @returns {Mocha} this * @chainable */ @@ -2226,13 +2137,11 @@ Mocha.prototype.delay = function delay() { * Causes tests marked `only` to fail the suite. * * @public - * @see [CLI option](../#-forbid-only) - * @param {boolean} [forbidOnly=true] - Whether tests marked `only` fail the suite. * @returns {Mocha} this * @chainable */ -Mocha.prototype.forbidOnly = function(forbidOnly) { - this.options.forbidOnly = forbidOnly !== false; +Mocha.prototype.forbidOnly = function() { + this.options.forbidOnly = true; return this; }; @@ -2240,13 +2149,11 @@ Mocha.prototype.forbidOnly = function(forbidOnly) { * Causes pending tests and tests marked `skip` to fail the suite. * * @public - * @see [CLI option](../#-forbid-pending) - * @param {boolean} [forbidPending=true] - Whether pending tests fail the suite. * @returns {Mocha} this * @chainable */ -Mocha.prototype.forbidPending = function(forbidPending) { - this.options.forbidPending = forbidPending !== false; +Mocha.prototype.forbidPending = function() { + this.options.forbidPending = true; return this; }; @@ -2280,17 +2187,14 @@ Object.defineProperty(Mocha.prototype, 'version', { * the cache first! * * @public + * @see {@link Mocha#loadFiles} * @see {@link Mocha#unloadFiles} * @see {@link Runner#run} * @param {DoneCB} [fn] - Callback invoked when test execution completed. - * @returns {Runner} runner instance - * @example - * - * // exit with non-zero status if there were test failures - * mocha.run(failures => process.exitCode = failures ? 1 : 0); + * @return {Runner} runner instance */ Mocha.prototype.run = function(fn) { - if (this.files.length && !this.loadAsync) { + if (this.files.length) { this.loadFiles(); } var suite = this.suite; @@ -2299,8 +2203,8 @@ Mocha.prototype.run = function(fn) { var runner = new exports.Runner(suite, options.delay); createStatsCollector(runner); var reporter = new this._reporter(runner, options); - runner.checkLeaks = options.checkLeaks === true; - runner.fullStackTrace = options.fullTrace; + runner.ignoreLeaks = options.ignoreLeaks !== false; + runner.fullStackTrace = options.fullStackTrace; runner.asyncOnly = options.asyncOnly; runner.allowUncaught = options.allowUncaught; runner.forbidOnly = options.forbidOnly; @@ -2308,17 +2212,17 @@ Mocha.prototype.run = function(fn) { if (options.grep) { runner.grep(options.grep, options.invert); } - if (options.global) { - runner.globals(options.global); + if (options.globals) { + runner.globals(options.globals); } if (options.growl) { this._growl(runner); } - if (options.color !== undefined) { - exports.reporters.Base.useColors = options.color; + if (options.useColors !== undefined) { + exports.reporters.Base.useColors = options.useColors; } - exports.reporters.Base.inlineDiffs = options.inlineDiffs; - exports.reporters.Base.hideDiff = !options.diff; + exports.reporters.Base.inlineDiffs = options.useInlineDiffs; + exports.reporters.Base.hideDiff = options.hideDiff; function done(failures) { fn = fn || utils.noop; @@ -2333,17 +2237,16 @@ Mocha.prototype.run = function(fn) { }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../package.json":90,"./context":5,"./errors":6,"./esm-utils":42,"./growl":2,"./hook":7,"./interfaces":11,"./mocharc.json":15,"./reporters":21,"./runnable":33,"./runner":34,"./stats-collector":35,"./suite":36,"./test":37,"./utils":38,"_process":69,"escape-string-regexp":49,"path":42}],15:[function(require,module,exports){ +},{"../package.json":90,"./context":5,"./errors":6,"./growl":2,"./hook":7,"./interfaces":11,"./mocharc.json":15,"./reporters":21,"./runnable":33,"./runner":34,"./stats-collector":35,"./suite":36,"./test":37,"./utils":38,"_process":69,"escape-string-regexp":49,"path":42}],15:[function(require,module,exports){ module.exports={ "diff": true, - "extension": ["js", "cjs", "mjs"], + "extension": ["js"], "opts": "./test/mocha.opts", "package": "./package.json", "reporter": "spec", "slow": 75, "timeout": 2000, - "ui": "bdd", - "watch-ignore": ["node_modules", ".git"] + "ui": "bdd" } },{}],16:[function(require,module,exports){ @@ -2389,12 +2292,7 @@ exports = module.exports = Base; * Check if both stdio streams are associated with a tty. */ -var isatty = process.stdout.isTTY && process.stderr.isTTY; - -/** - * Save log references to avoid tests interfering (see GH-3604). - */ -var consoleLog = console.log; +var isatty = tty.isatty(1) && tty.isatty(2); /** * Enable coloring by default, except in the browser interface. @@ -2518,14 +2416,14 @@ exports.cursor = { } }; -var showDiff = (exports.showDiff = function(err) { +function showDiff(err) { return ( err && err.showDiff !== false && sameType(err.actual, err.expected) && err.expected !== undefined ); -}); +} function stringifyDiffObjs(err) { if (!utils.isString(err.actual) || !utils.isString(err.expected)) { @@ -2546,19 +2444,9 @@ function stringifyDiffObjs(err) { * @return {string} Diff */ var generateDiff = (exports.generateDiff = function(actual, expected) { - try { - return exports.inlineDiffs - ? inlineDiff(actual, expected) - : unifiedDiff(actual, expected); - } catch (err) { - var msg = - '\n ' + - color('diff added', '+ expected') + - ' ' + - color('diff removed', '- actual: failed to generate Mocha diff') + - '\n'; - return msg; - } + return exports.inlineDiffs + ? inlineDiff(actual, expected) + : unifiedDiff(actual, expected); }); /** @@ -2571,8 +2459,7 @@ var generateDiff = (exports.generateDiff = function(actual, expected) { * Error property */ exports.list = function(failures) { - var multipleErr, multipleTest; - Base.consoleLog(); + console.log(); failures.forEach(function(test, i) { // format var fmt = @@ -2582,16 +2469,7 @@ exports.list = function(failures) { // msg var msg; - var err; - if (test.err && test.err.multiple) { - if (multipleTest !== test) { - multipleTest = test; - multipleErr = [test.err].concat(test.err.multiple); - } - err = multipleErr.shift(); - } else { - err = test.err; - } + var err = test.err; var message; if (err.message && typeof err.message.toString === 'function') { message = err.message + ''; @@ -2642,7 +2520,7 @@ exports.list = function(failures) { testTitle += str; }); - Base.consoleLog(fmt, i + 1, testTitle, msg, stack); + console.log(fmt, i + 1, testTitle, msg, stack); }); }; @@ -2682,12 +2560,7 @@ function Base(runner, options) { if (showDiff(err)) { stringifyDiffObjs(err); } - // more than one error per test - if (test.err && err instanceof Error) { - test.err.multiple = (test.err.multiple || []).concat(err); - } else { - test.err = err; - } + test.err = err; failures.push(test); }); } @@ -2696,13 +2569,13 @@ function Base(runner, options) { * Outputs common epilogue used by many of the bundled reporters. * * @public - * @memberof Mocha.reporters + * @memberof Mocha.reporters.Base */ Base.prototype.epilogue = function() { var stats = this.stats; var fmt; - Base.consoleLog(); + console.log(); // passes fmt = @@ -2710,26 +2583,26 @@ Base.prototype.epilogue = function() { color('green', ' %d passing') + color('light', ' (%s)'); - Base.consoleLog(fmt, stats.passes || 0, milliseconds(stats.duration)); + console.log(fmt, stats.passes || 0, milliseconds(stats.duration)); // pending if (stats.pending) { fmt = color('pending', ' ') + color('pending', ' %d pending'); - Base.consoleLog(fmt, stats.pending); + console.log(fmt, stats.pending); } // failures if (stats.failures) { fmt = color('fail', ' %d failing'); - Base.consoleLog(fmt, stats.failures); + console.log(fmt, stats.failures); Base.list(this.failures); - Base.consoleLog(); + console.log(); } - Base.consoleLog(); + console.log(); }; /** @@ -2882,8 +2755,6 @@ function sameType(a, b) { return objToString.call(a) === objToString.call(b); } -Base.consoleLog = consoleLog; - Base.abstract = true; }).call(this,require('_process')) @@ -2934,45 +2805,41 @@ function Doc(runner, options) { return; } ++indents; - Base.consoleLog('%s<section class="suite">', indent()); + console.log('%s<section class="suite">', indent()); ++indents; - Base.consoleLog('%s<h1>%s</h1>', indent(), utils.escape(suite.title)); - Base.consoleLog('%s<dl>', indent()); + console.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title)); + console.log('%s<dl>', indent()); }); runner.on(EVENT_SUITE_END, function(suite) { if (suite.root) { return; } - Base.consoleLog('%s</dl>', indent()); + console.log('%s</dl>', indent()); --indents; - Base.consoleLog('%s</section>', indent()); + console.log('%s</section>', indent()); --indents; }); runner.on(EVENT_TEST_PASS, function(test) { - Base.consoleLog('%s <dt>%s</dt>', indent(), utils.escape(test.title)); + console.log('%s <dt>%s</dt>', indent(), utils.escape(test.title)); var code = utils.escape(utils.clean(test.body)); - Base.consoleLog('%s <dd><pre><code>%s</code></pre></dd>', indent(), code); + console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code); }); runner.on(EVENT_TEST_FAIL, function(test, err) { - Base.consoleLog( + console.log( '%s <dt class="error">%s</dt>', indent(), utils.escape(test.title) ); var code = utils.escape(utils.clean(test.body)); - Base.consoleLog( + console.log( '%s <dd class="error"><pre><code>%s</code></pre></dd>', indent(), code ); - Base.consoleLog( - '%s <dd class="error">%s</dd>', - indent(), - utils.escape(err) - ); + console.log('%s <dd class="error">%s</dd>', indent(), utils.escape(err)); }); } @@ -3050,7 +2917,7 @@ function Dot(runner, options) { }); runner.once(EVENT_RUN_END, function() { - process.stdout.write('\n'); + console.log(); self.epilogue(); }); } @@ -3424,8 +3291,8 @@ function hideSuitesWithout(classname) { */ function unhide() { var els = document.getElementsByClassName('suite hidden'); - while (els.length > 0) { - els[0].className = els[0].className.replace('suite hidden', 'suite'); + for (var i = 0; i < els.length; ++i) { + els[i].className = els[i].className.replace('suite hidden', 'suite'); } } @@ -3810,7 +3677,7 @@ function Landing(runner, options) { runner.once(EVENT_RUN_END, function() { cursor.show(); - process.stdout.write('\n'); + console.log(); self.epilogue(); }); } @@ -3868,7 +3735,7 @@ function List(runner, options) { var n = 0; runner.on(EVENT_RUN_BEGIN, function() { - Base.consoleLog(); + console.log(); }); runner.on(EVENT_TEST_BEGIN, function(test) { @@ -3877,7 +3744,7 @@ function List(runner, options) { runner.on(EVENT_TEST_PENDING, function(test) { var fmt = color('checkmark', ' -') + color('pending', ' %s'); - Base.consoleLog(fmt, test.fullTitle()); + console.log(fmt, test.fullTitle()); }); runner.on(EVENT_TEST_PASS, function(test) { @@ -3886,12 +3753,12 @@ function List(runner, options) { color('pass', ' %s: ') + color(test.speed, '%dms'); cursor.CR(); - Base.consoleLog(fmt, test.fullTitle(), test.duration); + console.log(fmt, test.fullTitle(), test.duration); }); runner.on(EVENT_TEST_FAIL, function(test) { cursor.CR(); - Base.consoleLog(color('fail', ' %d) %s'), ++n, test.fullTitle()); + console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); }); runner.once(EVENT_RUN_END, self.epilogue.bind(self)); @@ -4419,7 +4286,7 @@ function Progress(runner, options) { // tests started runner.on(EVENT_RUN_BEGIN, function() { - process.stdout.write('\n'); + console.log(); cursor.hide(); }); @@ -4452,7 +4319,7 @@ function Progress(runner, options) { // and the failures if any runner.once(EVENT_RUN_END, function() { cursor.show(); - process.stdout.write('\n'); + console.log(); self.epilogue(); }); } @@ -4514,24 +4381,24 @@ function Spec(runner, options) { } runner.on(EVENT_RUN_BEGIN, function() { - Base.consoleLog(); + console.log(); }); runner.on(EVENT_SUITE_BEGIN, function(suite) { ++indents; - Base.consoleLog(color('suite', '%s%s'), indent(), suite.title); + console.log(color('suite', '%s%s'), indent(), suite.title); }); runner.on(EVENT_SUITE_END, function() { --indents; if (indents === 1) { - Base.consoleLog(); + console.log(); } }); runner.on(EVENT_TEST_PENDING, function(test) { var fmt = indent() + color('pending', ' - %s'); - Base.consoleLog(fmt, test.title); + console.log(fmt, test.title); }); runner.on(EVENT_TEST_PASS, function(test) { @@ -4541,19 +4408,19 @@ function Spec(runner, options) { indent() + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s'); - Base.consoleLog(fmt, test.title); + console.log(fmt, test.title); } else { fmt = indent() + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s') + color(test.speed, ' (%dms)'); - Base.consoleLog(fmt, test.title, test.duration); + console.log(fmt, test.title, test.duration); } }); runner.on(EVENT_TEST_FAIL, function(test) { - Base.consoleLog(indent() + color('fail', ' %d) %s'), ++n, test.title); + console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); }); runner.once(EVENT_RUN_END, self.epilogue.bind(self)); @@ -5010,7 +4877,7 @@ XUnit.prototype.write = function(line) { } else if (typeof process === 'object' && process.stdout) { process.stdout.write(line + '\n'); } else { - Base.consoleLog(line); + console.log(line); } }; @@ -5031,9 +4898,9 @@ XUnit.prototype.test = function(test) { if (test.state === STATE_FAILED) { var err = test.err; var diff = - !Base.hideDiff && Base.showDiff(err) - ? '\n' + Base.generateDiff(err.actual, err.expected) - : ''; + Base.hideDiff || !err.actual || !err.expected + ? '' + : '\n' + Base.generateDiff(err.actual, err.expected); this.write( tag( 'testcase', @@ -5223,8 +5090,7 @@ Runnable.prototype.enableTimeouts = function(enabled) { * @public */ Runnable.prototype.skip = function() { - this.pending = true; - throw new Pending('sync skip; aborting execution'); + throw new Pending('sync skip'); }; /** @@ -5423,45 +5289,43 @@ Runnable.prototype.run = function(fn) { fn(err); } - // for .resetTimeout() and Runner#uncaught() + // for .resetTimeout() this.callback = done; - if (this.fn && typeof this.fn.call !== 'function') { - done( - new TypeError( - 'A runnable must be passed a function as its second argument.' - ) - ); - return; - } - // explicit async with `done` argument if (this.async) { this.resetTimeout(); // allows skip() to be used in an explicit async context this.skip = function asyncSkip() { - this.pending = true; - done(); - // halt execution, the uncaught handler will ignore the failure. + done(new Pending('async skip call')); + // halt execution. the Runnable will be marked pending + // by the previous call, and the uncaught handler will ignore + // the failure. throw new Pending('async skip; aborting execution'); }; + if (this.allowUncaught) { + return callFnAsync(this.fn); + } try { callFnAsync(this.fn); } catch (err) { - // handles async runnables which actually run synchronously emitted = true; - if (err instanceof Pending) { - return; // done() is already called in this.skip() - } else if (this.allowUncaught) { - throw err; - } done(Runnable.toValueOrError(err)); } return; } + if (this.allowUncaught) { + if (this.isPending()) { + done(); + } else { + callFn(this.fn); + } + return; + } + // sync or promise-returning try { if (this.isPending()) { @@ -5471,11 +5335,6 @@ Runnable.prototype.run = function(fn) { } } catch (err) { emitted = true; - if (err instanceof Pending) { - return done(); - } else if (this.allowUncaught) { - throw err; - } done(Runnable.toValueOrError(err)); } @@ -5620,9 +5479,8 @@ var sQuote = utils.sQuote; var stackFilter = utils.stackTraceFilter(); var stringify = utils.stringify; var type = utils.type; -var errors = require('./errors'); -var createInvalidExceptionError = errors.createInvalidExceptionError; -var createUnsupportedError = errors.createUnsupportedError; +var createInvalidExceptionError = require('./errors') + .createInvalidExceptionError; /** * Non-enumerable globals. @@ -5731,11 +5589,6 @@ function Runner(suite, delay) { this.total = suite.total(); this.failures = 0; this.on(constants.EVENT_TEST_END, function(test) { - if (test.retriedTest() && test.parent) { - var idx = - test.parent.tests && test.parent.tests.indexOf(test.retriedTest()); - if (idx > -1) test.parent.tests[idx] = test; - } self.checkGlobals(test); }); this.on(constants.EVENT_HOOK_END, function(hook) { @@ -5743,7 +5596,7 @@ function Runner(suite, delay) { }); this._defaultGrep = /.*/; this.grep(this._defaultGrep); - this.globals(this.globalProps()); + this.globals(this.globalProps().concat(extraGlobals())); } /** @@ -5846,7 +5699,7 @@ Runner.prototype.globals = function(arr) { * @private */ Runner.prototype.checkGlobals = function(test) { - if (!this.checkLeaks) { + if (this.ignoreLeaks) { return; } var ok = this._globals; @@ -5917,7 +5770,8 @@ Runner.prototype.fail = function(test, err) { * - Failed `before each` hook skips remaining tests in a * suite and jumps to corresponding `after each` hook, * which is run only once - * - Failed `after` hook does not alter execution order + * - Failed `after` hook does not alter + * execution order * - Failed `after each` hook skips remaining tests in a * suite and subsuites, but executes other `after each` * hooks @@ -5987,37 +5841,34 @@ Runner.prototype.hook = function(name, fn) { if (testError) { self.fail(self.test, testError); } - // conditional skip - if (hook.pending) { - if (name === HOOK_TYPE_AFTER_EACH) { - // TODO define and implement use case - if (self.test) { - self.test.pending = true; + if (err) { + if (err instanceof Pending) { + if (name === HOOK_TYPE_AFTER_ALL) { + utils.deprecate( + 'Skipping a test within an "after all" hook is DEPRECATED and will throw an exception in a future version of Mocha. ' + + 'Use a return statement or other means to abort hook execution.' + ); } - } else if (name === HOOK_TYPE_BEFORE_EACH) { - if (self.test) { - self.test.pending = true; + if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) { + if (self.test) { + self.test.pending = true; + } + } else { + suite.tests.forEach(function(test) { + test.pending = true; + }); + suite.suites.forEach(function(suite) { + suite.pending = true; + }); + // a pending hook won't be executed twice. + hook.pending = true; } - self.emit(constants.EVENT_HOOK_END, hook); - hook.pending = false; // activates hook for next test - return fn(new Error('abort hookDown')); - } else if (name === HOOK_TYPE_BEFORE_ALL) { - suite.tests.forEach(function(test) { - test.pending = true; - }); - suite.suites.forEach(function(suite) { - suite.pending = true; - }); } else { - hook.pending = false; - var errForbid = createUnsupportedError('`this.skip` forbidden'); - self.failHook(hook, errForbid); - return fn(errForbid); + self.failHook(hook, err); + + // stop executing hooks, notify callee of hook err + return fn(err); } - } else if (err) { - self.failHook(hook, err); - // stop executing hooks, notify callee of hook err - return fn(err); } self.emit(constants.EVENT_HOOK_END, hook); delete hook.ctx.currentTest; @@ -6129,9 +5980,6 @@ Runner.prototype.runTest = function(fn) { test.asyncOnly = true; } test.on('error', function(err) { - if (err instanceof Pending) { - return; - } self.fail(test, err); }); if (this.allowUncaught) { @@ -6227,7 +6075,6 @@ Runner.prototype.runTests = function(suite, fn) { return; } - // static skip, no hooks are executed if (test.isPending()) { if (self.forbidPending) { test.isPending = alwaysFalse; @@ -6243,7 +6090,6 @@ Runner.prototype.runTests = function(suite, fn) { // execute test and hook(s) self.emit(constants.EVENT_TEST_BEGIN, (self.test = test)); self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) { - // conditional skip within beforeEach if (test.isPending()) { if (self.forbidPending) { test.isPending = alwaysFalse; @@ -6253,13 +6099,7 @@ Runner.prototype.runTests = function(suite, fn) { self.emit(constants.EVENT_TEST_PENDING, test); } self.emit(constants.EVENT_TEST_END, test); - // skip inner afterEach hooks below errSuite level - var origSuite = self.suite; - self.suite = errSuite || self.suite; - return self.hookUp(HOOK_TYPE_AFTER_EACH, function(e, eSuite) { - self.suite = origSuite; - next(e, eSuite); - }); + return next(); } if (err) { return hookErr(err, errSuite, false); @@ -6267,20 +6107,14 @@ Runner.prototype.runTests = function(suite, fn) { self.currentRunnable = self.test; self.runTest(function(err) { test = self.test; - // conditional skip within it - if (test.pending) { - if (self.forbidPending) { - test.isPending = alwaysFalse; + if (err) { + var retry = test.currentRetry(); + if (err instanceof Pending && self.forbidPending) { self.fail(test, new Error('Pending test forbidden')); - delete test.isPending; - } else { + } else if (err instanceof Pending) { + test.pending = true; self.emit(constants.EVENT_TEST_PENDING, test); - } - self.emit(constants.EVENT_TEST_END, test); - return self.hookUp(HOOK_TYPE_AFTER_EACH, next); - } else if (err) { - var retry = test.currentRetry(); - if (retry < test.retries()) { + } else if (retry < test.retries()) { var clonedTest = test.clone(); clonedTest.currentRetry(retry + 1); tests.unshift(clonedTest); @@ -6294,6 +6128,11 @@ Runner.prototype.runTests = function(suite, fn) { self.fail(test, err); } self.emit(constants.EVENT_TEST_END, test); + + if (err instanceof Pending) { + return next(); + } + return self.hookUp(HOOK_TYPE_AFTER_EACH, next); } @@ -6325,6 +6164,7 @@ Runner.prototype.runSuite = function(suite, fn) { var i = 0; var self = this; var total = this.grepTotal(suite); + var afterAllHookCalled = false; debug('run suite %s', suite.fullTitle()); @@ -6372,13 +6212,21 @@ Runner.prototype.runSuite = function(suite, fn) { self.suite = suite; self.nextSuite = next; - // remove reference to test - delete self.test; - - self.hook(HOOK_TYPE_AFTER_ALL, function() { - self.emit(constants.EVENT_SUITE_END, suite); + if (afterAllHookCalled) { fn(errSuite); - }); + } else { + // mark that the afterAll block has been called once + // and so can be skipped if there is an error in it. + afterAllHookCalled = true; + + // remove reference to test + delete self.test; + + self.hook(HOOK_TYPE_AFTER_ALL, function() { + self.emit(constants.EVENT_SUITE_END, suite); + fn(errSuite); + }); + } } this.nextSuite = next; @@ -6392,7 +6240,7 @@ Runner.prototype.runSuite = function(suite, fn) { }; /** - * Handle uncaught exceptions within runner. + * Handle uncaught exceptions. * * @param {Error} err * @private @@ -6401,11 +6249,6 @@ Runner.prototype.uncaught = function(err) { if (err instanceof Pending) { return; } - // browser does not exit script when throwing in global.onerror() - if (this.allowUncaught && !process.browser) { - throw err; - } - if (err) { debug('uncaught exception %O', err); } else { @@ -6441,37 +6284,43 @@ Runner.prototype.uncaught = function(err) { runnable.clearTimeout(); - if (runnable.isFailed()) { - // Ignore error if already failed - return; - } else if (runnable.isPending()) { - // report 'pending test' retrospectively as failed - runnable.isPending = alwaysFalse; - this.fail(runnable, err); - delete runnable.isPending; + // Ignore errors if already failed or pending + // See #3226 + if (runnable.isFailed() || runnable.isPending()) { return; } - // we cannot recover gracefully if a Runnable has already passed // then fails asynchronously - if (runnable.isPassed()) { - this.fail(runnable, err); - this.abort(); - } else { + var alreadyPassed = runnable.isPassed(); + // this will change the state to "failed" regardless of the current value + this.fail(runnable, err); + if (!alreadyPassed) { + // recover from test + if (runnable.type === constants.EVENT_TEST_BEGIN) { + this.emit(constants.EVENT_TEST_END, runnable); + this.hookUp(HOOK_TYPE_AFTER_EACH, this.next); + return; + } debug(runnable); - return runnable.callback(err); + + // recover from hooks + var errSuite = this.suite; + + // XXX how about a less awful way to determine this? + // if hook failure is in afterEach block + if (runnable.fullTitle().indexOf('after each') > -1) { + return this.hookErr(err, errSuite, true); + } + // if hook failure is in beforeEach block + if (runnable.fullTitle().indexOf('before each') > -1) { + return this.hookErr(err, errSuite, false); + } + // if hook failure is in after or before blocks + return this.nextSuite(errSuite); } -}; -/** - * Handle uncaught exceptions after runner's end event. - * - * @param {Error} err - * @private - */ -Runner.prototype.uncaughtEnd = function uncaughtEnd(err) { - if (err instanceof Pending) return; - throw err; + // bail + this.emit(constants.EVENT_RUN_END); }; /** @@ -6521,12 +6370,10 @@ Runner.prototype.run = function(fn) { this.on(constants.EVENT_RUN_END, function() { debug(constants.EVENT_RUN_END); process.removeListener('uncaughtException', uncaught); - process.on('uncaughtException', self.uncaughtEnd); fn(self.failures); }); // uncaught exception - process.removeListener('uncaughtException', self.uncaughtEnd); process.on('uncaughtException', uncaught); if (this._delay) { @@ -6535,9 +6382,7 @@ Runner.prototype.run = function(fn) { this.emit(constants.EVENT_DELAY_BEGIN, rootSuite); rootSuite.once(EVENT_ROOT_SUITE_RUN, start); } else { - Runner.immediately(function() { - start(); - }); + start(); } return this; @@ -6626,6 +6471,30 @@ function thrown2Error(err) { ); } +/** + * Array of globals dependent on the environment. + * + * @return {Array} + * @deprecated + * @todo remove; long since unsupported + * @private + */ +function extraGlobals() { + if (typeof process === 'object' && typeof process.version === 'string') { + var parts = process.version.split('.'); + var nodeVersion = parts.reduce(function(a, v) { + return (a << 8) | v; + }); + + // 'errno' was renamed to process._errno in v0.9.11. + if (nodeVersion < 0x00090b) { + return ['errno']; + } + } + + return []; +} + Runner.constants = constants; /** @@ -7405,18 +7274,6 @@ function Test(title, fn) { */ utils.inherits(Test, Runnable); -/** - * Set or get retried test - * - * @private - */ -Test.prototype.retriedTest = function(n) { - if (!arguments.length) { - return this._retriedTest; - } - this._retriedTest = n; -}; - Test.prototype.clone = function() { var test = new Test(this.title, this.fn); test.timeout(this.timeout()); @@ -7424,7 +7281,6 @@ Test.prototype.clone = function() { test.enableTimeouts(this.enableTimeouts()); test.retries(this.retries()); test.currentRetry(this.currentRetry()); - test.retriedTest(this.retriedTest() || this); test.globals(this.globals()); test.parent = this.parent; test.file = this.file; @@ -7490,6 +7346,80 @@ exports.isString = function(obj) { }; /** + * Watch the given `files` for changes + * and invoke `fn(file)` on modification. + * + * @private + * @param {Array} files + * @param {Function} fn + */ +exports.watch = function(files, fn) { + var options = {interval: 100}; + var debug = require('debug')('mocha:watch'); + files.forEach(function(file) { + debug('file %s', file); + fs.watchFile(file, options, function(curr, prev) { + if (prev.mtime < curr.mtime) { + fn(file); + } + }); + }); +}; + +/** + * Predicate to screen `pathname` for further consideration. + * + * @description + * Returns <code>false</code> for pathname referencing: + * <ul> + * <li>'npm' package installation directory + * <li>'git' version control directory + * </ul> + * + * @private + * @param {string} pathname - File or directory name to screen + * @return {boolean} whether pathname should be further considered + * @example + * ['node_modules', 'test.js'].filter(considerFurther); // => ['test.js'] + */ +function considerFurther(pathname) { + var ignore = ['node_modules', '.git']; + + return !~ignore.indexOf(pathname); +} + +/** + * Lookup files in the given `dir`. + * + * @description + * Filenames are returned in _traversal_ order by the OS/filesystem. + * **Make no assumption that the names will be sorted in any fashion.** + * + * @private + * @param {string} dir + * @param {string[]} [exts=['js']] + * @param {Array} [ret=[]] + * @return {Array} + */ +exports.files = function(dir, exts, ret) { + ret = ret || []; + exts = exts || ['js']; + + fs.readdirSync(dir) + .filter(considerFurther) + .forEach(function(dirent) { + var pathname = path.join(dir, dirent); + if (fs.lstatSync(pathname).isDirectory()) { + exports.files(pathname, exts, ret); + } else if (hasMatchingExtname(pathname, exts)) { + ret.push(pathname); + } + }); + + return ret; +}; + +/** * Compute a slug from the given `str`. * * @private @@ -7924,41 +7854,32 @@ function isHiddenOnUnix(pathname) { * * @public * @memberof Mocha.utils + * @todo Fix extension handling * @param {string} filepath - Base path to start searching from. - * @param {string[]} [extensions=[]] - File extensions to look for. - * @param {boolean} [recursive=false] - Whether to recurse into subdirectories. + * @param {string[]} extensions - File extensions to look for. + * @param {boolean} recursive - Whether to recurse into subdirectories. * @return {string[]} An array of paths. * @throws {Error} if no files match pattern. * @throws {TypeError} if `filepath` is directory and `extensions` not provided. */ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { - extensions = extensions || []; - recursive = recursive || false; var files = []; var stat; if (!fs.existsSync(filepath)) { - var pattern; - if (glob.hasMagic(filepath)) { - // Handle glob as is without extensions - pattern = filepath; + if (fs.existsSync(filepath + '.js')) { + filepath += '.js'; } else { - // glob pattern e.g. 'filepath+(.js|.ts)' - var strExtensions = extensions - .map(function(v) { - return '.' + v; - }) - .join('|'); - pattern = filepath + '+(' + strExtensions + ')'; - } - files = glob.sync(pattern, {nodir: true}); - if (!files.length) { - throw createNoFilesMatchPatternError( - 'Cannot find any files matching pattern ' + exports.dQuote(filepath), - filepath - ); + // Handle glob + files = glob.sync(filepath); + if (!files.length) { + throw createNoFilesMatchPatternError( + 'Cannot find any files matching pattern ' + exports.dQuote(filepath), + filepath + ); + } + return files; } - return files; } // Handle file @@ -7989,7 +7910,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { // ignore error return; } - if (!extensions.length) { + if (!extensions) { throw createMissingArgumentError( util.format( 'Argument %s required when argument %s is a directory', @@ -8085,8 +8006,7 @@ exports.stackTraceFilter = function() { function isMochaInternal(line) { return ( ~line.indexOf('node_modules' + slash + 'mocha' + slash) || - ~line.indexOf(slash + 'mocha.js') || - ~line.indexOf(slash + 'mocha.min.js') + ~line.indexOf(slash + 'mocha.js') ); } @@ -8268,30 +8188,8 @@ exports.defineConstants = function(obj) { return Object.freeze(exports.createMap(obj)); }; -/** - * Whether current version of Node support ES modules - * - * @description - * Versions prior to 10 did not support ES Modules, and version 10 has an old incompatibile version of ESM. - * This function returns whether Node.JS has ES Module supports that is compatible with Mocha's needs, - * which is version >=12.11. - * - * @returns {Boolean} whether the current version of Node.JS supports ES Modules in a way that is compatible with Mocha - */ -exports.supportsEsModules = function() { - if (!process.browser && process.versions && process.versions.node) { - var versionFields = process.versions.node.split('.'); - var major = +versionFields[0]; - var minor = +versionFields[1]; - - if (major >= 13 || (major === 12 && minor >= 11)) { - return true; - } - } -}; - }).call(this,require('_process'),require("buffer").Buffer) -},{"./errors":6,"_process":69,"buffer":43,"fs":42,"glob":42,"he":54,"object.assign":65,"path":42,"util":89}],39:[function(require,module,exports){ +},{"./errors":6,"_process":69,"buffer":43,"debug":45,"fs":42,"glob":42,"he":54,"object.assign":65,"path":42,"util":89}],39:[function(require,module,exports){ 'use strict' exports.byteLength = byteLength @@ -18171,7 +18069,7 @@ function hasOwnProperty(obj, prop) { },{"./support/isBuffer":88,"_process":69,"inherits":56}],90:[function(require,module,exports){ module.exports={ "name": "mocha", - "version": "7.1.1", + "version": "6.1.4", "homepage": "https://mochajs.org/", "notifyLogo": "https://ibin.co/4QuRuGjXvl36.png" } |