From 07edde41ec3d129818ab6a6f80e80a4d6b9dddfe Mon Sep 17 00:00:00 2001 From: Yaroslav Admin Date: Wed, 20 Jan 2021 01:57:13 +0100 Subject: [PATCH 01/27] docs: document how to load files from another server (#3622) Fixes #1486 --- docs/config/02-files.md | 62 ++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/docs/config/02-files.md b/docs/config/02-files.md index f85c4d772..aac9304e0 100644 --- a/docs/config/02-files.md +++ b/docs/config/02-files.md @@ -1,28 +1,14 @@ -**The `files` array determines which files are included in the browser and which files are watched and served by Karma.** +The `files` array determines which files are included in the browser, watched, and served by Karma. - -## Pattern matching and `basePath` -- All of the relative patterns will get resolved using the `basePath` first. -- If the `basePath` is a relative path, it gets resolved to the - directory where the configuration file is located. -- Eventually, all the patterns will get resolved into files using - [glob], so you can use [minimatch] expressions like `test/unit/**/*.spec.js`. - - -## Ordering -- The order of patterns determines the order in which files are included in the browser. -- Multiple files matching a single pattern are sorted alphabetically. -- Each file is included exactly once. If multiple patterns match the - same file, it's included as if it only matched the first pattern. - - -## Included, served, watched -Each pattern is either a simple string or an object with the following properties: +## `files` +**Type.** Array +**No Default.** This property is mandatory. +**Description.** Each item is either a string (equivalent to `{ pattern: "" }`) or an object with the following properties: ### `pattern` * **Type.** String * **No Default.** This property is mandatory. -* **Description.** The pattern to use for matching. +* **Description.** The pattern to use for matching. See below for details on how patterns are resolved. ### `type` * **Type.** String @@ -68,6 +54,18 @@ Each pattern is either a simple string or an object with the following propertie * **Default.** `false` * **Description.** Should the files be served from disk on each request by Karma's webserver? +## Pattern matching and `basePath` +- All of the relative patterns will get resolved using the `basePath` first. +- If the `basePath` is a relative path, it gets resolved to the + directory where the configuration file is located. +- Eventually, all the patterns will get resolved into files using + [glob], so you can use [minimatch] expressions like `test/unit/**/*.spec.js`. + +## Ordering +- The order of patterns determines the order in which files are included in the browser. +- Multiple files matching a single pattern are sorted alphabetically. +- Each file is included exactly once. If multiple patterns match the + same file, it's included as if it only matched the first pattern. ## Preprocessor transformations Depending on preprocessor configuration, be aware that files loaded may be transformed and no longer available in @@ -100,6 +98,30 @@ files: [ ], ``` +## Loading files from another server + +Pattern can also be an absolute URL. This allows including files which are not served by Karma. + +Example: + +```javascript +config.set({ + files: [ + 'https://example.com/my-lib.js', + { pattern: 'https://example.com/my-lib', type: 'js' } + ] +}) +``` + +Absolute URLs have some special rules comparing to the regular file paths: + +- Globing is not support, so each URL must be specified as a separate pattern. +- Most of the regular options are not supported: + - `watched` is always `false` + - `included` is always `true` + - `served` is always `false` + - `nocache` is always `false` + ## Loading Assets By default all assets are served at `http://localhost:[PORT]/base/` From 25d9abb76929b6ea8abe1cf040ba6db2f269d50e Mon Sep 17 00:00:00 2001 From: Yaroslav Admin Date: Wed, 20 Jan 2021 18:45:25 +0100 Subject: [PATCH 02/27] fix: restore `customFileHandlers` provider (#3624) The removal of `customFileHandlers` turned out to be more disruptive change than anticipated as this provider is still used by several popular plugins: https://github.com/karma-runner/karma/issues/3619, https://github.com/ryanclark/karma-webpack/issues/462, https://github.com/angular/angular-cli/issues/19815. Hence we restore the provider and print a deprecation warning to make upgrading easier. This should give more time for the plugin authors to release new versions and users to adopt these versions. --- lib/server.js | 2 ++ lib/web-server.js | 21 +++++++++++++++++++++ test/unit/web-server.spec.js | 24 +++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/server.js b/lib/server.js index ff03553eb..8b7823e43 100644 --- a/lib/server.js +++ b/lib/server.js @@ -82,6 +82,8 @@ class Server extends KarmaEventEmitter { filesPromise: ['factory', createFilesPromise], socketServer: ['factory', createSocketIoServer], executor: ['factory', Executor.factory], + // TODO: Deprecated. Remove in the next major + customFileHandlers: ['value', []], reporter: ['factory', reporter.createReporters], capturedBrowsers: ['factory', BrowserCollection.factory], args: ['value', {}], diff --git a/lib/web-server.js b/lib/web-server.js index 379818435..394066b7b 100644 --- a/lib/web-server.js +++ b/lib/web-server.js @@ -16,6 +16,25 @@ const proxyMiddleware = require('./middleware/proxy') const log = require('./logger').create('web-server') +function createCustomHandler (customFileHandlers, config) { + let warningDone = false + + return function (request, response, next) { + const handler = customFileHandlers.find((handler) => handler.urlRegex.test(request.url)) + + if (customFileHandlers.length > 0 && !warningDone) { + warningDone = true + log.warn('The `customFileHandlers` is deprecated and will be removed in Karma 7. Please upgrade plugins relying on this provider.') + } + + return handler + ? handler.handler(request, response, 'fake/static', 'fake/adapter', config.basePath, 'fake/root') + : next() + } +} + +createCustomHandler.$inject = ['customFileHandlers', 'config'] + function createFilesPromise (emitter, fileList) { // Set an empty list of files to avoid race issues with // file_list_modified not having been emitted yet @@ -58,6 +77,8 @@ function createWebServer (injector, config) { handler.use(injector.invoke(sourceFilesMiddleware.create)) // TODO(vojta): extract the proxy into a plugin handler.use(proxyMiddlewareInstance) + // TODO: Deprecated. Remove in the next major + handler.use(injector.invoke(createCustomHandler)) if (config.middleware) { config.middleware.forEach((middleware) => handler.use(injector.get('middleware:' + middleware))) diff --git a/test/unit/web-server.spec.js b/test/unit/web-server.spec.js index 8517f172e..fccf57b65 100644 --- a/test/unit/web-server.spec.js +++ b/test/unit/web-server.spec.js @@ -31,7 +31,7 @@ describe('web-server', () => { // NOTE(vojta): only loading once, to speed things up // this relies on the fact that none of these tests mutate fs const m = mocks.loadFile(path.join(__dirname, '/../../lib/web-server.js'), _mocks, _globals) - server = emitter = null + let customFileHandlers = server = emitter = null let beforeMiddlewareActive = false let middlewareActive = false const servedFiles = (files) => { @@ -40,6 +40,7 @@ describe('web-server', () => { describe('request', () => { beforeEach(() => { + customFileHandlers = [] emitter = new EventEmitter() const config = { basePath: '/base/path', @@ -56,6 +57,7 @@ describe('web-server', () => { const injector = new di.Injector([{ config: ['value', config], + customFileHandlers: ['value', customFileHandlers], emitter: ['value', emitter], fileList: ['value', { files: { served: [], included: [] } }], filesPromise: ['factory', m.createFilesPromise], @@ -180,6 +182,22 @@ describe('web-server', () => { }) }) + it('should load custom handlers', () => { + servedFiles(new Set()) + + customFileHandlers.push({ + urlRegex: /\/some\/weird/, + handler (request, response, staticFolder, adapterFolder, baseFolder, urlRoot) { + response.writeHead(222) + response.end('CONTENT') + } + }) + + return request(server) + .get('/some/weird/url') + .expect(222, 'CONTENT') + }) + it('should serve 404 for non-existing files', () => { servedFiles(new Set()) @@ -196,6 +214,7 @@ describe('web-server', () => { cert: fs.readFileSync(path.join(__dirname, '/certificates/server.crt')) } + customFileHandlers = [] emitter = new EventEmitter() const injector = new di.Injector([{ @@ -206,6 +225,7 @@ describe('web-server', () => { httpsServerOptions: credentials, client: { useIframe: true, useSingleWindow: false } }], + customFileHandlers: ['value', customFileHandlers], emitter: ['value', emitter], fileList: ['value', { files: { served: [], included: [] } }], filesPromise: ['factory', m.createFilesPromise], @@ -244,10 +264,12 @@ describe('web-server', () => { cert: fs.readFileSync(path.join(__dirname, '/certificates/server.crt')) } + customFileHandlers = [] emitter = new EventEmitter() const injector = new di.Injector([{ config: ['value', { basePath: '/base/path', urlRoot: '/', httpModule: http2, protocol: 'https:', httpsServerOptions: credentials }], + customFileHandlers: ['value', customFileHandlers], emitter: ['value', emitter], fileList: ['value', { files: { served: [], included: [] } }], filesPromise: ['factory', m.createFilesPromise], From 69baddc843e4852a6770bfc1212fc2bce1f38fe7 Mon Sep 17 00:00:00 2001 From: johnjbarton Date: Wed, 20 Jan 2021 09:45:44 -0800 Subject: [PATCH 03/27] fix(server): set maxHttpBufferSize to the socket.io v2 default (#3626) Fixes #3621 --- lib/server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/server.js b/lib/server.js index 8b7823e43..0cf94ce97 100644 --- a/lib/server.js +++ b/lib/server.js @@ -43,7 +43,9 @@ function createSocketIoServer (webServer, executor, config) { transports: config.transports, forceJSONP: config.forceJSONP, // Default is 5000 in socket.io v2.x and v3.x. - pingTimeout: config.pingTimeout || 5000 + pingTimeout: config.pingTimeout || 5000, + // Default in v2 is 1e8 and coverage results can fail at 1e6 + maxHttpBufferSize: 1e8 }) // hack to overcome circular dependency From b29fa10db0fb994f7d7016e2d39edfbb421c5060 Mon Sep 17 00:00:00 2001 From: Yaroslav Admin Date: Wed, 20 Jan 2021 18:53:36 +0100 Subject: [PATCH 04/27] chore(test): run unit tests without grunt wrapper (#3618) --- gruntfile.js | 22 --- package-lock.json | 494 ---------------------------------------------- package.json | 5 +- test/mocha.opts | 3 + 4 files changed, 4 insertions(+), 520 deletions(-) delete mode 100644 gruntfile.js create mode 100644 test/mocha.opts diff --git a/gruntfile.js b/gruntfile.js deleted file mode 100644 index 40da12beb..000000000 --- a/gruntfile.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = function (grunt) { - grunt.initConfig({ - mochaTest: { - options: { - reporter: 'dot', - ui: 'bdd', - quiet: false, - colors: true - }, - unit: { - src: [ - 'test/unit/mocha-globals.js', - 'test/unit/**/*.spec.js' - ] - } - } - }) - - grunt.loadNpmTasks('grunt-mocha-test') - - grunt.registerTask('default', ['mochaTest:unit']) -} diff --git a/package-lock.json b/package-lock.json index b92a4b0a0..69090efe4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2416,12 +2416,6 @@ "through": ">=2.2.7 <3" } }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -2599,12 +2593,6 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true - }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -2634,12 +2622,6 @@ "is-string": "^1.0.5" } }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true - }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -4164,12 +4146,6 @@ "minimalistic-assert": "^1.0.0" } }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, "detective": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", @@ -4912,12 +4888,6 @@ } } }, - "eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", - "dev": true - }, "eventemitter3": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", @@ -5021,12 +4991,6 @@ } } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -5062,15 +5026,6 @@ } } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -5357,49 +5312,6 @@ } } }, - "findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", - "dev": true, - "requires": { - "glob": "~5.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - } - }, - "flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true - }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -5457,15 +5369,6 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, "form-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.0.tgz", @@ -5582,12 +5485,6 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "getobject": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", - "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", - "dev": true - }, "gherkin": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gherkin/-/gherkin-5.0.0.tgz", @@ -5649,30 +5546,6 @@ "ini": "^1.3.4" } }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, "globals": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", @@ -5731,147 +5604,6 @@ "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", "dev": true }, - "grunt": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.2.1.tgz", - "integrity": "sha512-zgJjn9N56tScvRt/y0+1QA+zDBnKTrkpyeSBqQPLcZvbqTD/oyGMrdZQXmm6I3828s+FmPvxc3Xv+lgKFtudOw==", - "dev": true, - "requires": { - "dateformat": "~3.0.3", - "eventemitter2": "~0.4.13", - "exit": "~0.1.2", - "findup-sync": "~0.3.0", - "glob": "~7.1.6", - "grunt-cli": "~1.3.2", - "grunt-known-options": "~1.1.0", - "grunt-legacy-log": "~2.0.0", - "grunt-legacy-util": "~1.1.1", - "iconv-lite": "~0.4.13", - "js-yaml": "~3.14.0", - "minimatch": "~3.0.4", - "mkdirp": "~1.0.4", - "nopt": "~3.0.6", - "rimraf": "~3.0.2" - }, - "dependencies": { - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "grunt-cli": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.3.2.tgz", - "integrity": "sha512-8OHDiZZkcptxVXtMfDxJvmN7MVJNE8L/yIcPb4HB7TlyFD1kDvjHrb62uhySsU14wJx9ORMnTuhRMQ40lH/orQ==", - "dev": true, - "requires": { - "grunt-known-options": "~1.1.0", - "interpret": "~1.1.0", - "liftoff": "~2.5.0", - "nopt": "~4.0.1", - "v8flags": "~3.1.1" - }, - "dependencies": { - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - } - } - }, - "grunt-known-options": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", - "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", - "dev": true - }, - "grunt-legacy-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz", - "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==", - "dev": true, - "requires": { - "colors": "~1.1.2", - "grunt-legacy-log-utils": "~2.0.0", - "hooker": "~0.2.3", - "lodash": "~4.17.5" - }, - "dependencies": { - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - } - } - }, - "grunt-legacy-log-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz", - "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==", - "dev": true, - "requires": { - "chalk": "~2.4.1", - "lodash": "~4.17.10" - } - }, - "grunt-legacy-util": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz", - "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==", - "dev": true, - "requires": { - "async": "~1.5.2", - "exit": "~0.1.1", - "getobject": "~0.1.0", - "hooker": "~0.2.3", - "lodash": "~4.17.10", - "underscore.string": "~3.3.4", - "which": "~1.3.0" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } - } - }, - "grunt-mocha-test": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.13.3.tgz", - "integrity": "sha512-zQGEsi3d+ViPPi7/4jcj78afKKAKiAA5n61pknQYi25Ugik+aNOuRmiOkmb8mN2CeG8YxT+YdT1H1Q7B/eNkoQ==", - "dev": true, - "requires": { - "hooker": "^0.2.3", - "mkdirp": "^0.5.0" - } - }, "hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -5974,27 +5706,12 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, "hook-std": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==", "dev": true }, - "hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", - "dev": true - }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", @@ -6387,12 +6104,6 @@ "xtend": "^4.0.0" } }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, "into-stream": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-5.1.1.tgz", @@ -6403,16 +6114,6 @@ "p-is-promise": "^3.0.0" } }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -6603,15 +6304,6 @@ "has-symbols": "^1.0.1" } }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, - "requires": { - "is-unc-path": "^1.0.0" - } - }, "is-running": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", @@ -6648,15 +6340,6 @@ "text-extensions": "^1.0.0" } }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dev": true, - "requires": { - "unc-path-regex": "^0.1.2" - } - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -6948,45 +6631,6 @@ "type-check": "~0.4.0" } }, - "liftoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", - "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", - "dev": true, - "requires": { - "extend": "^3.0.0", - "findup-sync": "^2.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" - }, - "dependencies": { - "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -7195,15 +6839,6 @@ "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", "dev": true }, - "make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -7699,15 +7334,6 @@ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -11352,28 +10978,6 @@ "object-keys": "^1.0.11" } }, - "object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", - "dev": true, - "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", - "dev": true, - "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -11443,12 +11047,6 @@ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, "os-name": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", @@ -11465,22 +11063,6 @@ "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", "dev": true }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "outpipe": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/outpipe/-/outpipe-1.1.1.tgz", @@ -11604,17 +11186,6 @@ "safe-buffer": "^5.1.1" } }, - "parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", - "dev": true, - "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - } - }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -11624,12 +11195,6 @@ "error-ex": "^1.2.0" } }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -11682,21 +11247,6 @@ "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", "dev": true }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "dev": true, - "requires": { - "path-root-regex": "^0.1.0" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true - }, "path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -12122,15 +11672,6 @@ } } }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, "redeyed": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", @@ -12214,16 +11755,6 @@ "path-parse": "^1.0.6" } }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -13744,12 +13275,6 @@ "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", "dev": true }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true - }, "undeclared-identifiers": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", @@ -13763,16 +13288,6 @@ "xtend": "^4.0.1" } }, - "underscore.string": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", - "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", - "dev": true, - "requires": { - "sprintf-js": "^1.0.3", - "util-deprecate": "^1.0.2" - } - }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -13958,15 +13473,6 @@ "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", "dev": true }, - "v8flags": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", - "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", diff --git a/package.json b/package.json index 9fdce2cad..9507faf3a 100644 --- a/package.json +++ b/package.json @@ -449,9 +449,6 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", - "grunt": "^1.2.1", - "grunt-cli": "^1.1.0", - "grunt-mocha-test": "^0.13.2", "http2": "^3.3.6", "husky": "^4.2.5", "jasmine-core": "^3.6.0", @@ -496,7 +493,7 @@ "scripts": { "lint": "eslint . --ext js --ignore-pattern *.tpl.js", "lint:fix": "eslint . --ext js --ignore-pattern *.tpl.js --fix", - "test:unit": "grunt", + "test:unit": "mocha \"test/unit/**/*.spec.js\"", "test:e2e": "cucumber-js test/e2e/*.feature", "test:client": "node bin/karma start test/client/karma.conf.js", "test": "npm run test:unit && npm run test:e2e && npm run test:client", diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 000000000..58a295fd3 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,3 @@ +--reporter dot +--ui bdd +test/unit/mocha-globals.js From e246461c528dd84690b6d086b948986046f28ce7 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 20 Jan 2021 18:52:24 +0000 Subject: [PATCH 05/27] chore(release): 6.0.1 [skip ci] ## [6.0.1](https://github.com/karma-runner/karma/compare/v6.0.0...v6.0.1) (2021-01-20) ### Bug Fixes * **server:** set maxHttpBufferSize to the socket.io v2 default ([#3626](https://github.com/karma-runner/karma/issues/3626)) ([69baddc](https://github.com/karma-runner/karma/commit/69baddc843e4852a6770bfc1212fc2bce1f38fe7)), closes [#3621](https://github.com/karma-runner/karma/issues/3621) * restore `customFileHandlers` provider ([#3624](https://github.com/karma-runner/karma/issues/3624)) ([25d9abb](https://github.com/karma-runner/karma/commit/25d9abb76929b6ea8abe1cf040ba6db2f269d50e)) --- CHANGELOG.md | 8 ++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1556bc8d0..495f1241c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [6.0.1](https://github.com/karma-runner/karma/compare/v6.0.0...v6.0.1) (2021-01-20) + + +### Bug Fixes + +* **server:** set maxHttpBufferSize to the socket.io v2 default ([#3626](https://github.com/karma-runner/karma/issues/3626)) ([69baddc](https://github.com/karma-runner/karma/commit/69baddc843e4852a6770bfc1212fc2bce1f38fe7)), closes [#3621](https://github.com/karma-runner/karma/issues/3621) +* restore `customFileHandlers` provider ([#3624](https://github.com/karma-runner/karma/issues/3624)) ([25d9abb](https://github.com/karma-runner/karma/commit/25d9abb76929b6ea8abe1cf040ba6db2f269d50e)) + # [6.0.0](https://github.com/karma-runner/karma/compare/v5.2.3...v6.0.0) (2021-01-13) diff --git a/package-lock.json b/package-lock.json index 69090efe4..8884670bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "karma", - "version": "6.0.0", + "version": "6.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 9507faf3a..2d7e01c2c 100644 --- a/package.json +++ b/package.json @@ -482,7 +482,7 @@ "engines": { "node": ">= 10" }, - "version": "6.0.0", + "version": "6.0.1", "license": "MIT", "husky": { "hooks": { From 6629e96901dbeae24fbaa4d0bfa009618fb8ee75 Mon Sep 17 00:00:00 2001 From: Long Ho Date: Mon, 25 Jan 2021 13:33:56 -0500 Subject: [PATCH 06/27] fix: avoid ES6+ syntax in client scripts (#3629) * remove template literals and arrow functions that break in IE11 Fixes #3630 --- client/karma.js | 7 ++++--- client/updater.js | 22 +++++++++++----------- static/karma.js | 29 +++++++++++++++-------------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/client/karma.js b/client/karma.js index 0734822ff..20a5e19e0 100644 --- a/client/karma.js +++ b/client/karma.js @@ -191,7 +191,7 @@ function Karma (updater, socket, iframe, opener, navigator, location, document) } socket.emit('karma_error', message) - self.updater.updateTestStatus(`karma_error ${message}`) + self.updater.updateTestStatus('karma_error ' + message) this.complete() return false } @@ -240,8 +240,9 @@ function Karma (updater, socket, iframe, opener, navigator, location, document) // A test could have incorrectly issued a navigate. Wait one turn // to ensure the error from an incorrect navigate is processed. - setTimeout(() => { - if (this.config.clearContext) { + var config = this.config + setTimeout(function () { + if (config.clearContext) { navigateContextTo('about:blank') } diff --git a/client/updater.js b/client/updater.js index 3365b5253..569709e3d 100644 --- a/client/updater.js +++ b/client/updater.js @@ -29,7 +29,7 @@ function StatusUpdater (socket, titleElement, bannerElement, browsersElement) { if (!titleElement || !bannerElement) { return } - titleElement.textContent = `Karma v ${VERSION} - ${connectionText}; test: ${testText}; ${pingText}` + titleElement.textContent = 'Karma v ' + VERSION + ' - ' + connectionText + '; test: ' + testText + '; ' + pingText bannerElement.className = connectionText === 'connected' ? 'online' : 'offline' } @@ -46,32 +46,32 @@ function StatusUpdater (socket, titleElement, bannerElement, browsersElement) { updateBanner() } - socket.on('connect', () => { + socket.on('connect', function () { updateConnectionStatus('connected') }) - socket.on('disconnect', () => { + socket.on('disconnect', function () { updateConnectionStatus('disconnected') }) - socket.on('reconnecting', (sec) => { - updateConnectionStatus(`reconnecting in ${sec} seconds`) + socket.on('reconnecting', function (sec) { + updateConnectionStatus('reconnecting in ' + sec + ' seconds') }) - socket.on('reconnect', () => { + socket.on('reconnect', function () { updateConnectionStatus('reconnected') }) - socket.on('reconnect_failed', () => { + socket.on('reconnect_failed', function () { updateConnectionStatus('reconnect_failed') }) socket.on('info', updateBrowsersInfo) - socket.on('disconnect', () => { + socket.on('disconnect', function () { updateBrowsersInfo([]) }) - socket.on('ping', () => { + socket.on('ping', function () { updatePingStatus('ping...') }) - socket.on('pong', (latency) => { - updatePingStatus(`ping ${latency}ms`) + socket.on('pong', function (latency) { + updatePingStatus('ping ' + latency + 'ms') }) return { updateTestStatus: updateTestStatus } diff --git a/static/karma.js b/static/karma.js index 8c08d3fca..1c1111450 100644 --- a/static/karma.js +++ b/static/karma.js @@ -201,7 +201,7 @@ function Karma (updater, socket, iframe, opener, navigator, location, document) } socket.emit('karma_error', message) - self.updater.updateTestStatus(`karma_error ${message}`) + self.updater.updateTestStatus('karma_error ' + message) this.complete() return false } @@ -250,8 +250,9 @@ function Karma (updater, socket, iframe, opener, navigator, location, document) // A test could have incorrectly issued a navigate. Wait one turn // to ensure the error from an incorrect navigate is processed. - setTimeout(() => { - if (this.config.clearContext) { + var config = this.config + setTimeout(function () { + if (config.clearContext) { navigateContextTo('about:blank') } @@ -384,7 +385,7 @@ function StatusUpdater (socket, titleElement, bannerElement, browsersElement) { if (!titleElement || !bannerElement) { return } - titleElement.textContent = `Karma v ${VERSION} - ${connectionText}; test: ${testText}; ${pingText}` + titleElement.textContent = 'Karma v ' + VERSION + ' - ' + connectionText + '; test: ' + testText + '; ' + pingText bannerElement.className = connectionText === 'connected' ? 'online' : 'offline' } @@ -401,32 +402,32 @@ function StatusUpdater (socket, titleElement, bannerElement, browsersElement) { updateBanner() } - socket.on('connect', () => { + socket.on('connect', function () { updateConnectionStatus('connected') }) - socket.on('disconnect', () => { + socket.on('disconnect', function () { updateConnectionStatus('disconnected') }) - socket.on('reconnecting', (sec) => { - updateConnectionStatus(`reconnecting in ${sec} seconds`) + socket.on('reconnecting', function (sec) { + updateConnectionStatus('reconnecting in ' + sec + ' seconds') }) - socket.on('reconnect', () => { + socket.on('reconnect', function () { updateConnectionStatus('reconnected') }) - socket.on('reconnect_failed', () => { + socket.on('reconnect_failed', function () { updateConnectionStatus('reconnect_failed') }) socket.on('info', updateBrowsersInfo) - socket.on('disconnect', () => { + socket.on('disconnect', function () { updateBrowsersInfo([]) }) - socket.on('ping', () => { + socket.on('ping', function () { updatePingStatus('ping...') }) - socket.on('pong', (latency) => { - updatePingStatus(`ping ${latency}ms`) + socket.on('pong', function (latency) { + updatePingStatus('ping ' + latency + 'ms') }) return { updateTestStatus: updateTestStatus } From 9a6439cb3ca018b722a68093047ea6c8dc6d6fb4 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Mon, 25 Jan 2021 10:36:00 -0800 Subject: [PATCH 07/27] docs: fix link to Sauce Labs plugin (#3627) The Sauce Labs plugin link goes to a module at version 0.0.0 that hasn't been published in 5 years. Update the link to point to the module that is at version 3.4.3 and was published two months ago. In addition, this module is within the karmar-runner GitHub org. --- docs/config/03-browsers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config/03-browsers.md b/docs/config/03-browsers.md index 0f69fa12e..2ae47e05e 100644 --- a/docs/config/03-browsers.md +++ b/docs/config/03-browsers.md @@ -16,7 +16,7 @@ Note: Most of the browser launchers need to be loaded as [plugins]. - [JSDOM](https://www.npmjs.com/package/karma-jsdom-launcher) - [Opera](https://www.npmjs.com/package/karma-opera-launcher) - [Internet Explorer](https://www.npmjs.com/package/karma-ie-launcher) -- [SauceLabs](https://www.npmjs.com/package/karma-saucelabs-launcher) +- [SauceLabs](https://www.npmjs.com/package/karma-sauce-launcher) - [BrowserStack](https://www.npmjs.com/package/karma-browserstack-launcher) - [many more](https://www.npmjs.org/browse/keyword/karma-launcher) From b5c0baa075839e03c8f30f0b16d473dc0150d8a8 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 25 Jan 2021 21:58:49 +0000 Subject: [PATCH 08/27] chore(release): 6.0.2 [skip ci] ## [6.0.2](https://github.com/karma-runner/karma/compare/v6.0.1...v6.0.2) (2021-01-25) ### Bug Fixes * avoid ES6+ syntax in client scripts ([#3629](https://github.com/karma-runner/karma/issues/3629)) ([6629e96](https://github.com/karma-runner/karma/commit/6629e96901dbeae24fbaa4d0bfa009618fb8ee75)), closes [#3630](https://github.com/karma-runner/karma/issues/3630) --- CHANGELOG.md | 7 +++++++ package-lock.json | 2 +- package.json | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 495f1241c..c55633e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [6.0.2](https://github.com/karma-runner/karma/compare/v6.0.1...v6.0.2) (2021-01-25) + + +### Bug Fixes + +* avoid ES6+ syntax in client scripts ([#3629](https://github.com/karma-runner/karma/issues/3629)) ([6629e96](https://github.com/karma-runner/karma/commit/6629e96901dbeae24fbaa4d0bfa009618fb8ee75)), closes [#3630](https://github.com/karma-runner/karma/issues/3630) + ## [6.0.1](https://github.com/karma-runner/karma/compare/v6.0.0...v6.0.1) (2021-01-20) diff --git a/package-lock.json b/package-lock.json index 8884670bd..34025e1a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "karma", - "version": "6.0.1", + "version": "6.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2d7e01c2c..72c1487b2 100644 --- a/package.json +++ b/package.json @@ -257,6 +257,7 @@ "Kostiantyn Kahanskyi ", "Kris Kowal ", "Lenny Urbanowski ", + "Long Ho ", "LoveIsGrief ", "Lucas Theisen ", "Lukasz Zatorski ", @@ -326,6 +327,7 @@ "Remy Sharp ", "Ricardo Melo Joia ", "Rich Kuzsma ", + "Rich Trott ", "Richard Herrera ", "Roarke Gaskill ", "Rob Cherry ", @@ -482,7 +484,7 @@ "engines": { "node": ">= 10" }, - "version": "6.0.1", + "version": "6.0.2", "license": "MIT", "husky": { "hooks": { From 8d589ed9a94e9e8becab9de853fbefce3fdf4807 Mon Sep 17 00:00:00 2001 From: johnjbarton Date: Wed, 27 Jan 2021 10:58:49 -0800 Subject: [PATCH 09/27] chore(testing): stop log4js output in unit tests (#3634) Use https://log4js-node.github.io/log4js-node/recording.html to verify logs --- test/unit/mocha-globals.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/unit/mocha-globals.js b/test/unit/mocha-globals.js index 8afdd587b..6659714ee 100644 --- a/test/unit/mocha-globals.js +++ b/test/unit/mocha-globals.js @@ -1,6 +1,7 @@ const sinon = require('sinon') const chai = require('chai') const logger = require('../../lib/logger') +const recording = require('log4js/lib/appenders/recording') // publish globals that all specs can use global.expect = chai.expect @@ -15,13 +16,14 @@ chai.use(require('chai-subset')) beforeEach(() => { global.sinon = sinon.createSandbox() - // set logger to log INFO, but do not append to console - // so that we can assert logs by logger.on('info', ...) - logger.setup('INFO', false, []) + // Use https://log4js-node.github.io/log4js-node/recording.html to verify logs + const vcr = { vcr: { type: 'recording' } } + logger.setup('INFO', false, vcr) }) afterEach(() => { global.sinon.restore() + recording.erase() }) // TODO(vojta): move to helpers or something From e02858ae0d0de3f05add976b10e4b6b935cc3dd7 Mon Sep 17 00:00:00 2001 From: johnjbarton Date: Wed, 27 Jan 2021 10:59:51 -0800 Subject: [PATCH 10/27] fix(plugins): refactor instantiatePlugin from preproprocessor (#3628) add unit test for plugin.js add comments on role of function and cache --- lib/plugin.js | 38 ++++- lib/preprocessor.js | 36 +--- lib/server.js | 1 + test/unit/plugin.spec.js | 61 +++++++ test/unit/preprocessor.spec.js | 291 +++++++++++++-------------------- 5 files changed, 219 insertions(+), 208 deletions(-) create mode 100644 test/unit/plugin.spec.js diff --git a/lib/plugin.js b/lib/plugin.js index 2e0ad5a24..20a180597 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -50,4 +50,40 @@ function resolve (plugins, emitter) { return modules } -exports.resolve = resolve +/** + Create a function to handle errors in plugin loading. + @param {Object} injector, the dict of dependency injection objects. + @return function closed over injector, which reports errors. +*/ +function createInstantiatePlugin (injector) { + const emitter = injector.get('emitter') + // Cache to avoid report errors multiple times per plugin. + const pluginInstances = new Map() + return function instantiatePlugin (kind, name) { + if (pluginInstances.has(name)) { + return pluginInstances.get(name) + } + + let p + try { + p = injector.get(`${kind}:${name}`) + if (!p) { + log.error(`Failed to instantiate ${kind} ${name}`) + emitter.emit('load_error', kind, name) + } + } catch (e) { + if (e.message.includes(`No provider for "${kind}:${name}"`)) { + log.error(`Cannot load "${name}", it is not registered!\n Perhaps you are missing some plugin?`) + } else { + log.error(`Cannot load "${name}"!\n ` + e.stack) + } + emitter.emit('load_error', kind, name) + } + pluginInstances.set(name, p, `${kind}:${name}`) + return p + } +} + +createInstantiatePlugin.$inject = ['injector'] + +module.exports = { resolve, createInstantiatePlugin } diff --git a/lib/preprocessor.js b/lib/preprocessor.js index 4b56a3633..b6bf75695 100644 --- a/lib/preprocessor.js +++ b/lib/preprocessor.js @@ -70,36 +70,8 @@ async function runProcessors (preprocessors, file, content) { file.sha = CryptoUtils.sha1(content) } -function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, injector) { - const emitter = injector.get('emitter') - const instances = new Map() - - function instantiatePreprocessor (name) { - if (instances.has(name)) { - return instances.get(name) - } - - let p - try { - p = injector.get('preprocessor:' + name) - if (!p) { - log.error(`Failed to instantiate preprocessor ${name}`) - emitter.emit('load_error', 'preprocessor', name) - } - } catch (e) { - if (e.message.includes(`No provider for "preprocessor:${name}"`)) { - log.error(`Can not load "${name}", it is not registered!\n Perhaps you are missing some plugin?`) - } else { - log.error(`Can not load "${name}"!\n ` + e.stack) - } - emitter.emit('load_error', 'preprocessor', name) - } - - instances.set(name, p) - return p - } - _.union.apply(_, Object.values(config)).forEach(instantiatePreprocessor) - +function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, instantiatePlugin) { + _.union.apply(_, Object.values(config)).forEach((name) => instantiatePlugin('preprocessor', name)) return async function preprocess (file) { const buffer = await tryToRead(file.originalPath, log) let isBinary = file.isBinary @@ -121,7 +93,7 @@ function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath .sort((a, b) => b[1] - a[1]) .map((duo) => duo[0]) .reduce((preProcs, name) => { - const p = instantiatePreprocessor(name) + const p = instantiatePlugin('preprocessor', name) if (!isBinary || (p && p.handleBinaryFiles)) { preProcs.push(p) @@ -135,5 +107,5 @@ function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath } } -createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'injector'] +createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'instantiatePlugin'] exports.createPriorityPreprocessor = createPriorityPreprocessor diff --git a/lib/server.js b/lib/server.js index 0cf94ce97..aa96b76ea 100644 --- a/lib/server.js +++ b/lib/server.js @@ -76,6 +76,7 @@ class Server extends KarmaEventEmitter { watcher: ['value', watcher], launcher: ['factory', Launcher.factory], config: ['value', config], + instantiatePlugin: ['factory', plugin.createInstantiatePlugin], preprocess: ['factory', preprocessor.createPriorityPreprocessor], fileList: ['factory', FileList.factory], webServer: ['factory', createWebServer], diff --git a/test/unit/plugin.spec.js b/test/unit/plugin.spec.js new file mode 100644 index 000000000..3ec9824d1 --- /dev/null +++ b/test/unit/plugin.spec.js @@ -0,0 +1,61 @@ +'use strict' + +const createInstantiatePlugin = require('../../lib/plugin').createInstantiatePlugin + +describe('plugin', () => { + describe('createInstantiatePlugin', () => { + it('creates the instantiatePlugin function', () => { + const fakeGet = sinon.stub() + const fakeInjector = { get: fakeGet } + + expect(typeof createInstantiatePlugin(fakeInjector)).to.be.equal('function') + expect(fakeGet).to.have.been.calledWith('emitter') + }) + + it('creates the instantiatePlugin function', () => { + const fakes = { + emitter: { emit: sinon.stub() } + } + const fakeInjector = { get: (id) => fakes[id] } + + const instantiatePlugin = createInstantiatePlugin(fakeInjector) + expect(typeof instantiatePlugin('kind', 'name')).to.be.equal('undefined') + expect(fakes.emitter.emit).to.have.been.calledWith('load_error', 'kind', 'name') + }) + + it('caches plugins', () => { + const fakes = { + emitter: { emit: sinon.stub() }, + 'kind:name': { my: 'plugin' } + } + const fakeInjector = { + get: (id) => { + return fakes[id] + } + } + + const instantiatePlugin = createInstantiatePlugin(fakeInjector) + expect(instantiatePlugin('kind', 'name')).to.be.equal(fakes['kind:name']) + fakeInjector.get = (id) => { throw new Error('failed to cache') } + expect(instantiatePlugin('kind', 'name')).to.be.equal(fakes['kind:name']) + }) + + it('errors if the injector errors', () => { + const fakes = { + emitter: { emit: sinon.stub() } + } + const fakeInjector = { + get: (id) => { + if (id in fakes) { + return fakes[id] + } + throw new Error('fail') + } + } + + const instantiatePlugin = createInstantiatePlugin(fakeInjector) + expect(typeof instantiatePlugin('unknown', 'name')).to.be.equal('undefined') + expect(fakes.emitter.emit).to.have.been.calledWith('load_error', 'unknown', 'name') + }) + }) +}) diff --git a/test/unit/preprocessor.spec.js b/test/unit/preprocessor.spec.js index 2b5fefbc2..5f0b7860d 100644 --- a/test/unit/preprocessor.spec.js +++ b/test/unit/preprocessor.spec.js @@ -1,18 +1,19 @@ 'use strict' const mocks = require('mocks') -const di = require('di') const path = require('path') -const events = require('../../lib/events') - describe('preprocessor', () => { let m let mockFs - let emitterSetting // mimic first few bytes of a pdf file const binarydata = Buffer.from([0x25, 0x50, 0x44, 0x66, 0x46, 0x00]) + // Each test will define a spy; the fakeInstatiatePlugin will return it. + let fakePreprocessor + + const simpleFakeInstantiatePlugin = () => { return fakePreprocessor } + beforeEach(() => { mockFs = mocks.fs.create({ some: { @@ -32,22 +33,16 @@ describe('preprocessor', () => { 'graceful-fs': mockFs, minimatch: require('minimatch') } - emitterSetting = { emitter: ['value', new events.EventEmitter()] } m = mocks.loadFile(path.join(__dirname, '/../../lib/preprocessor.js'), mocks_) }) it('should preprocess matching file', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { file.path = file.path + '-preprocessed' done(null, 'new-content') }) - const injector = new di.Injector([{ - 'preprocessor:fake': [ - 'factory', function () { return fakePreprocessor } - ] - }, emitterSetting]) - const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/a.js', path: 'path' } @@ -58,15 +53,12 @@ describe('preprocessor', () => { }) it('should match directories starting with a dot', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { file.path = file.path + '-preprocessed' done(null, 'new-content') }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/.dir/a.js', path: 'path' } @@ -77,15 +69,12 @@ describe('preprocessor', () => { }) it('should get content if preprocessor is an async function or return Promise with content', async () => { - const fakePreprocessor = sinon.spy(async (content, file, done) => { + fakePreprocessor = sinon.spy(async (content, file, done) => { file.path = file.path + '-preprocessed' return 'new-content' }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/.dir/a.js', path: 'path' } @@ -96,15 +85,12 @@ describe('preprocessor', () => { }) it('should get content if preprocessor is an async function still calling done()', async () => { - const fakePreprocessor = sinon.spy(async (content, file, done) => { + fakePreprocessor = sinon.spy(async (content, file, done) => { file.path = file.path + '-preprocessed' done(null, 'new-content') }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/.dir/a.js', path: 'path' } @@ -115,16 +101,13 @@ describe('preprocessor', () => { }) it('should check patterns after creation when invoked', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { file.path = file.path + '-preprocessed' done(null, 'new-content') }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) const config = { '**/*.txt': ['fake'] } - const pp = m.createPriorityPreprocessor(config, {}, null, injector) + const pp = m.createPriorityPreprocessor(config, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/a.js', path: 'path' } @@ -137,14 +120,11 @@ describe('preprocessor', () => { }) it('should ignore not matching file', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(null, '') }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/a.txt', path: 'path' } @@ -153,34 +133,33 @@ describe('preprocessor', () => { }) it('should apply all preprocessors', async () => { - const fakePreprocessor1 = sinon.spy((content, file, done) => { - file.path = file.path + '-p1' - done(null, content + '-c1') - }) - - const fakePreprocessor2 = sinon.spy((content, file, done) => { - file.path = file.path + '-p2' - done(content + '-c2') - }) - - const injector = new di.Injector([{ - 'preprocessor:fake1': ['factory', function () { return fakePreprocessor1 }], - 'preprocessor:fake2': ['factory', function () { return fakePreprocessor2 }] - }, emitterSetting]) + const fakes = { + fake1: sinon.spy((content, file, done) => { + file.path = file.path + '-p1' + done(null, content + '-c1') + }), + fake2: sinon.spy((content, file, done) => { + file.path = file.path + '-p2' + done(content + '-c2') + }) + } + function fakeInstatiatePlugin (kind, name) { + return fakes[name] + } - const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake1', 'fake2'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake1', 'fake2'] }, {}, null, fakeInstatiatePlugin) const file = { originalPath: '/some/a.js', path: 'path' } await pp(file) - expect(fakePreprocessor1).to.have.been.calledOnce - expect(fakePreprocessor2).to.have.been.calledOnce + expect(fakes.fake1).to.have.been.calledOnce + expect(fakes.fake2).to.have.been.calledOnce expect(file.path).to.equal('path-p1-p2') expect(file.content).to.equal('content-c1-c2') }) it('should compute SHA', async () => { - const pp = m.createPriorityPreprocessor({}, {}, null, new di.Injector([emitterSetting])) + const pp = m.createPriorityPreprocessor({}, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/a.js', path: 'path' } await pp(file) @@ -198,15 +177,11 @@ describe('preprocessor', () => { }) it('should compute SHA from content returned by a processor', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(null, content + '-processed') }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/a.js': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/a.js': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const fileProcess = { originalPath: '/some/a.js', path: 'path' } const fileSkip = { originalPath: '/some/b.js', path: 'path' } @@ -221,15 +196,11 @@ describe('preprocessor', () => { }) it('should return error if any preprocessor fails', () => { - const failingPreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(new Error('Some error'), null) }) - const injector = new di.Injector([{ - 'preprocessor:failing': ['factory', function () { return failingPreprocessor }] - }, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/*.js': ['failing'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['failing'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/a.js', path: 'path' } @@ -241,20 +212,20 @@ describe('preprocessor', () => { }) it('should stop preprocessing after an error', async () => { - const failingPreprocessor = sinon.spy((content, file, done) => { - done(new Error('Some error'), null) - }) - - const fakePreprocessor = sinon.spy((content, file, done) => { - done(null, content) - }) + const fakes = { + failing: sinon.spy((content, file, done) => { + done(new Error('Some error'), null) + }), + fake: sinon.spy((content, file, done) => { + done(null, content) + }) + } - const injector = new di.Injector([{ - 'preprocessor:failing': ['factory', function () { return failingPreprocessor }], - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) + function fakeInstantiatePlugin (kind, name) { + return fakes[name] + } - const pp = m.createPriorityPreprocessor({ '**/*.js': ['failing', 'fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['failing', 'fake'] }, {}, null, fakeInstantiatePlugin) const file = { originalPath: '/some/a.js', path: 'path' } @@ -263,7 +234,7 @@ describe('preprocessor', () => { }, (err) => { expect(err.message).to.equal('Some error') }) - expect(fakePreprocessor).not.to.have.been.called + expect(fakes.fake).not.to.have.been.called }) describe('when fs.readFile fails', () => { @@ -274,15 +245,11 @@ describe('preprocessor', () => { }) it('should retry up to 3 times', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(null, content) }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) await pp(file).then(() => { throw new Error('Should be rejected') @@ -295,9 +262,7 @@ describe('preprocessor', () => { }) it('should throw after 3 retries', async () => { - const injector = new di.Injector([{}, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/*.js': [] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*.js': [] }, {}, null, simpleFakeInstantiatePlugin) await pp(file).then(() => { throw new Error('Should be rejected') @@ -309,15 +274,11 @@ describe('preprocessor', () => { }) it('should not preprocess binary files by default', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(null, content) }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/photo.png', path: 'path' } @@ -327,15 +288,11 @@ describe('preprocessor', () => { }) it('should not preprocess files configured to be binary', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(null, content) }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/proto.pb', path: 'path', isBinary: true } @@ -345,15 +302,11 @@ describe('preprocessor', () => { }) it('should preprocess files configured not to be binary', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(null, content) }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) // Explicit false for isBinary const file = { originalPath: '/some/proto.pb', path: 'path', isBinary: false } @@ -364,16 +317,12 @@ describe('preprocessor', () => { }) it('should preprocess binary files if handleBinaryFiles=true', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(null, content) }) fakePreprocessor.handleBinaryFiles = true - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { return fakePreprocessor }] - }, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/photo.png', path: 'path' } @@ -383,15 +332,11 @@ describe('preprocessor', () => { }) it('should not preprocess binary files with capital letters in extension', async () => { - const fakePreprocessor = sinon.spy((content, file, done) => { + fakePreprocessor = sinon.spy((content, file, done) => { done(null, content) }) - const injector = new di.Injector([{ - 'preprocessor:fake': ['factory', function () { fakePreprocessor }] - }, emitterSetting]) - - const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, injector) + const pp = m.createPriorityPreprocessor({ '**/*': ['fake'] }, {}, null, simpleFakeInstantiatePlugin) const file = { originalPath: '/some/CAM_PHOTO.JPG', path: 'path' } @@ -402,72 +347,68 @@ describe('preprocessor', () => { it('should merge lists of preprocessors using default priority', async () => { const callOrder = [] - const fakePreprocessorA = sinon.spy((content, file, done) => { - callOrder.push('a') - done(null, content) - }) - const fakePreprocessorB = sinon.spy((content, file, done) => { - callOrder.push('b') - done(null, content) - }) - const fakePreprocessorC = sinon.spy((content, file, done) => { - callOrder.push('c') - done(null, content) - }) - const fakePreprocessorD = sinon.spy((content, file, done) => { - callOrder.push('d') - done(null, content) - }) - - const injector = new di.Injector([{ - 'preprocessor:fakeA': ['factory', function () { return fakePreprocessorA }], - 'preprocessor:fakeB': ['factory', function () { return fakePreprocessorB }], - 'preprocessor:fakeC': ['factory', function () { return fakePreprocessorC }], - 'preprocessor:fakeD': ['factory', function () { return fakePreprocessorD }] - }, emitterSetting]) + const fakes = { + fakeA: sinon.spy((content, file, done) => { + callOrder.push('a') + done(null, content) + }), + fakeB: sinon.spy((content, file, done) => { + callOrder.push('b') + done(null, content) + }), + fakeC: sinon.spy((content, file, done) => { + callOrder.push('c') + done(null, content) + }), + fakeD: sinon.spy((content, file, done) => { + callOrder.push('d') + done(null, content) + }) + } + function fakeInstantiatePlugin (kind, name) { + return fakes[name] + } const pp = m.createPriorityPreprocessor({ '/*/a.js': ['fakeA', 'fakeB'], '/some/*': ['fakeB', 'fakeC'], '/some/a.js': ['fakeD'] - }, {}, null, injector) + }, {}, null, fakeInstantiatePlugin) const file = { originalPath: '/some/a.js', path: 'path' } await pp(file) - expect(fakePreprocessorA).to.have.been.called - expect(fakePreprocessorB).to.have.been.called - expect(fakePreprocessorC).to.have.been.called - expect(fakePreprocessorD).to.have.been.called + expect(fakes.fakeA).to.have.been.called + expect(fakes.fakeB).to.have.been.called + expect(fakes.fakeC).to.have.been.called + expect(fakes.fakeD).to.have.been.called expect(callOrder).to.eql(['a', 'b', 'c', 'd']) }) it('should merge lists of preprocessors obeying priority', async () => { const callOrder = [] - const fakePreprocessorA = sinon.spy((content, file, done) => { - callOrder.push('a') - done(null, content) - }) - const fakePreprocessorB = sinon.spy((content, file, done) => { - callOrder.push('b') - done(null, content) - }) - const fakePreprocessorC = sinon.spy((content, file, done) => { - callOrder.push('c') - done(null, content) - }) - const fakePreprocessorD = sinon.spy((content, file, done) => { - callOrder.push('d') - done(null, content) - }) - - const injector = new di.Injector([{ - 'preprocessor:fakeA': ['factory', function () { return fakePreprocessorA }], - 'preprocessor:fakeB': ['factory', function () { return fakePreprocessorB }], - 'preprocessor:fakeC': ['factory', function () { return fakePreprocessorC }], - 'preprocessor:fakeD': ['factory', function () { return fakePreprocessorD }] - }, emitterSetting]) + const fakes = { + fakeA: sinon.spy((content, file, done) => { + callOrder.push('a') + done(null, content) + }), + fakeB: sinon.spy((content, file, done) => { + callOrder.push('b') + done(null, content) + }), + fakeC: sinon.spy((content, file, done) => { + callOrder.push('c') + done(null, content) + }), + fakeD: sinon.spy((content, file, done) => { + callOrder.push('d') + done(null, content) + }) + } + function fakeInstantiatePlugin (kind, name) { + return fakes[name] + } const priority = { fakeA: -1, fakeB: 1, fakeD: 100 } @@ -475,15 +416,15 @@ describe('preprocessor', () => { '/*/a.js': ['fakeA', 'fakeB'], '/some/*': ['fakeB', 'fakeC'], '/some/a.js': ['fakeD'] - }, priority, null, injector) + }, priority, null, fakeInstantiatePlugin) const file = { originalPath: '/some/a.js', path: 'path' } await pp(file) - expect(fakePreprocessorA).to.have.been.called - expect(fakePreprocessorB).to.have.been.called - expect(fakePreprocessorC).to.have.been.called - expect(fakePreprocessorD).to.have.been.called + expect(fakes.fakeA).to.have.been.called + expect(fakes.fakeB).to.have.been.called + expect(fakes.fakeC).to.have.been.called + expect(fakes.fakeD).to.have.been.called expect(callOrder).to.eql(['d', 'b', 'c', 'a']) }) From 7a1344f5879aed58f620ff1717144d89278937f1 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 27 Jan 2021 22:21:28 +0000 Subject: [PATCH 11/27] chore(release): 6.0.3 [skip ci] ## [6.0.3](https://github.com/karma-runner/karma/compare/v6.0.2...v6.0.3) (2021-01-27) ### Bug Fixes * **plugins:** refactor instantiatePlugin from preproprocessor ([#3628](https://github.com/karma-runner/karma/issues/3628)) ([e02858a](https://github.com/karma-runner/karma/commit/e02858ae0d0de3f05add976b10e4b6b935cc3dd7)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c55633e0a..63f838372 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [6.0.3](https://github.com/karma-runner/karma/compare/v6.0.2...v6.0.3) (2021-01-27) + + +### Bug Fixes + +* **plugins:** refactor instantiatePlugin from preproprocessor ([#3628](https://github.com/karma-runner/karma/issues/3628)) ([e02858a](https://github.com/karma-runner/karma/commit/e02858ae0d0de3f05add976b10e4b6b935cc3dd7)) + ## [6.0.2](https://github.com/karma-runner/karma/compare/v6.0.1...v6.0.2) (2021-01-25) diff --git a/package-lock.json b/package-lock.json index 34025e1a5..476bd6832 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "karma", - "version": "6.0.2", + "version": "6.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 72c1487b2..9c132864c 100644 --- a/package.json +++ b/package.json @@ -484,7 +484,7 @@ "engines": { "node": ">= 10" }, - "version": "6.0.2", + "version": "6.0.3", "license": "MIT", "husky": { "hooks": { From dbd1943e6901c4cb86280db7663afde32f9ab86c Mon Sep 17 00:00:00 2001 From: Yaroslav Admin Date: Mon, 1 Feb 2021 18:53:00 +0100 Subject: [PATCH 12/27] fix: ensure that Karma supports running tests on IE 11 (#3642) They were failing because of unsupported arrow function syntax in two places: - in karma.spec.js file (only affecting Karma's own tests) - in socket.io.js file installed from NPM, hence the dependency update (this affected Karma consumers) Enabled BrowserStack tests on IE 11 to prevent regressions. --- package-lock.json | 94 +++++++++++++++++++-------------------- package.json | 2 +- test/client/karma.conf.js | 37 +++++---------- test/client/karma.spec.js | 4 +- 4 files changed, 61 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index 476bd6832..c05418b26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4297,31 +4297,31 @@ } }, "engine.io": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.5.tgz", - "integrity": "sha512-Ri+whTNr2PKklxQkfbGjwEo+kCBUM4Qxk4wtLqLrhH+b1up2NFL9g9pjYWiCV/oazwB0rArnvF/ZmZN2ab5Hpg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.0.tgz", + "integrity": "sha512-vW7EAtn0HDQ4MtT5QbmCHF17TaYLONv2/JwdYsq9USPRZVM4zG7WB3k0Nc321z8EuSOlhGokrYlYx4176QhD0A==", "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.4.1", "cors": "~2.8.5", - "debug": "~4.1.0", + "debug": "~4.3.1", "engine.io-parser": "~4.0.0", - "ws": "^7.1.2" + "ws": "~7.4.2" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -12451,54 +12451,54 @@ } }, "socket.io": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.4.tgz", - "integrity": "sha512-Vj1jUoO75WGc9txWd311ZJJqS9Dr8QtNJJ7gk2r7dcM/yGe9sit7qOijQl3GAwhpBOz/W8CwkD7R6yob07nLbA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.0.tgz", + "integrity": "sha512-Aqg2dlRh6xSJvRYK31ksG65q4kmBOqU4g+1ukhPcoT6wNGYoIwSYPlCPuRwOO9pgLUajojGFztl6+V2opmKcww==", "requires": { "@types/cookie": "^0.4.0", "@types/cors": "^2.8.8", - "@types/node": "^14.14.7", + "@types/node": "^14.14.10", "accepts": "~1.3.4", "base64id": "~2.0.0", - "debug": "~4.1.0", - "engine.io": "~4.0.0", - "socket.io-adapter": "~2.0.3", - "socket.io-parser": "~4.0.1" + "debug": "~4.3.1", + "engine.io": "~4.1.0", + "socket.io-adapter": "~2.1.0", + "socket.io-parser": "~4.0.3" }, "dependencies": { "@types/node": { - "version": "14.14.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", - "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==" + "version": "14.14.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", + "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==" }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, "socket.io-adapter": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3.tgz", - "integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz", + "integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==" }, "socket.io-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.2.tgz", - "integrity": "sha512-Bs3IYHDivwf+bAAuW/8xwJgIiBNtlvnjYRc4PbXgniLmcP1BrakBoq/QhO24rgtgW7VZ7uAaswRGxutUnlAK7g==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", "requires": { "@types/component-emitter": "^1.2.10", "component-emitter": "~1.3.0", - "debug": "~4.1.0" + "debug": "~4.3.1" }, "dependencies": { "component-emitter": { @@ -12507,17 +12507,17 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -13812,9 +13812,9 @@ } }, "ws": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", - "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==" + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, "xmlbuilder": { "version": "12.0.0", diff --git a/package.json b/package.json index 9c132864c..dcd577fb8 100644 --- a/package.json +++ b/package.json @@ -428,7 +428,7 @@ "qjobs": "^1.2.0", "range-parser": "^1.2.1", "rimraf": "^3.0.2", - "socket.io": "^3.0.4", + "socket.io": "^3.1.0", "source-map": "^0.6.1", "tmp": "0.2.1", "ua-parser-js": "^0.7.23", diff --git a/test/client/karma.conf.js b/test/client/karma.conf.js index 0b1281d78..e700b64de 100644 --- a/test/client/karma.conf.js +++ b/test/client/karma.conf.js @@ -14,35 +14,20 @@ const launchers = { browser: 'firefox', os: 'Windows', os_version: '10' - } + }, // bs_safari: { // base: 'BrowserStack', - // browser: 'safari', - // browser_version: '9.0', - // os_version: 'El Capitan', - // os: 'OS X' - // }, - // bs_ie_11: { - // base: 'BrowserStack', - // browser: 'ie', - // browser_version: '11.0', - // os: 'Windows', - // os_version: '10' - // }, - // bs_ie_10: { - // base: 'BrowserStack', - // browser: 'ie', - // browser_version: '10.0', - // os: 'Windows', - // os_version: '8' + // browser: 'Safari', + // os: 'OS X', + // os_version: 'Big Sur' // }, - // bs_ie_9: { - // base: 'BrowserStack', - // browser: 'ie', - // browser_version: '9.0', - // os: 'Windows', - // os_version: '7' - // } + bs_ie: { + base: 'BrowserStack', + browser: 'IE', + browser_version: '11.0', + os: 'Windows', + os_version: '10' + } } // Verify the install. This will run async but that's ok we'll see the log. diff --git a/test/client/karma.spec.js b/test/client/karma.spec.js index b1c50a00a..036a4ecbd 100644 --- a/test/client/karma.spec.js +++ b/test/client/karma.spec.js @@ -17,7 +17,7 @@ describe('Karma', function () { beforeEach(function () { mockTestStatus = '' updater = { - updateTestStatus: (s) => { + updateTestStatus: function (s) { mockTestStatus = s } } @@ -454,7 +454,7 @@ describe('Karma', function () { clock.tick(500) ck.complete() - setTimeout(() => { + setTimeout(function () { assert(windowLocation.href === 'http://return.com') done() }, 5) From 9c755e0d61f1e8fb0fed1281fc8a331d5f1734be Mon Sep 17 00:00:00 2001 From: Yaroslav Admin Date: Mon, 1 Feb 2021 19:19:12 +0100 Subject: [PATCH 13/27] fix(cli): temporarily disable strict parameters validation (#3641) As discussed in #3625 there are multiple cases, where Karma users rely on the possibility to pass arbitrary options/arguments to the karma CLI. These arguments are either handled in a `karma.conf.js` file or are consumed by Karma plugins. Given the disruptive effect of the strict parameters validation and the lack of the feasible workarounds, the feature is reverted until we have better understanding of the custom options/arguments use cases and can implement a solution which works for everybody. --- lib/cli.js | 5 +++- test/e2e/cli.feature | 58 ++++++++++++-------------------------------- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 0aac17e9a..6e7fdac4f 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -153,7 +153,7 @@ function describeRoot () { .command('stop [configFile]', 'Stop the server.', describeStop) .command('completion', 'Shell completion for karma.', describeCompletion) .demandCommand(1, 'Command not specified.') - .strict() + .strictCommands() .describe('help', 'Print usage and options.') .describe('version', 'Print current version.') } @@ -164,6 +164,7 @@ function describeInit (yargs) { 'INIT - Initialize a config file.\n\n' + 'Usage:\n' + ' $0 init [configFile]') + .strictCommands(false) .version(false) .positional('configFile', { describe: 'Name of the generated Karma configuration file', @@ -215,6 +216,7 @@ function describeRun (yargs) { 'RUN - Run the tests (requires running server).\n\n' + 'Usage:\n' + ' $0 run [configFile] [-- ]') + .strictCommands(false) .version(false) .positional('configFile', { describe: 'Path to the Karma configuration file', @@ -247,6 +249,7 @@ function describeStop (yargs) { 'STOP - Stop the server (requires running server).\n\n' + 'Usage:\n' + ' $0 stop [configFile]') + .strictCommands(false) .version(false) .positional('configFile', { describe: 'Path to the Karma configuration file', diff --git a/test/e2e/cli.feature b/test/e2e/cli.feature index 476d29a8e..e4f235985 100644 --- a/test/e2e/cli.feature +++ b/test/e2e/cli.feature @@ -55,52 +55,24 @@ Feature: CLI --help Print usage and options. [boolean] --version Print current version. [boolean] - Unknown argument: strat + Unknown command: strat """ - Scenario: Error when option is unknown - When I execute Karma with arguments: "start --invalid-option" - Then the stderr is exactly: + Scenario: No error when unknown option and argument are passed in + Given a configuration with: """ - Karma - Spectacular Test Runner for JavaScript. - - START - Start the server / do a single run. - - Usage: - karma start [configFile] - - Positionals: - configFile Path to the Karma configuration file [string] - - Options: - --help Print usage and options. [boolean] - --port Port where the server is running. - --auto-watch Auto watch source files and run on change. - --detached Detach the server. - --no-auto-watch Do not watch source files. - --log-level Level - of logging. - --colors Use colors when reporting and printing logs. - --no-colors Do not use colors when reporting or printing - logs. - --reporters List of reporters (available: dots, progress, - junit, growl, coverage). - --browsers List of browsers to start (eg. --browsers - Chrome,ChromeCanary,Firefox). - --capture-timeout Kill browser if does not capture in - given time [ms]. - --single-run Run the test when browsers captured and exit. - --no-single-run Disable single-run. - --report-slower-than Report tests that are slower than - given time [ms]. - --fail-on-empty-test-suite Fail on empty test suite. - --no-fail-on-empty-test-suite Do not fail on empty test suite. - --fail-on-failing-test-suite Fail on failing test suite. - --no-fail-on-failing-test-suite Do not fail on failing test suite. - --format-error A path to a file that exports the format - function. [string] - - Unknown arguments: invalid-option, invalidOption + files = ['basic/plus.js', 'basic/test.js']; + browsers = ['ChromeHeadlessNoSandbox']; + plugins = [ + 'karma-jasmine', + 'karma-chrome-launcher' + ]; + """ + When I execute Karma with arguments: "start sandbox/karma.conf.js unknown-argument --unknown-option" + Then it passes with: + """ + .. + Chrome Headless """ Scenario: Init command help From 2a57b230cd6b27e1a6e903ca6557c5a6b3e31bf6 Mon Sep 17 00:00:00 2001 From: Yaroslav Admin Date: Mon, 1 Feb 2021 20:55:02 +0100 Subject: [PATCH 14/27] fix(client): fix a false positive page reload error in Safari (#3643) Previous code was susceptible to the race condition in Safari browser. Specifically below piece: ``` iframe.src = policy.createURL(url) karmaNavigating = false ``` There is no guarantee that onbeforeunload event will be triggered synchronously after setting `iframe.src`. It was the case in Chrome and Firefox, but not in Safari, where this caused an error every test run. New approach resets the onbeforeunload handler before navigation is triggered by Karma itself to avoid the need for synchronization and this race condition altogether. The handler will be restored by the new context once it is loaded. PS Code is a bit fragile as there is an implicit dependency on `onbeforeunload` from the context, but I can't think of a cleaner approach. --- client/karma.js | 18 ++++++------------ context/karma.js | 6 +++--- static/context.js | 6 +++--- static/karma.js | 18 ++++++------------ test/client/karma.spec.js | 2 +- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/client/karma.js b/client/karma.js index 20a5e19e0..5586bdc9e 100644 --- a/client/karma.js +++ b/client/karma.js @@ -5,7 +5,6 @@ var util = require('../common/util') function Karma (updater, socket, iframe, opener, navigator, location, document) { this.updater = updater var startEmitted = false - var karmaNavigating = false var self = this var queryParams = util.parseQueryParams(location.search) var browserId = queryParams.id || util.generateId('manual-') @@ -83,21 +82,21 @@ function Karma (updater, socket, iframe, opener, navigator, location, document) var childWindow = null function navigateContextTo (url) { - karmaNavigating = true if (self.config.useIframe === false) { // run in new window if (self.config.runInParent === false) { // If there is a window already open, then close it // DEV: In some environments (e.g. Electron), we don't have setter access for location if (childWindow !== null && childWindow.closed !== true) { + // The onbeforeunload listener was added by context to catch + // unexpected navigations while running tests. + childWindow.onbeforeunload = undefined childWindow.close() } childWindow = opener(url) - karmaNavigating = false // run context on parent element (client_with_context) // using window.__karma__.scriptUrls to get the html element strings and load them dynamically } else if (url !== 'about:blank') { - karmaNavigating = false var loadScript = function (idx) { if (idx < window.__karma__.scriptUrls.length) { var parser = new DOMParser() @@ -128,15 +127,10 @@ function Karma (updater, socket, iframe, opener, navigator, location, document) } // run in iframe } else { + // The onbeforeunload listener was added by the context to catch + // unexpected navigations while running tests. + iframe.contentWindow.onbeforeunload = undefined iframe.src = policy.createURL(url) - karmaNavigating = false - } - } - - this.onbeforeunload = function () { - if (!karmaNavigating) { - // TODO(vojta): show what test (with explanation about jasmine.UPDATE_INTERVAL) - self.error('Some of your tests did a full page reload!') } } diff --git a/context/karma.js b/context/karma.js index 677767c9a..859d56d19 100644 --- a/context/karma.js +++ b/context/karma.js @@ -77,9 +77,9 @@ function ContextKarma (callParentKarmaMethod) { contextWindow.onerror = function () { return self.error.apply(self, arguments) } - // DEV: We must defined a function since we don't want to pass the event object - contextWindow.onbeforeunload = function (e, b) { - callParentKarmaMethod('onbeforeunload', []) + + contextWindow.onbeforeunload = function () { + return self.error('Some of your tests did a full page reload!') } contextWindow.dump = function () { diff --git a/static/context.js b/static/context.js index 8cc67ebf6..417503165 100644 --- a/static/context.js +++ b/static/context.js @@ -214,9 +214,9 @@ function ContextKarma (callParentKarmaMethod) { contextWindow.onerror = function () { return self.error.apply(self, arguments) } - // DEV: We must defined a function since we don't want to pass the event object - contextWindow.onbeforeunload = function (e, b) { - callParentKarmaMethod('onbeforeunload', []) + + contextWindow.onbeforeunload = function () { + return self.error('Some of your tests did a full page reload!') } contextWindow.dump = function () { diff --git a/static/karma.js b/static/karma.js index 1c1111450..23b7c1ae2 100644 --- a/static/karma.js +++ b/static/karma.js @@ -15,7 +15,6 @@ var util = require('../common/util') function Karma (updater, socket, iframe, opener, navigator, location, document) { this.updater = updater var startEmitted = false - var karmaNavigating = false var self = this var queryParams = util.parseQueryParams(location.search) var browserId = queryParams.id || util.generateId('manual-') @@ -93,21 +92,21 @@ function Karma (updater, socket, iframe, opener, navigator, location, document) var childWindow = null function navigateContextTo (url) { - karmaNavigating = true if (self.config.useIframe === false) { // run in new window if (self.config.runInParent === false) { // If there is a window already open, then close it // DEV: In some environments (e.g. Electron), we don't have setter access for location if (childWindow !== null && childWindow.closed !== true) { + // The onbeforeunload listener was added by context to catch + // unexpected navigations while running tests. + childWindow.onbeforeunload = undefined childWindow.close() } childWindow = opener(url) - karmaNavigating = false // run context on parent element (client_with_context) // using window.__karma__.scriptUrls to get the html element strings and load them dynamically } else if (url !== 'about:blank') { - karmaNavigating = false var loadScript = function (idx) { if (idx < window.__karma__.scriptUrls.length) { var parser = new DOMParser() @@ -138,15 +137,10 @@ function Karma (updater, socket, iframe, opener, navigator, location, document) } // run in iframe } else { + // The onbeforeunload listener was added by the context to catch + // unexpected navigations while running tests. + iframe.contentWindow.onbeforeunload = undefined iframe.src = policy.createURL(url) - karmaNavigating = false - } - } - - this.onbeforeunload = function () { - if (!karmaNavigating) { - // TODO(vojta): show what test (with explanation about jasmine.UPDATE_INTERVAL) - self.error('Some of your tests did a full page reload!') } } diff --git a/test/client/karma.spec.js b/test/client/karma.spec.js index 036a4ecbd..3e7af73d8 100644 --- a/test/client/karma.spec.js +++ b/test/client/karma.spec.js @@ -22,7 +22,7 @@ describe('Karma', function () { } } socket = new MockSocket() - iframe = {} + iframe = { contentWindow: {} } windowNavigator = {} windowLocation = { search: '' } windowStub = sinon.stub().returns({}) From 7cdb43eebc330ba0bb05b09e6fdda81078b6560c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 1 Feb 2021 22:05:25 +0000 Subject: [PATCH 15/27] chore(release): 6.0.4 [skip ci] ## [6.0.4](https://github.com/karma-runner/karma/compare/v6.0.3...v6.0.4) (2021-02-01) ### Bug Fixes * **cli:** temporarily disable strict parameters validation ([#3641](https://github.com/karma-runner/karma/issues/3641)) ([9c755e0](https://github.com/karma-runner/karma/commit/9c755e0d61f1e8fb0fed1281fc8a331d5f1734be)), closes [#3625](https://github.com/karma-runner/karma/issues/3625) * **client:** fix a false positive page reload error in Safari ([#3643](https://github.com/karma-runner/karma/issues/3643)) ([2a57b23](https://github.com/karma-runner/karma/commit/2a57b230cd6b27e1a6e903ca6557c5a6b3e31bf6)) * ensure that Karma supports running tests on IE 11 ([#3642](https://github.com/karma-runner/karma/issues/3642)) ([dbd1943](https://github.com/karma-runner/karma/commit/dbd1943e6901c4cb86280db7663afde32f9ab86c)) --- CHANGELOG.md | 9 +++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f838372..d7b668de7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [6.0.4](https://github.com/karma-runner/karma/compare/v6.0.3...v6.0.4) (2021-02-01) + + +### Bug Fixes + +* **cli:** temporarily disable strict parameters validation ([#3641](https://github.com/karma-runner/karma/issues/3641)) ([9c755e0](https://github.com/karma-runner/karma/commit/9c755e0d61f1e8fb0fed1281fc8a331d5f1734be)), closes [#3625](https://github.com/karma-runner/karma/issues/3625) +* **client:** fix a false positive page reload error in Safari ([#3643](https://github.com/karma-runner/karma/issues/3643)) ([2a57b23](https://github.com/karma-runner/karma/commit/2a57b230cd6b27e1a6e903ca6557c5a6b3e31bf6)) +* ensure that Karma supports running tests on IE 11 ([#3642](https://github.com/karma-runner/karma/issues/3642)) ([dbd1943](https://github.com/karma-runner/karma/commit/dbd1943e6901c4cb86280db7663afde32f9ab86c)) + ## [6.0.3](https://github.com/karma-runner/karma/compare/v6.0.2...v6.0.3) (2021-01-27) diff --git a/package-lock.json b/package-lock.json index c05418b26..5bde7941d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "karma", - "version": "6.0.3", + "version": "6.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index dcd577fb8..9e5f9f4a1 100644 --- a/package.json +++ b/package.json @@ -484,7 +484,7 @@ "engines": { "node": ">= 10" }, - "version": "6.0.3", + "version": "6.0.4", "license": "MIT", "husky": { "hooks": { From a14a24ef81348b29daf70e0ea8406588fdd01f19 Mon Sep 17 00:00:00 2001 From: Yaroslav Admin Date: Tue, 2 Feb 2021 01:12:34 +0100 Subject: [PATCH 16/27] chore(test): adjust test to pass in Safari browser (#3645) In Safari stringified Proxy object has ProxyObject as a name, but in other browsers it does not. Enabled Safari tests on BrowserStack to prevent future regressions. --- test/client/karma.conf.js | 12 ++++++------ test/client/stringify.spec.js | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/client/karma.conf.js b/test/client/karma.conf.js index e700b64de..838e3b78d 100644 --- a/test/client/karma.conf.js +++ b/test/client/karma.conf.js @@ -15,12 +15,12 @@ const launchers = { os: 'Windows', os_version: '10' }, - // bs_safari: { - // base: 'BrowserStack', - // browser: 'Safari', - // os: 'OS X', - // os_version: 'Big Sur' - // }, + bs_safari: { + base: 'BrowserStack', + browser: 'Safari', + os: 'OS X', + os_version: 'Big Sur' + }, bs_ie: { base: 'BrowserStack', browser: 'IE', diff --git a/test/client/stringify.spec.js b/test/client/stringify.spec.js index 921ec3366..a3c744ca5 100644 --- a/test/client/stringify.spec.js +++ b/test/client/stringify.spec.js @@ -49,7 +49,9 @@ describe('stringify', function () { if (window.Proxy) { it('should serialize proxied functions', function () { var defProxy = new Proxy(function (d, e, f) { return 'whatever' }, {}) - assert.deepStrictEqual(stringify(defProxy), 'function () { ... }') + // In Safari stringified Proxy object has ProxyObject as a name, but + // in other browsers it does not. + assert.deepStrictEqual(/^function (ProxyObject)?\(\) { ... }$/.test(stringify(defProxy)), true) }) } From 9dba1e20af48d4885e1a1c6da8c08454acb0db9d Mon Sep 17 00:00:00 2001 From: Nick Petruzzelli Date: Tue, 2 Feb 2021 19:33:43 -0500 Subject: [PATCH 17/27] feat(config): improve `karma.config.parseConfig` error handling (#3635) --- lib/config.js | 31 ++++++++++++++++++++-------- lib/server.js | 10 ++++++++- test/unit/config.spec.js | 44 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/lib/config.js b/lib/config.js index 1ea49b85c..b08bbde7d 100644 --- a/lib/config.js +++ b/lib/config.js @@ -351,7 +351,26 @@ const CONFIG_SYNTAX_HELP = ' module.exports = function(config) {\n' + ' });\n' + ' };\n' -function parseConfig (configFilePath, cliOptions) { +function parseConfig (configFilePath, cliOptions, parseOptions) { + function fail () { + log.error(...arguments) + if (parseOptions && parseOptions.throwErrors === true) { + const errorMessage = Array.from(arguments).join(' ') + throw new Error(errorMessage) + } else { + const warningMessage = + 'The `parseConfig()` function historically called `process.exit(1)`' + + ' when it failed. This behavior is now deprecated and function will' + + ' throw an error in the next major release. To suppress this warning' + + ' pass `throwErrors: true` as a third argument to opt-in into the new' + + ' behavior and adjust your code to respond to the exception' + + ' accordingly.' + + ' Example: `parseConfig(path, cliOptions, { throwErrors: true })`' + log.warn(warningMessage) + process.exit(1) + } + } + let configModule if (configFilePath) { try { @@ -360,8 +379,6 @@ function parseConfig (configFilePath, cliOptions) { configModule = configModule.default } } catch (e) { - log.error('Error in config file!\n ' + e.stack || e) - const extension = path.extname(configFilePath) if (extension === '.coffee' && !COFFEE_SCRIPT_AVAILABLE) { log.error('You need to install CoffeeScript.\n npm install coffeescript --save-dev') @@ -370,11 +387,10 @@ function parseConfig (configFilePath, cliOptions) { } else if (extension === '.ts' && !TYPE_SCRIPT_AVAILABLE) { log.error('You need to install TypeScript.\n npm install typescript ts-node --save-dev') } - return process.exit(1) + return fail('Error in config file!\n ' + e.stack || e) } if (!helper.isFunction(configModule)) { - log.error('Config file must export a function!\n' + CONFIG_SYNTAX_HELP) - return process.exit(1) + return fail('Config file must export a function!\n' + CONFIG_SYNTAX_HELP) } } else { configModule = () => {} // if no config file path is passed, we define a dummy config module. @@ -395,8 +411,7 @@ function parseConfig (configFilePath, cliOptions) { try { configModule(config) } catch (e) { - log.error('Error in config file!\n', e) - return process.exit(1) + return fail('Error in config file!\n', e) } // merge the config from config file and cliOptions (precedence) diff --git a/lib/server.js b/lib/server.js index aa96b76ea..a6ae81dab 100644 --- a/lib/server.js +++ b/lib/server.js @@ -63,7 +63,15 @@ class Server extends KarmaEventEmitter { this.loadErrors = [] - const config = cfg.parseConfig(cliOptions.configFile, cliOptions) + let config + try { + config = cfg.parseConfig(cliOptions.configFile, cliOptions, { throwErrors: true }) + } catch (parseConfigError) { + // TODO: change how `done` falls back to exit in next major version + // SEE: https://github.com/karma-runner/karma/pull/3635#discussion_r565399378 + (done || process.exit)(1) + return + } this.log.debug('Final config', util.inspect(config, false, /** depth **/ null)) diff --git a/test/unit/config.spec.js b/test/unit/config.spec.js index 169c418af..d115a2b6e 100644 --- a/test/unit/config.spec.js +++ b/test/unit/config.spec.js @@ -46,6 +46,8 @@ describe('config', () => { '/conf/invalid.js': () => { throw new SyntaxError('Unexpected token =') }, + '/conf/export-not-function.js': 'not-a-function', + // '/conf/export-null.js': null, // Same as `/conf/not-exist.js`? '/conf/exclude.js': wrapCfg({ exclude: ['one.js', 'sub/two.js'] }), '/conf/absolute.js': wrapCfg({ files: ['http://some.com', 'https://more.org/file.js'] }), '/conf/both.js': wrapCfg({ files: ['one.js', 'two.js'], exclude: ['third.js'] }), @@ -57,6 +59,7 @@ describe('config', () => { m = loadFile(path.join(__dirname, '/../../lib/config.js'), mocks, { global: {}, process: mocks.process, + Error: Error, // Without this, chai's `.throw()` assertion won't correctly check against constructors. require (path) { if (mockConfigs[path]) { return mockConfigs[path] @@ -123,7 +126,20 @@ describe('config', () => { expect(mocks.process.exit).to.have.been.calledWith(1) }) - it('should throw and log error if invalid file', () => { + it('should log error and throw if file does not exist AND throwErrors is true', () => { + function parseConfig () { + e.parseConfig('/conf/not-exist.js', {}, { throwErrors: true }) + } + + expect(parseConfig).to.throw(Error, 'Error in config file!\n Error: Cannot find module \'/conf/not-exist.js\'') + expect(logSpy).to.have.been.called + const event = logSpy.lastCall.args + expect(event.toString().split('\n').slice(0, 2)).to.be.deep.equal( + ['Error in config file!', ' Error: Cannot find module \'/conf/not-exist.js\'']) + expect(mocks.process.exit).not.to.have.been.called + }) + + it('should log an error and exit if invalid file', () => { e.parseConfig('/conf/invalid.js', {}) expect(logSpy).to.have.been.called @@ -133,6 +149,32 @@ describe('config', () => { expect(mocks.process.exit).to.have.been.calledWith(1) }) + it('should log an error and throw if invalid file AND throwErrors is true', () => { + function parseConfig () { + e.parseConfig('/conf/invalid.js', {}, { throwErrors: true }) + } + + expect(parseConfig).to.throw(Error, 'Error in config file!\n SyntaxError: Unexpected token =') + expect(logSpy).to.have.been.called + const event = logSpy.lastCall.args + expect(event[0]).to.eql('Error in config file!\n') + expect(event[1].message).to.eql('Unexpected token =') + expect(mocks.process.exit).not.to.have.been.called + }) + + it('should log error and throw if file does not export a function AND throwErrors is true', () => { + function parseConfig () { + e.parseConfig('/conf/export-not-function.js', {}, { throwErrors: true }) + } + + expect(parseConfig).to.throw(Error, 'Config file must export a function!\n') + expect(logSpy).to.have.been.called + const event = logSpy.lastCall.args + expect(event.toString().split('\n').slice(0, 1)).to.be.deep.equal( + ['Config file must export a function!']) + expect(mocks.process.exit).not.to.have.been.called + }) + it('should override config with given cli options', () => { const config = e.parseConfig('/home/config4.js', { port: 456, autoWatch: false }) From 3cb26e353202c773438f422e41cd01856879b464 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 3 Feb 2021 01:29:39 +0000 Subject: [PATCH 18/27] chore(release): 6.1.0 [skip ci] # [6.1.0](https://github.com/karma-runner/karma/compare/v6.0.4...v6.1.0) (2021-02-03) ### Features * **config:** improve `karma.config.parseConfig` error handling ([#3635](https://github.com/karma-runner/karma/issues/3635)) ([9dba1e2](https://github.com/karma-runner/karma/commit/9dba1e20af48d4885e1a1c6da8c08454acb0db9d)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 2 +- package.json | 5 +++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7b668de7..a094bdc61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [6.1.0](https://github.com/karma-runner/karma/compare/v6.0.4...v6.1.0) (2021-02-03) + + +### Features + +* **config:** improve `karma.config.parseConfig` error handling ([#3635](https://github.com/karma-runner/karma/issues/3635)) ([9dba1e2](https://github.com/karma-runner/karma/commit/9dba1e20af48d4885e1a1c6da8c08454acb0db9d)) + ## [6.0.4](https://github.com/karma-runner/karma/compare/v6.0.3...v6.0.4) (2021-02-01) diff --git a/package-lock.json b/package-lock.json index 5bde7941d..fcebd843a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "karma", - "version": "6.0.4", + "version": "6.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 9e5f9f4a1..fc9e84e62 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,8 @@ "Karma Bot ", "Maksim Ryzhikov ", "ukasz Usarz ", - "Christian Budde Christensen ", "semantic-release-bot ", + "Christian Budde Christensen ", "Wesley Cho ", "taichi ", "Liam Newman ", @@ -292,6 +292,7 @@ "Nick Carter ", "Nick McCurdy ", "Nick Payne ", + "Nick Petruzzelli ", "Nick Williams ", "Nicolas Artman ", "Nicolas Ferrero ", @@ -484,7 +485,7 @@ "engines": { "node": ">= 10" }, - "version": "6.0.4", + "version": "6.1.0", "license": "MIT", "husky": { "hooks": { From 7ab86be25c334b07747632b0a6bdb1d650d881bc Mon Sep 17 00:00:00 2001 From: Chris Bottin Date: Wed, 3 Feb 2021 21:18:29 +0000 Subject: [PATCH 19/27] fix: report launcher process error when exit event is not emitted (#3647) Co-authored-by: Chris Bottin --- lib/launchers/process.js | 5 +++ test/unit/launchers/process.spec.js | 64 +++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/lib/launchers/process.js b/lib/launchers/process.js index 072c15b81..a7aa101ce 100644 --- a/lib/launchers/process.js +++ b/lib/launchers/process.js @@ -93,6 +93,7 @@ function ProcessLauncher (spawn, tempDir, timer, processKillTimeout) { } else { errorOutput += err.toString() } + self._onProcessExit(-1, null, errorOutput) }) self._process.stderr.on('data', function (errBuff) { @@ -101,6 +102,10 @@ function ProcessLauncher (spawn, tempDir, timer, processKillTimeout) { } this._onProcessExit = function (code, signal, errorOutput) { + if (!self._process) { + // Both exit and error events trigger _onProcessExit(), but we only need one cleanup. + return + } log.debug(`Process ${self.name} exited with code ${code} and signal ${signal}`) let error = null diff --git a/test/unit/launchers/process.spec.js b/test/unit/launchers/process.spec.js index 6dfc29b40..489d7ecdf 100644 --- a/test/unit/launchers/process.spec.js +++ b/test/unit/launchers/process.spec.js @@ -7,18 +7,25 @@ const CaptureTimeoutLauncher = require('../../../lib/launchers/capture_timeout') const ProcessLauncher = require('../../../lib/launchers/process') const EventEmitter = require('../../../lib/events').EventEmitter const createMockTimer = require('../mocks/timer') +const logger = require('../../../lib/logger') describe('launchers/process.js', () => { let emitter let mockSpawn let mockTempDir let launcher + let logErrorSpy + let logDebugSpy const BROWSER_PATH = path.normalize('/usr/bin/browser') beforeEach(() => { emitter = new EventEmitter() launcher = new BaseLauncher('fake-id', emitter) + launcher.name = 'fake-name' + launcher.ENV_CMD = 'fake-ENV-CMD' + logErrorSpy = sinon.spy(logger.create('launcher'), 'error') + logDebugSpy = sinon.spy(logger.create('launcher'), 'debug') mockSpawn = sinon.spy(function (cmd, args) { const process = new EventEmitter() @@ -74,7 +81,7 @@ describe('launchers/process.js', () => { }) describe('with RetryLauncher', () => { - it('should handle spawn ENOENT error and not even retry', (done) => { + function assertSpawnError ({ errorCode, emitExit, expectedError }, done) { ProcessLauncher.call(launcher, mockSpawn, mockTempDir) RetryLauncher.call(launcher, 2) launcher._getCommand = () => BROWSER_PATH @@ -83,35 +90,56 @@ describe('launchers/process.js', () => { emitter.on('browser_process_failure', failureSpy) launcher.start('http://host:9876/') - mockSpawn._processes[0].emit('error', { code: 'ENOENT' }) - mockSpawn._processes[0].emit('exit', 1) + mockSpawn._processes[0].emit('error', { code: errorCode }) + if (emitExit) { + mockSpawn._processes[0].emit('exit', 1) + } mockTempDir.remove.callArg(1) _.defer(() => { expect(launcher.state).to.equal(launcher.STATE_FINISHED) expect(failureSpy).to.have.been.called + expect(logDebugSpy).to.have.been.callCount(5) + expect(logDebugSpy.getCall(0)).to.have.been.calledWithExactly('null -> BEING_CAPTURED') + expect(logDebugSpy.getCall(1)).to.have.been.calledWithExactly(`${BROWSER_PATH} http://host:9876/?id=fake-id`) + expect(logDebugSpy.getCall(2)).to.have.been.calledWithExactly('Process fake-name exited with code -1 and signal null') + expect(logDebugSpy.getCall(3)).to.have.been.calledWithExactly('fake-name failed (cannot start). Not restarting.') + expect(logDebugSpy.getCall(4)).to.have.been.calledWithExactly('BEING_CAPTURED -> FINISHED') + expect(logErrorSpy).to.have.been.calledWith(expectedError) done() }) + } + + it('should handle spawn ENOENT error and not even retry', (done) => { + assertSpawnError({ + errorCode: 'ENOENT', + emitExit: true, + expectedError: `Cannot start fake-name\n\tCan not find the binary ${BROWSER_PATH}\n\tPlease set env variable fake-ENV-CMD` + }, done) }) it('should handle spawn EACCES error and not even retry', (done) => { - ProcessLauncher.call(launcher, mockSpawn, mockTempDir) - RetryLauncher.call(launcher, 2) - launcher._getCommand = () => BROWSER_PATH - - const failureSpy = sinon.spy() - emitter.on('browser_process_failure', failureSpy) + assertSpawnError({ + errorCode: 'EACCES', + emitExit: true, + expectedError: `Cannot start fake-name\n\tPermission denied accessing the binary ${BROWSER_PATH}\n\tMaybe it's a directory?` + }, done) + }) - launcher.start('http://host:9876/') - mockSpawn._processes[0].emit('error', { code: 'EACCES' }) - mockSpawn._processes[0].emit('exit', 1) - mockTempDir.remove.callArg(1) + it('should handle spawn ENOENT error and report the error when exit event is not emitted', (done) => { + assertSpawnError({ + errorCode: 'ENOENT', + emitExit: false, + expectedError: `Cannot start fake-name\n\tCan not find the binary ${BROWSER_PATH}\n\tPlease set env variable fake-ENV-CMD` + }, done) + }) - _.defer(() => { - expect(launcher.state).to.equal(launcher.STATE_FINISHED) - expect(failureSpy).to.have.been.called - done() - }) + it('should handle spawn EACCES error and report the error when exit event is not emitted', (done) => { + assertSpawnError({ + errorCode: 'EACCES', + emitExit: false, + expectedError: `Cannot start fake-name\n\tPermission denied accessing the binary ${BROWSER_PATH}\n\tMaybe it's a directory?` + }, done) }) }) From 474f4e1caff469cce87f19a11d9179e4e05552f9 Mon Sep 17 00:00:00 2001 From: xel23 Date: Fri, 12 Feb 2021 20:35:46 +0300 Subject: [PATCH 20/27] fix(config): check extension before ts-node register (#3651) Call require('ts-node').register() after checking configFilePath has `.ts` extension Fixes #3329 --- lib/config.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/config.js b/lib/config.js index b08bbde7d..30e874ee6 100644 --- a/lib/config.js +++ b/lib/config.js @@ -27,7 +27,7 @@ try { } catch (e) {} try { - require('ts-node').register() + require('ts-node') TYPE_SCRIPT_AVAILABLE = true } catch (e) {} @@ -374,6 +374,9 @@ function parseConfig (configFilePath, cliOptions, parseOptions) { let configModule if (configFilePath) { try { + if (path.extname(configFilePath) === '.ts' && TYPE_SCRIPT_AVAILABLE) { + require('ts-node').register() + } configModule = require(configFilePath) if (typeof configModule === 'object' && typeof configModule.default !== 'undefined') { configModule = configModule.default From 99908c3f5b5e48bc2e2158d090ca2e9d514fcb03 Mon Sep 17 00:00:00 2001 From: Yaroslav Admin Date: Fri, 12 Feb 2021 18:40:47 +0100 Subject: [PATCH 21/27] docs(plugins): add more information about plugins (#3649) Changes: - Promote `require('karma-plugin')` form over `'karma-plugin'` form. Former makes it more clear that plugin is imported from an NPM package and it is a regular JS object, there is no magic behind it. This is inspired by #3498 where user is not aware that it is even possible. This also should make it easier with plug'n'play package managers (like Yarn 2). - Explain that `plugins` array does not activate plugins, but only registers them to clarify https://github.com/karma-runner/karma/issues/1247#issuecomment-312267740. - Explain the plugin structure, DI and how to build a new plugin. - Re-arrange "Developing plugins" page to make it easier to add more information about every plugin type. Adding actual information should be done in the separate PRs though. Fixes #1247 --- docs/config/01-configuration-file.md | 10 +- docs/config/05-plugins.md | 50 +++++--- docs/dev/05-plugins.md | 184 ++++++++++++++++++++------- lib/config.js | 2 +- lib/plugin.js | 2 +- 5 files changed, 175 insertions(+), 73 deletions(-) diff --git a/docs/config/01-configuration-file.md b/docs/config/01-configuration-file.md index 24646e6c4..6d1c73f53 100644 --- a/docs/config/01-configuration-file.md +++ b/docs/config/01-configuration-file.md @@ -548,12 +548,9 @@ mime: { **Default:** `['karma-*']` -**Description:** List of plugins to load. A plugin can be a string (in which case it will be required by Karma) or an inlined plugin - Object. -By default, Karma loads all sibling NPM modules which have a name starting with `karma-*`. +**Description:** List of plugins to load. A plugin can be either a plugin object, or a string containing name of the module which exports a plugin object. See [plugins] for more information on how to install and use plugins. -Note: Just about all plugins in Karma require an additional library to be installed (via NPM). - -See [plugins] for more information. +By default, Karma loads plugins from all sibling NPM packages which have a name starting with `karma-*`. ## port @@ -587,8 +584,7 @@ If, after test execution or after Karma attempts to kill the browser, browser is Preprocessors can be loaded through [plugins]. -Note: Just about all preprocessors in Karma (other than CoffeeScript and some other defaults) -require an additional library to be installed (via NPM). +Note: Just about all preprocessors in Karma require an additional library to be installed (via NPM). Be aware that preprocessors may be transforming the files and file types that are available at run time. For instance, if you are using the "coverage" preprocessor on your source files, if you then attempt to interactively debug diff --git a/docs/config/05-plugins.md b/docs/config/05-plugins.md index 6272fc80f..4cf8b9203 100644 --- a/docs/config/05-plugins.md +++ b/docs/config/05-plugins.md @@ -1,11 +1,12 @@ -Karma can be easily extended through plugins. -In fact, all the existing preprocessors, reporters, browser launchers and frameworks are also plugins. +Karma can be easily extended through plugins. In fact, all the existing preprocessors, reporters, browser launchers and frameworks are plugins. -## Installation +You can install [existing plugins] from NPM or you can write [your own plugins][developing plugins] for Karma. -Karma plugins are NPM modules, so the recommended way to install them are as project dependencies in your `package.json`: +## Installing Plugins -```javascript +The recommended way to install plugins is to add them as project dependencies in your `package.json`: + +```json { "devDependencies": { "karma": "~0.10", @@ -22,26 +23,35 @@ Therefore, a simple way to install a plugin is: npm install karma- --save-dev ``` - ## Loading Plugins -By default, Karma loads all sibling NPM modules which have a name starting with `karma-*`. -You can also explicitly list plugins you want to load via the `plugins` configuration setting. The configuration value can either be -a string (module name), which will be required by Karma, or an object (inlined plugin). +By default, Karma loads plugins from all sibling NPM packages which have a name starting with `karma-*`. + +You can also override this behavior and explicitly list plugins you want to load via the `plugins` configuration setting: ```javascript -plugins: [ - // Karma will require() these plugins - 'karma-jasmine', - 'karma-chrome-launcher' - - // inlined plugins - {'framework:xyz': ['factory', factoryFn]}, - require('./plugin-required-from-config') -] +config.set({ + plugins: [ + // Load a plugin you installed from NPM. + require('karma-jasmine'), + + // Load a plugin from the file in your project. + require('./my-custom-plugin'), + + // Define a plugin inline. + { 'framework:xyz': ['factory', factoryFn] }, + + // Specify a module name or path which Karma will require() and load its + // default export as a plugin. + 'karma-chrome-launcher', + './my-fancy-plugin' + ] +}) ``` -There are already many [existing plugins]. Of course, you can write [your own plugins] too! +## Activating Plugins + +Adding a plugin to the `plugins` array only makes Karma aware of the plugin, but it does not activate it. Depending on the plugin type you'll need to add a plugin name into `frameworks`, `reporters`, `preprocessors`, `middleware` or `browsers` configuration key to activate it. For the detailed information refer to the corresponding plugin documentation or check out [Developing plugins][developing plugins] guide for more in-depth explanation of how plugins work. [existing plugins]: https://npmjs.org/browse/keyword/karma-plugin -[your own plugins]: ../dev/plugins.html +[developing plugins]: ../dev/plugins.html diff --git a/docs/dev/05-plugins.md b/docs/dev/05-plugins.md index d9371e46a..d7cae9e6d 100644 --- a/docs/dev/05-plugins.md +++ b/docs/dev/05-plugins.md @@ -1,61 +1,109 @@ pageTitle: Developing Plugins -Karma can be extended through plugins. A plugin is essentially an NPM module. Typically, there are four kinds of plugins: **frameworks**, **reporters**, **launchers** and **preprocessors**. The best way to understand how this works is to take a look at some of the existing plugins. Following sections list some of the plugins that you might use as a reference. +Karma can be extended through plugins. There are five kinds of plugins: *framework*, *reporter*, *launcher*, *preprocessor* and *middleware*. Each type allows to modify a certain aspect of the Karma behavior. -## Frameworks -- example plugins: [karma-jasmine], [karma-mocha], [karma-requirejs] -- use naming convention is `karma-*` -- use NPM keywords `karma-plugin`, `karma-framework`. +- A *framework* connects a testing framework (like Mocha) to a Karma API, so browser can send test results back to a Karma server. +- A *reporter* defines how test results are reported to a user. +- A *launcher* allows Karma to launch different browsers to run tests in. +- A *preprocessor* is responsible for transforming/transpiling source files before loading them into a browser. +- A *middleware* can be used to customise how files are served to a browser. -## Reporters -- example plugins: [karma-growl-reporter], [karma-junit-reporter], [karma-material-reporter] -- use naming convention is `karma-*-reporter` -- use NPM keywords `karma-plugin`, `karma-reporter` +## Dependency injection -## Launchers -- example plugins: [karma-chrome-launcher], [karma-sauce-launcher] -- use naming convention is `karma-*-launcher` -- use NPM keywords `karma-plugin`, `karma-launcher` +Karma is assembled using [*dependency injection*](https://en.wikipedia.org/wiki/Dependency_injection). It is important to understand this concept to be able to develop plugins. -## Preprocessors +On the very high level you can think of Karma as an object where each key (a *DI token*) is mapped to a certain Karma object (a *service*). For example, `config` DI token maps to `Config` instance, which holds current Karma configuration. Plugins can request (or *inject*) various Karma objects by specifying a corresponding DI token. Upon injection a plugin can interact with injected services to implement their functionality. -A preprocessor is a function that accepts three arguments (`content`, `file`, and `next`), mutates the content in some way, and passes it on to the next preprocessor. +There is no exhaustive list of all available services and their DI tokens, but you can discover them by reading Karma's or other plugins' source code. -- arguments passed to preprocessor plugins: - - **`content`** of the file being processed - - **`file`** object describing the file being processed - - **path:** the current file, mutable file path. e. g. `some/file.coffee` -> `some/file.coffee.js` _This path is mutable and may not actually exist._ - - **originalPath:** the original, unmutated path - - **encodings:** A mutable, keyed object where the keys are a valid encoding type ('gzip', 'compress', 'br', etc.) and the values are the encoded content. Encoded content should be stored here and not resolved using `next(null, encodedContent)` - - **type:** determines how to include a file, when serving - - **`next`** function to be called when preprocessing is complete, should be called as `next(null, processedContent)` or `next(error)` -- example plugins: [karma-coffee-preprocessor], [karma-ng-html2js-preprocessor] -- use naming convention is `karma-*-preprocessor` -- user NPM keywords `karma-plugin`, `karma-preprocessor` +## Plugin structure -## Crazier stuff -Karma is assembled by Dependency Injection and a plugin is just an additional DI module (see [node-di] for more), that can be loaded by Karma. Therefore, it can ask for pretty much any Karma component and interact with it. There are a couple of plugins that do more interesting stuff like this, check out [karma-closure], [karma-intellij]. +Each plugin is essentially a service with its associated DI token. When user [activates a plugin][plugins] in their config, Karma looks for a corresponding DI token and instantiates a service linked to this DI token. +To declare a plugin one should define a DI token for the plugin and explain Karma how to instantiate it. A DI token consists of two parts: a plugin type and plugin's unique name. The former defines what a plugin can do, requirements to the service's API and when it is instantiated. The latter is a unique name, which a plugin user will use to activate a plugin. -[karma-jasmine]: https://github.com/karma-runner/karma-jasmine -[karma-mocha]: https://github.com/karma-runner/karma-mocha +It is totally valid for a plugin to define multiple services. This can be done by adding more keys to the object exported by the plugin. Common example of this would be `framework` + `reporter` plugins, which usually come together. -[karma-requirejs]: https://github.com/karma-runner/karma-requirejs -[karma-growl-reporter]: https://github.com/karma-runner/karma-growl-reporter -[karma-junit-reporter]: https://github.com/karma-runner/karma-junit-reporter -[karma-chrome-launcher]: https://github.com/karma-runner/karma-chrome-launcher -[karma-sauce-launcher]: https://github.com/karma-runner/karma-sauce-launcher -[karma-coffee-preprocessor]: https://github.com/karma-runner/karma-coffee-preprocessor -[karma-ng-html2js-preprocessor]: https://github.com/karma-runner/karma-ng-html2js-preprocessor -[karma-closure]: https://github.com/karma-runner/karma-closure -[karma-intellij]: https://github.com/karma-runner/karma-intellij -[node-di]: https://github.com/vojtajina/node-di -[karma-material-reporter]: https://github.com/ameerthehacker/karma-material-reporter +Let's make a very simple plugin, which prints "Hello, world!" when instantiated. We'll use a `framework` type as it is instantiated early in the Karma lifecycle and does not have any requirements to its API. Let's call our plugin "hello", so its unique name will be `hello`. Joining these two parts we get a DI token for our plugin `framework:hello`. Let's declare it. + +```js +// hello-plugin.js + +// A factory function for our plugin, it will be called, when Karma needs to +// instantiate a plugin. Normally it should return an instance of the service +// conforming to the API requirements of the plugin type (more on that below), +// but for our simple example we don't need any service and just print +// a message when function is called. +function helloFrameworkFactory() { + console.log('Hello, world!') +} + +module.exports = { + // Declare the plugin, so Karma knows that it exists. + // 'factory' tells Karma that it should call `helloFrameworkFactory` + // function and use whatever it returns as a service for the DI token + // `framework:hello`. + 'framework:hello': ['factory', helloFrameworkFactory] +}; +``` + +```js +// karma.conf.js + +module.exports = (config) => { + config.set({ + plugins: [ + require('./hello-plugin') + ], + // Activate our plugin by specifying its unique name in the + // corresponding configuration key. + frameworks: ['hello'] + }) +} +``` + +## Injecting dependencies + +In "Dependency injection" section we discussed that it is possible to inject any Karma services into a plugin and interact with them. This can be done by setting an `$inject` property on the plugin's factory function to an array of DI tokens plugin wishes to interact with. Karma will pick up this property and pass requested services to the factory functions as parameters. + +Let's make the `hello` framework a bit more useful and make it add `hello.js` file to the `files` array. This way users of the plugin can, for example, access a function defined in `hello.js` from their tests. + +```js +// hello-plugin.js + +// Add parameters to the function to receive requested services. +function helloFrameworkFactory(config) { + config.files.unshift({ + pattern: __dirname + '/hello.js', + included: true, + served: true, + watched: false + }) +} + +// Declare DI tokens plugin wants to inject. +helloFrameworkFactory.$inject = ['config'] + +module.exports = { + 'framework:hello': ['factory', helloFrameworkFactory] +}; +``` + +The Karma config is unchanged and is omitted for brevity. See above example for the plugin usage. + +Note: Currently, Karma uses [node-di] library as a DI implementation. The library is more powerful than what's documented above, however, the DI implementation may change in the future, so we recommend not to rely on the node-di implementation details. + +## Plugin types -## Karma Framework API +This section outlines API requirements and conventions for different plugin types. There also links to some plugins, which you can use for inspiration. -Karma Framework connects existing testing libraries to Karma's API, so that their -results can be displayed in a browser and sent back to the server. +### Frameworks + +- example plugins: [karma-jasmine], [karma-mocha], [karma-requirejs] +- use naming convention is `karma-*` +- use NPM keywords `karma-plugin`, `karma-framework`. + +A framework connects existing testing libraries to Karma's API, so that their results can be displayed in a browser and sent back to the server. Karma frameworks _must_ implement a `window.__karma__.start` method that Karma will call to start test execution. This function is called with an object that has methods @@ -89,3 +137,51 @@ statuses. The method takes an object of the form: skipped: Boolean // skipped / ran } ``` + +### Reporters + +- example plugins: [karma-growl-reporter], [karma-junit-reporter], [karma-material-reporter] +- use naming convention is `karma-*-reporter` +- use NPM keywords `karma-plugin`, `karma-reporter` + +### Launchers + +- example plugins: [karma-chrome-launcher], [karma-sauce-launcher] +- use naming convention is `karma-*-launcher` +- use NPM keywords `karma-plugin`, `karma-launcher` + +### Preprocessors + +- example plugins: [karma-coffee-preprocessor], [karma-ng-html2js-preprocessor] +- use naming convention is `karma-*-preprocessor` +- user NPM keywords `karma-plugin`, `karma-preprocessor` + +A preprocessor is a function that accepts three arguments (`content`, `file`, and `next`), mutates the content in some way, and passes it on to the next preprocessor. + +- arguments passed to preprocessor plugins: + - **`content`** of the file being processed + - **`file`** object describing the file being processed + - **path:** the current file, mutable file path. e. g. `some/file.coffee` -> `some/file.coffee.js` _This path is mutable and may not actually exist._ + - **originalPath:** the original, unmutated path + - **encodings:** A mutable, keyed object where the keys are a valid encoding type ('gzip', 'compress', 'br', etc.) and the values are the encoded content. Encoded content should be stored here and not resolved using `next(null, encodedContent)` + - **type:** determines how to include a file, when serving + - **`next`** function to be called when preprocessing is complete, should be called as `next(null, processedContent)` or `next(error)` + +### Crazier stuff + +As Karma is assembled by dependency injection, a plugin can ask for pretty much any Karma component and interact with it. There are a couple of plugins that do more interesting stuff like this, check out [karma-closure], [karma-intellij]. + +[karma-jasmine]: https://github.com/karma-runner/karma-jasmine +[karma-mocha]: https://github.com/karma-runner/karma-mocha +[karma-requirejs]: https://github.com/karma-runner/karma-requirejs +[karma-growl-reporter]: https://github.com/karma-runner/karma-growl-reporter +[karma-junit-reporter]: https://github.com/karma-runner/karma-junit-reporter +[karma-chrome-launcher]: https://github.com/karma-runner/karma-chrome-launcher +[karma-sauce-launcher]: https://github.com/karma-runner/karma-sauce-launcher +[karma-coffee-preprocessor]: https://github.com/karma-runner/karma-coffee-preprocessor +[karma-ng-html2js-preprocessor]: https://github.com/karma-runner/karma-ng-html2js-preprocessor +[karma-closure]: https://github.com/karma-runner/karma-closure +[karma-intellij]: https://github.com/karma-runner/karma-intellij +[node-di]: https://github.com/vojtajina/node-di +[karma-material-reporter]: https://github.com/ameerthehacker/karma-material-reporter +[plugins]: ../config/plugins.html diff --git a/lib/config.js b/lib/config.js index 30e874ee6..1192afbac 100644 --- a/lib/config.js +++ b/lib/config.js @@ -226,7 +226,7 @@ function normalizeConfig (config, configFilePath) { ? [preprocessors[pattern]] : preprocessors[pattern] }) - // define custom launchers/preprocessors/reporters - create an inlined plugin + // define custom launchers/preprocessors/reporters - create a new plugin const module = Object.create(null) let hasSomeInlinedPlugin = false const types = ['launcher', 'preprocessor', 'reporter'] diff --git a/lib/plugin.js b/lib/plugin.js index 20a180597..53dfc68a2 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -39,7 +39,7 @@ function resolve (plugins, emitter) { .filter((pluginName) => !IGNORED_PACKAGES.includes(pluginName) && regexp.test(pluginName)) .forEach((pluginName) => requirePlugin(`${pluginDirectory}/${pluginName}`)) } else if (helper.isObject(plugin)) { - log.debug(`Loading inlined plugin (defining ${Object.keys(plugin).join(', ')}).`) + log.debug(`Loading inline plugin defining ${Object.keys(plugin).join(', ')}.`) modules.push(plugin) } else { log.error(`Invalid plugin ${plugin}`) From f52a07199f7f589ab2bee1c1f2eb0cd2c05a0ce2 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 12 Feb 2021 18:28:19 +0000 Subject: [PATCH 22/27] chore(release): 6.1.1 [skip ci] ## [6.1.1](https://github.com/karma-runner/karma/compare/v6.1.0...v6.1.1) (2021-02-12) ### Bug Fixes * **config:** check extension before ts-node register ([#3651](https://github.com/karma-runner/karma/issues/3651)) ([474f4e1](https://github.com/karma-runner/karma/commit/474f4e1caff469cce87f19a11d9179e4e05552f9)), closes [#3329](https://github.com/karma-runner/karma/issues/3329) * report launcher process error when exit event is not emitted ([#3647](https://github.com/karma-runner/karma/issues/3647)) ([7ab86be](https://github.com/karma-runner/karma/commit/7ab86be25c334b07747632b0a6bdb1d650d881bc)) --- CHANGELOG.md | 8 ++++++++ package-lock.json | 2 +- package.json | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a094bdc61..ea0f26066 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [6.1.1](https://github.com/karma-runner/karma/compare/v6.1.0...v6.1.1) (2021-02-12) + + +### Bug Fixes + +* **config:** check extension before ts-node register ([#3651](https://github.com/karma-runner/karma/issues/3651)) ([474f4e1](https://github.com/karma-runner/karma/commit/474f4e1caff469cce87f19a11d9179e4e05552f9)), closes [#3329](https://github.com/karma-runner/karma/issues/3329) +* report launcher process error when exit event is not emitted ([#3647](https://github.com/karma-runner/karma/issues/3647)) ([7ab86be](https://github.com/karma-runner/karma/commit/7ab86be25c334b07747632b0a6bdb1d650d881bc)) + # [6.1.0](https://github.com/karma-runner/karma/compare/v6.0.4...v6.1.0) (2021-02-03) diff --git a/package-lock.json b/package-lock.json index fcebd843a..7e44ef44e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "karma", - "version": "6.1.0", + "version": "6.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index fc9e84e62..93f23a5db 100644 --- a/package.json +++ b/package.json @@ -174,6 +174,7 @@ "Chang Wang ", "Chelsea Urquhart ", "Chris ", + "Chris Bottin ", "Chris Chua ", "Chris Dawson ", "Christian Weiss ", @@ -407,6 +408,7 @@ "thetrevdev ", "thorn0 ", "toran billups ", + "xel23 ", "chalkerx@gmail.com>", "weiran.zsd@outlook.com>" ], @@ -485,7 +487,7 @@ "engines": { "node": ">= 10" }, - "version": "6.1.0", + "version": "6.1.1", "license": "MIT", "husky": { "hooks": { From 5bfcf5f37de6f0a12abcf9914c2fad510395b4d6 Mon Sep 17 00:00:00 2001 From: Charles Suh Date: Mon, 8 Mar 2021 15:59:02 -0800 Subject: [PATCH 23/27] fix: patch karma to allow loading virtual packages (#3663) * fix: patch karma to allow loading virtual packages * fix: additional patch to handle SCRIPT_URL_ARRAY Co-authored-by: Shahriyar Nasir --- lib/middleware/karma.js | 6 ++-- test/unit/middleware/karma.spec.js | 45 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/lib/middleware/karma.js b/lib/middleware/karma.js index 3a34b4e58..37e9a46c8 100644 --- a/lib/middleware/karma.js +++ b/lib/middleware/karma.js @@ -222,10 +222,10 @@ function createKarmaMiddleware ( }) : [] return data - .replace('%SCRIPTS%', scriptTags.join('\n')) + .replace('%SCRIPTS%', () => scriptTags.join('\n')) .replace('%CLIENT_CONFIG%', 'window.__karma__.config = ' + JSON.stringify(client) + ';\n') - .replace('%SCRIPT_URL_ARRAY%', 'window.__karma__.scriptUrls = ' + JSON.stringify(scriptUrls) + ';\n') - .replace('%MAPPINGS%', 'window.__karma__.files = {\n' + mappings.join(',\n') + '\n};\n') + .replace('%SCRIPT_URL_ARRAY%', () => 'window.__karma__.scriptUrls = ' + JSON.stringify(scriptUrls) + ';\n') + .replace('%MAPPINGS%', () => 'window.__karma__.files = {\n' + mappings.join(',\n') + '\n};\n') .replace('\n%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url)) }) }) diff --git a/test/unit/middleware/karma.spec.js b/test/unit/middleware/karma.spec.js index 8a968262c..b6a0d5d59 100644 --- a/test/unit/middleware/karma.spec.js +++ b/test/unit/middleware/karma.spec.js @@ -28,6 +28,7 @@ describe('middleware.karma', () => { karma: { static: { 'client.html': mocks.fs.file(0, 'CLIENT HTML\n%X_UA_COMPATIBLE%%X_UA_COMPATIBLE_URL%'), + 'client_with_context.html': mocks.fs.file(0, 'CLIENT_WITH_CONTEXT\n%SCRIPT_URL_ARRAY%'), 'context.html': mocks.fs.file(0, 'CONTEXT\n%SCRIPTS%'), 'debug.html': mocks.fs.file(0, 'DEBUG\n%SCRIPTS%\n%X_UA_COMPATIBLE%'), 'karma.js': mocks.fs.file(0, 'root: %KARMA_URL_ROOT%, proxy: %KARMA_PROXY_PATH%, v: %KARMA_VERSION%') @@ -214,6 +215,21 @@ describe('middleware.karma', () => { callHandlerWith('/__karma__/context.html') }) + it('should serve context.html without using special patterns when replacing script tags', (done) => { + includedFiles([ + new MockFile('/.yarn/$$virtual/first.js', 'sha123'), + new MockFile('/.yarn/$$virtual/second.dart', 'sha456') + ]) + + response.once('end', () => { + expect(nextSpy).not.to.have.been.called + expect(response).to.beServedAs(200, 'CONTEXT\n\n') + done() + }) + + callHandlerWith('/__karma__/context.html') + }) + it('should serve context.html with replaced link tags', (done) => { includedFiles([ new MockFile('/first.css', 'sha007'), @@ -373,6 +389,20 @@ describe('middleware.karma', () => { callHandlerWith('/__karma__/context.html') }) + it('should inline mappings without using special patterns', (done) => { + fsMock._touchFile('/karma/static/context.html', 0, '%MAPPINGS%') + servedFiles([ + new MockFile('/.yarn/$$virtual/abc/a.js', 'sha_a') + ]) + + response.once('end', () => { + expect(response).to.beServedAs(200, "window.__karma__.files = {\n '/__proxy__/__karma__/absolute/.yarn/$$virtual/abc/a.js': 'sha_a'\n};\n") + done() + }) + + callHandlerWith('/__karma__/context.html') + }) + it('should escape quotes in mappings with all served files', (done) => { fsMock._touchFile('/karma/static/context.html', 0, '%MAPPINGS%') servedFiles([ @@ -490,4 +520,19 @@ describe('middleware.karma', () => { callHandlerWith('/__karma__/debug.html') }) + + it('should serve client_with_context.html without using special patterns when replacing script urls', (done) => { + includedFiles([ + new MockFile('/.yarn/$$virtual/first.js', 'sha123'), + new MockFile('/.yarn/$$virtual/second.dart', 'sha456') + ]) + + response.once('end', () => { + expect(nextSpy).not.to.have.been.called + expect(response).to.beServedAs(200, 'CLIENT_WITH_CONTEXT\nwindow.__karma__.scriptUrls = ["\\\\x3Cscript type=\\"text/javascript\\" src=\\"/__proxy__/__karma__/absolute/.yarn/$$virtual/first.js\\" crossorigin=\\"anonymous\\"\\\\x3E\\\\x3C/script\\\\x3E","\\\\x3Cscript type=\\"text/javascript\\" src=\\"/__proxy__/__karma__/absolute/.yarn/$$virtual/second.dart\\" crossorigin=\\"anonymous\\"\\\\x3E\\\\x3C/script\\\\x3E"];\n') + done() + }) + + callHandlerWith('/__karma__/client_with_context.html') + }) }) From 3fc6fdadd6b0ed6838de048c15485b1bd815fe23 Mon Sep 17 00:00:00 2001 From: Chris Bottin Date: Tue, 9 Mar 2021 00:06:33 +0000 Subject: [PATCH 24/27] fix(commitlint): skip task on master (#3650) --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7c1e548d8..e6bee9aca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,9 @@ jobs: - node_js: "14" script: - npm run init - - commitlint-travis + - if [[ "$TRAVIS_BRANCH" != "master" ]]; then + commitlint-travis; + fi; - npm run lint - npm run build:check - npm run test:unit From 10afab16bfc10b5199bb97207413c0045de442c1 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 9 Mar 2021 00:16:48 +0000 Subject: [PATCH 25/27] chore(release): 6.1.2 [skip ci] ## [6.1.2](https://github.com/karma-runner/karma/compare/v6.1.1...v6.1.2) (2021-03-09) ### Bug Fixes * **commitlint:** skip task on master ([#3650](https://github.com/karma-runner/karma/issues/3650)) ([3fc6fda](https://github.com/karma-runner/karma/commit/3fc6fdadd6b0ed6838de048c15485b1bd815fe23)) * patch karma to allow loading virtual packages ([#3663](https://github.com/karma-runner/karma/issues/3663)) ([5bfcf5f](https://github.com/karma-runner/karma/commit/5bfcf5f37de6f0a12abcf9914c2fad510395b4d6)) --- CHANGELOG.md | 8 ++++++++ package-lock.json | 2 +- package.json | 5 +++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea0f26066..e614239ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [6.1.2](https://github.com/karma-runner/karma/compare/v6.1.1...v6.1.2) (2021-03-09) + + +### Bug Fixes + +* **commitlint:** skip task on master ([#3650](https://github.com/karma-runner/karma/issues/3650)) ([3fc6fda](https://github.com/karma-runner/karma/commit/3fc6fdadd6b0ed6838de048c15485b1bd815fe23)) +* patch karma to allow loading virtual packages ([#3663](https://github.com/karma-runner/karma/issues/3663)) ([5bfcf5f](https://github.com/karma-runner/karma/commit/5bfcf5f37de6f0a12abcf9914c2fad510395b4d6)) + ## [6.1.1](https://github.com/karma-runner/karma/compare/v6.1.0...v6.1.1) (2021-02-12) diff --git a/package-lock.json b/package-lock.json index 7e44ef44e..9bcac86e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "karma", - "version": "6.1.1", + "version": "6.1.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 93f23a5db..95562fd7e 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "Bryan Smith ", "Bulat Shakirzyanov ", "ChangZhuo Chen (陳昌倬) ", + "Chris Bottin ", "Cyrus Chan ", "DarthCharles ", "David Herges ", @@ -172,9 +173,9 @@ "Carl Goldberg ", "Chad Smith ", "Chang Wang ", + "Charles Suh ", "Chelsea Urquhart ", "Chris ", - "Chris Bottin ", "Chris Chua ", "Chris Dawson ", "Christian Weiss ", @@ -487,7 +488,7 @@ "engines": { "node": ">= 10" }, - "version": "6.1.1", + "version": "6.1.2", "license": "MIT", "husky": { "hooks": { From 39831b1c2f9cbeebdba94c73ce353efb7c44e802 Mon Sep 17 00:00:00 2001 From: hdmr14 <58992133+hdmr14@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:26:24 +0900 Subject: [PATCH 26/27] feat(plugins): add support wildcard config for scoped package plugin (#3659) * feat(plugins): add support wildcard config for scoped package plugin * fix(plugins): support Node 10 --- lib/plugin.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/plugin.js b/lib/plugin.js index 53dfc68a2..8805a288d 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -32,12 +32,21 @@ function resolve (plugins, emitter) { return } const pluginDirectory = path.normalize(path.join(__dirname, '/../..')) - const regexp = new RegExp(`^${plugin.replace('*', '.*')}`) + const regexp = new RegExp(`^${plugin.replace(/\*/g, '.*').replace(/\//g, '[/\\\\]')}`) log.debug(`Loading ${plugin} from ${pluginDirectory}`) fs.readdirSync(pluginDirectory) - .filter((pluginName) => !IGNORED_PACKAGES.includes(pluginName) && regexp.test(pluginName)) - .forEach((pluginName) => requirePlugin(`${pluginDirectory}/${pluginName}`)) + .map((e) => { + const modulePath = path.join(pluginDirectory, e) + if (e[0] === '@') { + return fs.readdirSync(modulePath).map((e) => path.join(modulePath, e)) + } + return modulePath + }) + .reduce((a, x) => a.concat(x), []) + .map((modulePath) => path.relative(pluginDirectory, modulePath)) + .filter((moduleName) => !IGNORED_PACKAGES.includes(moduleName) && regexp.test(moduleName)) + .forEach((pluginName) => requirePlugin(path.join(pluginDirectory, pluginName))) } else if (helper.isObject(plugin)) { log.debug(`Loading inline plugin defining ${Object.keys(plugin).join(', ')}.`) modules.push(plugin) From a2bca0df0b9ca23ce8620eee1fc3935515ab8840 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 10 Mar 2021 23:35:19 +0000 Subject: [PATCH 27/27] chore(release): 6.2.0 [skip ci] # [6.2.0](https://github.com/karma-runner/karma/compare/v6.1.2...v6.2.0) (2021-03-10) ### Features * **plugins:** add support wildcard config for scoped package plugin ([#3659](https://github.com/karma-runner/karma/issues/3659)) ([39831b1](https://github.com/karma-runner/karma/commit/39831b1c2f9cbeebdba94c73ce353efb7c44e802)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 2 +- package.json | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e614239ee..3fff01afc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [6.2.0](https://github.com/karma-runner/karma/compare/v6.1.2...v6.2.0) (2021-03-10) + + +### Features + +* **plugins:** add support wildcard config for scoped package plugin ([#3659](https://github.com/karma-runner/karma/issues/3659)) ([39831b1](https://github.com/karma-runner/karma/commit/39831b1c2f9cbeebdba94c73ce353efb7c44e802)) + ## [6.1.2](https://github.com/karma-runner/karma/compare/v6.1.1...v6.1.2) (2021-03-09) diff --git a/package-lock.json b/package-lock.json index 9bcac86e7..ab7b36418 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "karma", - "version": "6.1.2", + "version": "6.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 95562fd7e..5c2110653 100644 --- a/package.json +++ b/package.json @@ -393,6 +393,7 @@ "deepak1556 ", "dorey ", "grifball ", + "hdmr14 <58992133+hdmr14@users.noreply.github.com>", "hrgdavor ", "ianjobling ", "inf3rno ", @@ -488,7 +489,7 @@ "engines": { "node": ">= 10" }, - "version": "6.1.2", + "version": "6.2.0", "license": "MIT", "husky": { "hooks": {