diff --git a/.eslintignore b/.eslintignore
index 32bb882d2..93626b9ee 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1 +1,5 @@
-test/e2e/support/error/under-test.js
\ No newline at end of file
+test/e2e/support/error/under-test.js
+test/unit/fixtures/bundled.js
+static/karma.js
+static/context.js
+tmp/*
diff --git a/.eslintrc b/.eslintrc
index e3578aadf..48e539e81 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,3 +1,7 @@
{
- "extends": "standard"
+ "extends": "standard",
+ "rules": {
+ "arrow-parens": [2, "always"],
+ "space-before-function-paren": ["error", "always"]
+ }
}
diff --git a/.gitignore b/.gitignore
index 10ed8f9d7..52a47db45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,5 @@ test/e2e/coverageRequirejs/coverage
test/e2e/coffee-coverage/coverage
test-results.xml
test/unit/test.log
+test/unit/fixtures/bundled.js
.DS_Store
diff --git a/.travis.yml b/.travis.yml
index 95c0c269f..7dfc3ae9a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,7 @@ node_js:
- 6
- 8
- 9
+ - 10
env:
global:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0ceb27b08..d73cc749b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,312 @@
+
+## [2.0.3](https://github.com/karma-runner/karma/compare/v0.12.16...v2.0.3) (2018-06-15)
+
+
+### Bug Fixes
+
+* **BaseReporter:** log message correctly with just one browser ([#3045](https://github.com/karma-runner/karma/issues/3045)) ([c1eb236](https://github.com/karma-runner/karma/commit/c1eb236))
+* **browser:** don't add already active socket again on reconnect ([37a7958](https://github.com/karma-runner/karma/commit/37a7958))
+* **browser:** filter browser logging by level ([35965d9](https://github.com/karma-runner/karma/commit/35965d9)), closes [#2228](https://github.com/karma-runner/karma/issues/2228)
+* **browser:** nicer "disconnect" - no more "Disconnectedundefined" ([a987d63](https://github.com/karma-runner/karma/commit/a987d63))
+* **build:** pin npm version in appveyor to v3, compat with node 4 ([#2983](https://github.com/karma-runner/karma/issues/2983)) ([bc1453e](https://github.com/karma-runner/karma/commit/bc1453e))
+* **ci:** Repaired AppVeyor for Node.js@0.12 ([cbfd98c](https://github.com/karma-runner/karma/commit/cbfd98c))
+* **cli:** override if an arg is defined multiple times ([31eb2c2](https://github.com/karma-runner/karma/commit/31eb2c2)), closes [#1192](https://github.com/karma-runner/karma/issues/1192)
+* **cli:** print UserAgent string verbatim if from an unknown browser ([9d97226](https://github.com/karma-runner/karma/commit/9d97226))
+* **cli:** restore shell completion in the npm package ([f56b5a5](https://github.com/karma-runner/karma/commit/f56b5a5)), closes [#2351](https://github.com/karma-runner/karma/issues/2351)
+* **cli:** Use `bin` field in package.json ([6823926](https://github.com/karma-runner/karma/commit/6823926)), closes [#1351](https://github.com/karma-runner/karma/issues/1351)
+* **client:** add ES5 shim ([14c30b7](https://github.com/karma-runner/karma/commit/14c30b7)), closes [#1529](https://github.com/karma-runner/karma/issues/1529)
+* **client:** add proxy support to stringify ([be10116](https://github.com/karma-runner/karma/commit/be10116))
+* **client:** does not throws an error for non DOM object that has `tagName` property ([ba55afb](https://github.com/karma-runner/karma/commit/ba55afb)), closes [#2139](https://github.com/karma-runner/karma/issues/2139)
+* **client:** don't crash if receive array-like results ([e095411](https://github.com/karma-runner/karma/commit/e095411)), closes [#2061](https://github.com/karma-runner/karma/issues/2061)
+* **client:** dynamic protocol for socket.io ([c986eef](https://github.com/karma-runner/karma/commit/c986eef)), closes [#1400](https://github.com/karma-runner/karma/issues/1400)
+* **client:** Fix stringify serializing objects ([0d0972a](https://github.com/karma-runner/karma/commit/0d0972a))
+* **client:** Revert back to old reloading detection ([f1c22d6](https://github.com/karma-runner/karma/commit/f1c22d6)), closes [#1656](https://github.com/karma-runner/karma/issues/1656)
+* **client:** serialise DOM objects ([1f73be4](https://github.com/karma-runner/karma/commit/1f73be4)), closes [#1106](https://github.com/karma-runner/karma/issues/1106)
+* **client:** Update location detection for socket.io ([7a23fa5](https://github.com/karma-runner/karma/commit/7a23fa5))
+* **client:** Use supported shim path. ([184f12e](https://github.com/karma-runner/karma/commit/184f12e))
+* **client:** Wait for childwindow to load ([c1bb15a](https://github.com/karma-runner/karma/commit/c1bb15a))
+* **client:** Wait for iframe to be loaded ([1631474](https://github.com/karma-runner/karma/commit/1631474)), closes [#1652](https://github.com/karma-runner/karma/issues/1652)
+* **client.html:** always open debug.html in a new browser process ([d176bcf](https://github.com/karma-runner/karma/commit/d176bcf))
+* **common:** fix AppVeyor build ([6c5e7d0](https://github.com/karma-runner/karma/commit/6c5e7d0))
+* **common:** more detailed info about error ([424aacc](https://github.com/karma-runner/karma/commit/424aacc))
+* **common:** Proxy function toString does not contain Proxy. ([4fb3484](https://github.com/karma-runner/karma/commit/4fb3484))
+* **common:** stringify error on 'Cannot convert a Symbol value to a string' ([#2990](https://github.com/karma-runner/karma/issues/2990)) ([65b658a](https://github.com/karma-runner/karma/commit/65b658a)), closes [#2856](https://github.com/karma-runner/karma/issues/2856)
+* **config:** [#1113](https://github.com/karma-runner/karma/issues/1113) Watching is not working properly on linux ([c91ffbc](https://github.com/karma-runner/karma/commit/c91ffbc))
+* **config:** add crossOriginAttribute config option ([1e465b1](https://github.com/karma-runner/karma/commit/1e465b1))
+* **config:** Call debug log methods after setting the loglevel based upon config/cli-options. ([a340dae](https://github.com/karma-runner/karma/commit/a340dae))
+* **config:** Call debug log methods after setting the loglevel based upon config/cli-options. ([99fd3f0](https://github.com/karma-runner/karma/commit/99fd3f0))
+* **config:** corrects spelling in example config template ([9fafc60](https://github.com/karma-runner/karma/commit/9fafc60))
+* **config:** Default remaining client options if any are set ([632dd5e](https://github.com/karma-runner/karma/commit/632dd5e)), closes [#961](https://github.com/karma-runner/karma/issues/961)
+* **config:** Error when browers option isn't array ([b695460](https://github.com/karma-runner/karma/commit/b695460))
+* **config:** Log the final config just before use. ([#3041](https://github.com/karma-runner/karma/issues/3041)) ([05dd09a](https://github.com/karma-runner/karma/commit/05dd09a))
+* **config:** Retry install with appveyor-retry. ([17d5791](https://github.com/karma-runner/karma/commit/17d5791))
+* **config:** Workaround npm 5.4 windows bug ([ec47d81](https://github.com/karma-runner/karma/commit/ec47d81))
+* **context:** Updated postMessage listener to stop validating non-Karma messages ([306e565](https://github.com/karma-runner/karma/commit/306e565))
+* **debug-runner:** support asynchronous tests in the debug runner ([a36f3eb](https://github.com/karma-runner/karma/commit/a36f3eb)), closes [#2811](https://github.com/karma-runner/karma/issues/2811)
+* **deps:** freeze socket.io version ([73e300d](https://github.com/karma-runner/karma/commit/73e300d))
+* **deps:** Update dependencies ([b9a4ce9](https://github.com/karma-runner/karma/commit/b9a4ce9)), closes [#1410](https://github.com/karma-runner/karma/issues/1410)
+* **deps:** Update log4js in package.json ([#2996](https://github.com/karma-runner/karma/issues/2996)) ([667b47e](https://github.com/karma-runner/karma/commit/667b47e))
+* **deps:** update socket.io to version 2.0.3. ([3b7b019](https://github.com/karma-runner/karma/commit/3b7b019)), closes [#2821](https://github.com/karma-runner/karma/issues/2821) [#2777](https://github.com/karma-runner/karma/issues/2777)
+* **deps:** Upgrade connect 3. ([b490985](https://github.com/karma-runner/karma/commit/b490985)), closes [#1410](https://github.com/karma-runner/karma/issues/1410)
+* **docs:** fix stopper.stop wrong variable name. closes [#2244](https://github.com/karma-runner/karma/issues/2244) ([0745a00](https://github.com/karma-runner/karma/commit/0745a00))
+* **docs:** Remove mention of pre 1.0.0 version ([#3010](https://github.com/karma-runner/karma/issues/3010)) ([6847ca0](https://github.com/karma-runner/karma/commit/6847ca0))
+* **eslint:** Fix formatting for the new ESLint 1.8.0 ([dc1bbab](https://github.com/karma-runner/karma/commit/dc1bbab))
+* **executor:** ensure run_complete is emitted last ([9c894f9](https://github.com/karma-runner/karma/commit/9c894f9)), closes [#2210](https://github.com/karma-runner/karma/issues/2210)
+* **file_list:** follow symlinks ([ee26748](https://github.com/karma-runner/karma/commit/ee26748))
+* **file_list:** Incorrect response after remove and add file ([0dbc020](https://github.com/karma-runner/karma/commit/0dbc020))
+* **file-list:** always use file from first matcher ([74bfdf3](https://github.com/karma-runner/karma/commit/74bfdf3))
+* **file-list:** Ensure autowatchDelay is working ([0f33268](https://github.com/karma-runner/karma/commit/0f33268)), closes [#1520](https://github.com/karma-runner/karma/issues/1520)
+* **file-list:** Ensure autowatchDelay is working. ([655599a](https://github.com/karma-runner/karma/commit/655599a)), closes [#1520](https://github.com/karma-runner/karma/issues/1520)
+* **file-list:** Ensure files are sorted and unique ([9dc5f8b](https://github.com/karma-runner/karma/commit/9dc5f8b)), closes [#1498](https://github.com/karma-runner/karma/issues/1498) [#1499](https://github.com/karma-runner/karma/issues/1499)
+* **file-list:** ensure patterns are comparable ([4d1bf3e](https://github.com/karma-runner/karma/commit/4d1bf3e)), closes [#2194](https://github.com/karma-runner/karma/issues/2194)
+* **file-list:** Normalize glob patterns ([fb841a7](https://github.com/karma-runner/karma/commit/fb841a7)), closes [#1494](https://github.com/karma-runner/karma/issues/1494)
+* **file-list:** refresh resolves before 'file_list_modified' event ([65f1eca](https://github.com/karma-runner/karma/commit/65f1eca)), closes [#1550](https://github.com/karma-runner/karma/issues/1550)
+* **file-list:** Stop polluting global environment with core-js ([0988022](https://github.com/karma-runner/karma/commit/0988022))
+* **file-list:** Use correct find function ([4cfaae9](https://github.com/karma-runner/karma/commit/4cfaae9))
+* **file-list:** use lodash find() ([3bd15a7](https://github.com/karma-runner/karma/commit/3bd15a7)), closes [#1533](https://github.com/karma-runner/karma/issues/1533)
+* **file-list:** Use modified throttle instead of debounce ([cb2aafb](https://github.com/karma-runner/karma/commit/cb2aafb)), closes [#1545](https://github.com/karma-runner/karma/issues/1545)
+* **files:** Ignore included:false pattern ([db42a7f](https://github.com/karma-runner/karma/commit/db42a7f)), closes [#1530](https://github.com/karma-runner/karma/issues/1530)
+* **flaky-test:** Add time to beforeEach() to allow plugins to load on first pass. ([#3025](https://github.com/karma-runner/karma/issues/3025)) ([31d9a08](https://github.com/karma-runner/karma/commit/31d9a08))
+* **helper:** Ensure browser detection is handled in the unkown case ([9328f67](https://github.com/karma-runner/karma/commit/9328f67))
+* **helper:** Patched replaceWinPath from choking on `null` values ([caa4d21](https://github.com/karma-runner/karma/commit/caa4d21))
+* **init:** fix test-main.(js/coffee) generation ([d8521ef](https://github.com/karma-runner/karma/commit/d8521ef)), closes [#1120](https://github.com/karma-runner/karma/issues/1120) [#896](https://github.com/karma-runner/karma/issues/896)
+* **init:** Make the requirejs config template normalize paths ([54dcce3](https://github.com/karma-runner/karma/commit/54dcce3)), closes [/github.com/karma-runner/karma/issues/513#issuecomment-48616784](https://github.com//github.com/karma-runner/karma/issues/513/issues/issuecomment-48616784)
+* **karma:** Escape quotes for file names. This fixes issue [#1876](https://github.com/karma-runner/karma/issues/1876). ([9dff3f3](https://github.com/karma-runner/karma/commit/9dff3f3))
+* **launcher:** Allow dynamic browser launches ([2b7d703](https://github.com/karma-runner/karma/commit/2b7d703))
+* **launcher:** Continue with exit when SIGKILL fails ([1eaccb4](https://github.com/karma-runner/karma/commit/1eaccb4))
+* **launcher:** exclude concurrent browser on launcher restart ([96f8f14](https://github.com/karma-runner/karma/commit/96f8f14)), closes [#2280](https://github.com/karma-runner/karma/issues/2280)
+* **launcher:** send sigkill on timeout when force killing ([c615c1f](https://github.com/karma-runner/karma/commit/c615c1f))
+* **launchers:** Listen to the correct error event. ([45a6922](https://github.com/karma-runner/karma/commit/45a6922))
+* **lint:** exempt built files ([#3024](https://github.com/karma-runner/karma/issues/3024)) ([bc9acd3](https://github.com/karma-runner/karma/commit/bc9acd3))
+* **logging:** Summarize SKIPPED tests in debug.html. ([a01100f](https://github.com/karma-runner/karma/commit/a01100f)), closes [#1111](https://github.com/karma-runner/karma/issues/1111)
+* **logging:** Upgrade to log4js 2.x API. ([#2868](https://github.com/karma-runner/karma/issues/2868)) ([f6f8707](https://github.com/karma-runner/karma/commit/f6f8707)), closes [#2858](https://github.com/karma-runner/karma/issues/2858)
+* **middleware:** Actually serve the favicon. ([f12db63](https://github.com/karma-runner/karma/commit/f12db63))
+* **middleware:** add file type to absolute urls ([bd1f799](https://github.com/karma-runner/karma/commit/bd1f799))
+* **middleware:** avoid using deprecated Buffer API ([018e6be](https://github.com/karma-runner/karma/commit/018e6be)), closes [/nodejs.org/api/deprecations.html#deprecations_dep0005](https://github.com//nodejs.org/api/deprecations.html/issues/deprecations_dep0005)
+* **middleware:** change to use vanilla for loop ([ac62cc0](https://github.com/karma-runner/karma/commit/ac62cc0)), closes [#2671](https://github.com/karma-runner/karma/issues/2671)
+* **middleware:** Correct spelling of middleware logger name ([9e9e7e6](https://github.com/karma-runner/karma/commit/9e9e7e6))
+* **middleware:** does not work with mootools ([#2591](https://github.com/karma-runner/karma/issues/2591)) ([2685e13](https://github.com/karma-runner/karma/commit/2685e13))
+* **middleware:** ensure Range headers adhere more closely to RFC 2616 ([8b1b4b1](https://github.com/karma-runner/karma/commit/8b1b4b1)), closes [#2310](https://github.com/karma-runner/karma/issues/2310)
+* **middleware:** fix WARN log when passing undefined error handler to promise.then ([20b87de](https://github.com/karma-runner/karma/commit/20b87de)), closes [#2227](https://github.com/karma-runner/karma/issues/2227)
+* **middleware:** Inject `config.urlRoot`. ([569ca0e](https://github.com/karma-runner/karma/commit/569ca0e)), closes [#1516](https://github.com/karma-runner/karma/issues/1516)
+* **middleware:** update `Buffer` usage ([3d94b8c](https://github.com/karma-runner/karma/commit/3d94b8c))
+* **package.json:** sinon-chai 2.13 is not compatible with sinon 4.x ([#2977](https://github.com/karma-runner/karma/issues/2977)) ([e095b05](https://github.com/karma-runner/karma/commit/e095b05))
+* **preprocessor:** Better handling of failing preprocessors ([a2376b8](https://github.com/karma-runner/karma/commit/a2376b8)), closes [#1521](https://github.com/karma-runner/karma/issues/1521)
+* **preprocessor:** calculate sha1 on content returned from a preprocessor ([6cf7955](https://github.com/karma-runner/karma/commit/6cf7955)), closes [#1204](https://github.com/karma-runner/karma/issues/1204)
+* **preprocessor:** Directory names with dots ([4b5e094](https://github.com/karma-runner/karma/commit/4b5e094))
+* **preprocessor:** Improve handling of failed preprocessors ([e726d1c](https://github.com/karma-runner/karma/commit/e726d1c)), closes [#1521](https://github.com/karma-runner/karma/issues/1521)
+* **preprocessor:** Lookup patterns once invoked ([00a2781](https://github.com/karma-runner/karma/commit/00a2781)), closes [#1340](https://github.com/karma-runner/karma/issues/1340)
+* **preprocessor:** renamed handeFile to readFileCallback ([92a8c81](https://github.com/karma-runner/karma/commit/92a8c81))
+* **preprocessor:** retry if fs.readFile fails ([4b60513](https://github.com/karma-runner/karma/commit/4b60513))
+* **preprocessor:** Throw error if can't open file ([bb4edde](https://github.com/karma-runner/karma/commit/bb4edde))
+* **preprocessor:** throw if retry fails ([2789bf5](https://github.com/karma-runner/karma/commit/2789bf5))
+* **preprocessor:** treat *.gz files as binary ([1b56932](https://github.com/karma-runner/karma/commit/1b56932))
+* **preprocessor:** treat *.swf files as binary ([62d7d38](https://github.com/karma-runner/karma/commit/62d7d38))
+* **preprocessor:** treat *.tgz, *.tbz2, *.txz & *.xz as binary ([7b64244](https://github.com/karma-runner/karma/commit/7b64244))
+* **proxy:** More useful proxyError log message ([96640a7](https://github.com/karma-runner/karma/commit/96640a7))
+* **proxy:** Pass protocol in target object to enable https requests ([142db90](https://github.com/karma-runner/karma/commit/142db90))
+* **proxy:** Port mixup and infinite loop ([05616a2](https://github.com/karma-runner/karma/commit/05616a2)), closes [#1987](https://github.com/karma-runner/karma/issues/1987)
+* **proxy:** proxy to correct port ([a483636](https://github.com/karma-runner/karma/commit/a483636))
+* **reporter:** Better handling of non string error ([82f1c12](https://github.com/karma-runner/karma/commit/82f1c12)), closes [#1969](https://github.com/karma-runner/karma/issues/1969) [#1988](https://github.com/karma-runner/karma/issues/1988)
+* **reporter:** Disable source maps for URLs without line number ([2080221](https://github.com/karma-runner/karma/commit/2080221)), closes [#1274](https://github.com/karma-runner/karma/issues/1274)
+* **reporter:** do not allow URL domains to span new lines ([2c13404](https://github.com/karma-runner/karma/commit/2c13404))
+* **reporter:** Enable sourcemaps for errors that without column # ([086a542](https://github.com/karma-runner/karma/commit/086a542))
+* **reporter:** Ensure errors use the source map. ([0407a22](https://github.com/karma-runner/karma/commit/0407a22)), closes [#1495](https://github.com/karma-runner/karma/issues/1495)
+* **reporter:** Fix issue causing error stack not to be parsed correctly ([ac4e1a9](https://github.com/karma-runner/karma/commit/ac4e1a9)), closes [#2930](https://github.com/karma-runner/karma/issues/2930)
+* **reporter:** inject correct config option ([80bd726](https://github.com/karma-runner/karma/commit/80bd726))
+* **reporter:** keep users exact formatError result ([17c2c43](https://github.com/karma-runner/karma/commit/17c2c43))
+* **reporter:** preserve base/absolute word in error ([b3798df](https://github.com/karma-runner/karma/commit/b3798df))
+* **reporter:** remove console.log ([b4e3694](https://github.com/karma-runner/karma/commit/b4e3694))
+* **reporter:** show file path correctly when urlRoot specified ([34dc7d3](https://github.com/karma-runner/karma/commit/34dc7d3)), closes [#2897](https://github.com/karma-runner/karma/issues/2897)
+* **reporter:** sourcemap not working in windows ([a9516af](https://github.com/karma-runner/karma/commit/a9516af)), closes [#1200](https://github.com/karma-runner/karma/issues/1200)
+* **reporter:** strip only hostname/port ([fbbeccf](https://github.com/karma-runner/karma/commit/fbbeccf)), closes [#2209](https://github.com/karma-runner/karma/issues/2209)
+* **reporters:** cannot read property map of undefined ([305df2c](https://github.com/karma-runner/karma/commit/305df2c)), closes [#1662](https://github.com/karma-runner/karma/issues/1662)
+* **reporters:** Fix results not being reported ([6303566](https://github.com/karma-runner/karma/commit/6303566))
+* **reporters:** Revert the backwards-incompatible log priority order changes ([316b944](https://github.com/karma-runner/karma/commit/316b944)), closes [#2582](https://github.com/karma-runner/karma/issues/2582)
+* **reporters:** Throwing error without loosing stack trace ([8a515ae](https://github.com/karma-runner/karma/commit/8a515ae))
+* **runner:** Fix typo in CSS class name for .idle ([fc5a7ce](https://github.com/karma-runner/karma/commit/fc5a7ce))
+* **runner:** Make process kill timeout configurable ([ffaa054](https://github.com/karma-runner/karma/commit/ffaa054)), closes [#2447](https://github.com/karma-runner/karma/issues/2447)
+* **runner:** Make process kill timeout configurable - Fix Build ([a128e5c](https://github.com/karma-runner/karma/commit/a128e5c)), closes [#2447](https://github.com/karma-runner/karma/issues/2447)
+* **runner:** Merge config.client.args with client.args provided by run ([91de383](https://github.com/karma-runner/karma/commit/91de383)), closes [#1746](https://github.com/karma-runner/karma/issues/1746)
+* **runner:** Remove null characters from terminal output ([3481500](https://github.com/karma-runner/karma/commit/3481500)), closes [#1343](https://github.com/karma-runner/karma/issues/1343)
+* **runner:** Test process kill timeout config ([99a1d48](https://github.com/karma-runner/karma/commit/99a1d48)), closes [#2447](https://github.com/karma-runner/karma/issues/2447)
+* **runner:** Wait for file list refresh to finish before running ([94cddc0](https://github.com/karma-runner/karma/commit/94cddc0))
+* **server:** check available port before start server (fix [#1476](https://github.com/karma-runner/karma/issues/1476), fix [#3011](https://github.com/karma-runner/karma/issues/3011)) ([a19b8d4](https://github.com/karma-runner/karma/commit/a19b8d4))
+* **server:** complete acknowledgment ([f4144b0](https://github.com/karma-runner/karma/commit/f4144b0))
+* **server:** exit with code 1 when failing due to missing browser ([86e2ef2](https://github.com/karma-runner/karma/commit/86e2ef2)), closes [#2403](https://github.com/karma-runner/karma/issues/2403)
+* **server:** Force clients disconnect on Windows ([28239f4](https://github.com/karma-runner/karma/commit/28239f4)), closes [#1109](https://github.com/karma-runner/karma/issues/1109)
+* **server:** Handle new socket.io internal format. ([3ab78d6](https://github.com/karma-runner/karma/commit/3ab78d6)), closes [#1782](https://github.com/karma-runner/karma/issues/1782)
+* **server:** log browser messages to the terminal ([d1f924c](https://github.com/karma-runner/karma/commit/d1f924c)), closes [#2187](https://github.com/karma-runner/karma/issues/2187)
+* **server:** Remove Socket.IO listeners ([c3f05ef](https://github.com/karma-runner/karma/commit/c3f05ef)), closes [#2980](https://github.com/karma-runner/karma/issues/2980)
+* **server:** Start webserver and browsers after preprocessing completed ([e0d2d23](https://github.com/karma-runner/karma/commit/e0d2d23))
+* **server:** switch to sync write ([6ec74ee](https://github.com/karma-runner/karma/commit/6ec74ee))
+* **server:** Update timers for limited execution environments ([9cfc1cd](https://github.com/karma-runner/karma/commit/9cfc1cd)), closes [#1519](https://github.com/karma-runner/karma/issues/1519)
+* **socket.io:** Force 0.9.16 which works with Chrome ([840ee5f](https://github.com/karma-runner/karma/commit/840ee5f))
+* **stringify:** guard Symobl from IE ([#3023](https://github.com/karma-runner/karma/issues/3023)) ([538081c](https://github.com/karma-runner/karma/commit/538081c))
+* invalid characters in the headers on Node 5.6.0 ([152337d](https://github.com/karma-runner/karma/commit/152337d))
+* **test:** locale in Expire header ([db04cf0](https://github.com/karma-runner/karma/commit/db04cf0)), closes [#1741](https://github.com/karma-runner/karma/issues/1741)
+* **test:** update bundleResource test timeout ([#3038](https://github.com/karma-runner/karma/issues/3038)) ([d6060d4](https://github.com/karma-runner/karma/commit/d6060d4))
+* **travis_ci:** converted node versions as string ([25ee6fc](https://github.com/karma-runner/karma/commit/25ee6fc))
+* filter browser logging by level of LOG ([89a7a1c](https://github.com/karma-runner/karma/commit/89a7a1c)), closes [#2228](https://github.com/karma-runner/karma/issues/2228)
+* **updater:** Fix time unit on screen display from 'ms' to 'seconds'. ([f39dd04](https://github.com/karma-runner/karma/commit/f39dd04))
+* a missed argument in a debug message ([#3009](https://github.com/karma-runner/karma/issues/3009)) ([af8c6e4](https://github.com/karma-runner/karma/commit/af8c6e4))
+* Add crossorigin attribute to script HTML tags ([5690ffe](https://github.com/karma-runner/karma/commit/5690ffe))
+* add emscripten memory image as binary suffix ([f6b2b56](https://github.com/karma-runner/karma/commit/f6b2b56))
+* call .resume to prevent browser output streams filling up ([107cd02](https://github.com/karma-runner/karma/commit/107cd02))
+* catch exceptions from SourceMapConsumer ([5d42e64](https://github.com/karma-runner/karma/commit/5d42e64))
+* Change timing on test ([0cb6204](https://github.com/karma-runner/karma/commit/0cb6204))
+* ignore jsVersion configuration property in Firefox 59+ ([2694d54](https://github.com/karma-runner/karma/commit/2694d54)), closes [#2957](https://github.com/karma-runner/karma/issues/2957)
+* make window.parent.karma available in debugged context ([3e7eaeb](https://github.com/karma-runner/karma/commit/3e7eaeb))
+* Merge config child nodes on config.set() ([65b688a](https://github.com/karma-runner/karma/commit/65b688a)), closes [karma-runner/grunt-karma#165](https://github.com/karma-runner/grunt-karma/issues/165) [karma-runner/grunt-karma#166](https://github.com/karma-runner/grunt-karma/issues/166)
+* Remove inadvertently added dependency to mock-fs ([ad5f6b5](https://github.com/karma-runner/karma/commit/ad5f6b5))
+* remove support of jsVersion configuration property ([#3002](https://github.com/karma-runner/karma/issues/3002)) ([2bb4e36](https://github.com/karma-runner/karma/commit/2bb4e36)), closes [#2911](https://github.com/karma-runner/karma/issues/2911)
+* restore backward compatibility for karma@0.13 ([648b357](https://github.com/karma-runner/karma/commit/648b357))
+* Safeguard IE against console.log ([0b5ff8f](https://github.com/karma-runner/karma/commit/0b5ff8f)), closes [#1209](https://github.com/karma-runner/karma/issues/1209)
+* Setting default value for config in runner and stopper ([414db89](https://github.com/karma-runner/karma/commit/414db89))
+* Switch all requires from fs to graceful-fs ([1e21aaa](https://github.com/karma-runner/karma/commit/1e21aaa))
+* upgrade http-proxy module for bug fixes ([09c75fe](https://github.com/karma-runner/karma/commit/09c75fe))
+* Upgrade socket.io to 1.4.5 ([2f51a9f](https://github.com/karma-runner/karma/commit/2f51a9f))
+* **UTs:** Correct proxy listeners expectation ([af9c84a](https://github.com/karma-runner/karma/commit/af9c84a))
+* **watcher:** Close file watchers on exit event ([7181025](https://github.com/karma-runner/karma/commit/7181025))
+* **watcher:** handle paths on Windows ([6164d86](https://github.com/karma-runner/karma/commit/6164d86))
+* **web-server:** Allow karma to run in project which path contains HTML URL encoded characters. Karma fails on Jenkins when it checks out branches containing '/' as it converts it to '%2F'. Fixes errors seen on [#1751](https://github.com/karma-runner/karma/issues/1751), [#61](https://github.com/karma-runner/karma/issues/61). ([da1930f](https://github.com/karma-runner/karma/commit/da1930f))
+* Wrap url.parse to always return an object for query property ([72452e9](https://github.com/karma-runner/karma/commit/72452e9)), closes [#1182](https://github.com/karma-runner/karma/issues/1182)
+* **web-server:** cache static files ([eb5bd53](https://github.com/karma-runner/karma/commit/eb5bd53))
+* **web-server:** Correctly update filesPromise on files updated ([32eec8d](https://github.com/karma-runner/karma/commit/32eec8d))
+* **web-server:** Ensure `filesPromise` is always resolvable ([892fa89](https://github.com/karma-runner/karma/commit/892fa89)), closes [#1544](https://github.com/karma-runner/karma/issues/1544)
+* **web-server:** Restart disconnected browser in non-singleRun mode. ([f6587dc](https://github.com/karma-runner/karma/commit/f6587dc))
+* **web-server:** Update config on every request ([8ef475f](https://github.com/karma-runner/karma/commit/8ef475f)), closes [#1972](https://github.com/karma-runner/karma/issues/1972)
+
+
+### Code Refactoring
+
+* **context:** Future-proofed context.html and debug.html for modularity ([43f6a1a](https://github.com/karma-runner/karma/commit/43f6a1a)), closes [#1984](https://github.com/karma-runner/karma/issues/1984)
+
+
+### Features
+
+* Add `stopper` to the public API ([3d4fa00](https://github.com/karma-runner/karma/commit/3d4fa00))
+* add an option to run the tests by dynamically loading test scripts without iframe ([aa42c41](https://github.com/karma-runner/karma/commit/aa42c41))
+* Add engine support for iojs@3. ([eb1c8d2](https://github.com/karma-runner/karma/commit/eb1c8d2))
+* Add possibility to stop a karma server ([66ae80b](https://github.com/karma-runner/karma/commit/66ae80b))
+* add support for node 6 ([0b8dc2c](https://github.com/karma-runner/karma/commit/0b8dc2c))
+* add support for node@7 ([eb407ab](https://github.com/karma-runner/karma/commit/eb407ab)), closes [#2559](https://github.com/karma-runner/karma/issues/2559)
+* adding support for before middleware ([51b4206](https://github.com/karma-runner/karma/commit/51b4206))
+* Allow custom browser names ([60ba85f](https://github.com/karma-runner/karma/commit/60ba85f))
+* allow frameworks to add preprocessors ([f6f5eec](https://github.com/karma-runner/karma/commit/f6f5eec))
+* Allow frameworks to inject middleware ([d972f3d](https://github.com/karma-runner/karma/commit/d972f3d))
+* better string representation of errors ([c9e1ca9](https://github.com/karma-runner/karma/commit/c9e1ca9))
+* deprecate helper._ ([5c6b151](https://github.com/karma-runner/karma/commit/5c6b151)), closes [#1812](https://github.com/karma-runner/karma/issues/1812)
+* Do not fail on empty test suite ([8004763](https://github.com/karma-runner/karma/commit/8004763)), closes [#926](https://github.com/karma-runner/karma/issues/926)
+* drop core-js and babel where possible ([60dfc5c](https://github.com/karma-runner/karma/commit/60dfc5c))
+* Fail on launcher-, reporter-, plugin-, or preprocessor-load errors. ([fca930e](https://github.com/karma-runner/karma/commit/fca930e)), closes [#855](https://github.com/karma-runner/karma/issues/855)
+* serve ePub as binary files ([82ed0c6](https://github.com/karma-runner/karma/commit/82ed0c6))
+* **api:** add constants to the public api ([ee10977](https://github.com/karma-runner/karma/commit/ee10977)), closes [#2361](https://github.com/karma-runner/karma/issues/2361)
+* **api:** expose `config.parseConfig` on the public api ([7d2c1ae](https://github.com/karma-runner/karma/commit/7d2c1ae))
+* **browser:** add browser_info event ([09ac7d7](https://github.com/karma-runner/karma/commit/09ac7d7)), closes [#2192](https://github.com/karma-runner/karma/issues/2192)
+* **browser:** Emit a browser error when a disconnect occurs. ([e36ba6c](https://github.com/karma-runner/karma/commit/e36ba6c))
+* **ci:** disable testing of node versions below 4 ([ec92ea9](https://github.com/karma-runner/karma/commit/ec92ea9))
+* **cli:** Add .config/karma.conf.js to the default lookup path ([49bf1aa](https://github.com/karma-runner/karma/commit/49bf1aa)), closes [#1387](https://github.com/karma-runner/karma/issues/1387)
+* **cli:** Better CLI args validation ([73d31c2](https://github.com/karma-runner/karma/commit/73d31c2)), closes [#603](https://github.com/karma-runner/karma/issues/603)
+* **cli:** Warn on commands with underscores. ([0801a7f](https://github.com/karma-runner/karma/commit/0801a7f))
+* **client:** capture confirm & prompt ([3a618b3](https://github.com/karma-runner/karma/commit/3a618b3)), closes [#694](https://github.com/karma-runner/karma/issues/694)
+* **client:** log global error stack trace ([523d608](https://github.com/karma-runner/karma/commit/523d608)), closes [#2812](https://github.com/karma-runner/karma/issues/2812)
+* **config:** Add `forceJSONP` option ([8627d67](https://github.com/karma-runner/karma/commit/8627d67))
+* **config:** Add a clearContext config to prevent clearing of context. ([5fc8ee7](https://github.com/karma-runner/karma/commit/5fc8ee7))
+* **config:** Add configuration for adding javascript version. ([0239c75](https://github.com/karma-runner/karma/commit/0239c75)), closes [#1719](https://github.com/karma-runner/karma/issues/1719)
+* **config:** add nocache option for file patterns ([6ef7e7b](https://github.com/karma-runner/karma/commit/6ef7e7b))
+* **config:** add restartOnFileChange option ([1082f35](https://github.com/karma-runner/karma/commit/1082f35))
+* **config:** add support for TypeScript ([6445310](https://github.com/karma-runner/karma/commit/6445310))
+* **config:** allow config to be a default export ([9976dce](https://github.com/karma-runner/karma/commit/9976dce))
+* **config:** Allow custom context and debug files, with feature test and some specs. ([225c0e5](https://github.com/karma-runner/karma/commit/225c0e5))
+* **config:** allow to use newer versions of CoffeeScript ([c1fcf42](https://github.com/karma-runner/karma/commit/c1fcf42))
+* **config:** mime config option support ([d562383](https://github.com/karma-runner/karma/commit/d562383)), closes [#1735](https://github.com/karma-runner/karma/issues/1735)
+* **config:** Pass CLI arguments to `karma.config.js`. ([70cf903](https://github.com/karma-runner/karma/commit/70cf903)), closes [#1561](https://github.com/karma-runner/karma/issues/1561)
+* **config:** remove polling usage ([b0f41c7](https://github.com/karma-runner/karma/commit/b0f41c7)), closes [#2669](https://github.com/karma-runner/karma/issues/2669)
+* **deps:** add support for node@8 ([ea32194](https://github.com/karma-runner/karma/commit/ea32194)), closes [#2754](https://github.com/karma-runner/karma/issues/2754)
+* **deps:** add support for node@8 ([7feaee3](https://github.com/karma-runner/karma/commit/7feaee3)), closes [#2754](https://github.com/karma-runner/karma/issues/2754)
+* **deps:** update socket.io to `1.7.4` to avoid issue with `ws@1.1.2` ([264442b](https://github.com/karma-runner/karma/commit/264442b)), closes [#2593](https://github.com/karma-runner/karma/issues/2593)
+* **file-list:** Upgrade bluebird to v.3 ([f5c252f](https://github.com/karma-runner/karma/commit/f5c252f))
+* **file-list:** Use glob.sync for better speed ([1b65cde](https://github.com/karma-runner/karma/commit/1b65cde))
+* **grunt:** run check_clean before starting release. ([#2978](https://github.com/karma-runner/karma/issues/2978)) ([a3ff6c8](https://github.com/karma-runner/karma/commit/a3ff6c8))
+* **init:** install coffee-script automatically ([e876db6](https://github.com/karma-runner/karma/commit/e876db6)), closes [#1152](https://github.com/karma-runner/karma/issues/1152)
+* **launcher:** Add concurrency limit ([1741deb](https://github.com/karma-runner/karma/commit/1741deb)), closes [#1465](https://github.com/karma-runner/karma/issues/1465)
+* **launcher:** Enable specification of retry-limit ([cc5547c](https://github.com/karma-runner/karma/commit/cc5547c)), closes [#1126](https://github.com/karma-runner/karma/issues/1126)
+* **launcher:** output stderr for failing launchers ([7d33398](https://github.com/karma-runner/karma/commit/7d33398))
+* **launcher:** trim whitespace in browser name ([334f9fb](https://github.com/karma-runner/karma/commit/334f9fb))
+* **launcher:** trim whitespace in browser name ([871d46f](https://github.com/karma-runner/karma/commit/871d46f))
+* **logger:** Add date/time stamp to log output ([4a59443](https://github.com/karma-runner/karma/commit/4a59443))
+* **logger:** Add date/time stamp to log output ([a4b5cdd](https://github.com/karma-runner/karma/commit/a4b5cdd))
+* **logging:** Add colors and log-level options to run-command ([9d4e234](https://github.com/karma-runner/karma/commit/9d4e234)), closes [#1067](https://github.com/karma-runner/karma/issues/1067)
+* **logging:** Add colors and log-level options to run-command ([2d29165](https://github.com/karma-runner/karma/commit/2d29165)), closes [#1067](https://github.com/karma-runner/karma/issues/1067)
+* **logging:** Add logging-setup function ([d14bd62](https://github.com/karma-runner/karma/commit/d14bd62))
+* **logging:** Send color option to server ([486c4f3](https://github.com/karma-runner/karma/commit/486c4f3)), closes [#1067](https://github.com/karma-runner/karma/issues/1067)
+* **logging:** Send color option to server ([287d0db](https://github.com/karma-runner/karma/commit/287d0db)), closes [#1067](https://github.com/karma-runner/karma/issues/1067)
+* **middleware:** added manual file type option ([0330cd1](https://github.com/karma-runner/karma/commit/0330cd1)), closes [#2824](https://github.com/karma-runner/karma/issues/2824)
+* **preprocessor:** add 'mp3' and 'ogg' as binary formats to avoid media corruption in the browser. ([65a0767](https://github.com/karma-runner/karma/commit/65a0767))
+* **preprocessor:** Capital letters in binary files extenstions ([1688689](https://github.com/karma-runner/karma/commit/1688689)), closes [#1508](https://github.com/karma-runner/karma/issues/1508)
+* **preprocessor:** Instantiate preprocessors early to avoid race conditions ([8a9c8c7](https://github.com/karma-runner/karma/commit/8a9c8c7))
+* **preprocessors:** if a file matches multiple preprocessor patterns, intelligently merge the list of preprocessors, deduping and trying to preserve the order ([59642a6](https://github.com/karma-runner/karma/commit/59642a6))
+* **proxy:** add proxy events to config ([f5d99fb](https://github.com/karma-runner/karma/commit/f5d99fb))
+* **proxy:** Allow proxies configuration to be an object ([ad94356](https://github.com/karma-runner/karma/commit/ad94356))
+* **proxy:** Allow to configure changeOrigin option of http-proxy ([ae05ea4](https://github.com/karma-runner/karma/commit/ae05ea4)), closes [#1729](https://github.com/karma-runner/karma/issues/1729)
+* **reporter:** add config formatError function ([98a4fbf](https://github.com/karma-runner/karma/commit/98a4fbf)), closes [#2119](https://github.com/karma-runner/karma/issues/2119)
+* **reporter:** cache SourceMapConsumer ([fe6ed7e](https://github.com/karma-runner/karma/commit/fe6ed7e))
+* **reporter:** improve source map handling and reporting. ([cf0be47](https://github.com/karma-runner/karma/commit/cf0be47))
+* **reporter:** Replace way-too-big memoizee with a trivial solution. ([58340b1](https://github.com/karma-runner/karma/commit/58340b1))
+* **reporter:** Replace way-too-big memoizee with a trivial solution. ([d926fe3](https://github.com/karma-runner/karma/commit/d926fe3))
+* **reporters:** Look for color-reporter ([fd9262d](https://github.com/karma-runner/karma/commit/fd9262d))
+* **runner:** Buffer stdout and stderr for output when errors occur ([460d423](https://github.com/karma-runner/karma/commit/460d423)), closes [karma-runner/karma#2663](https://github.com/karma-runner/karma/issues/2663)
+* **runner:** provide error code on 'ECONNREFUSED' callback ([439bddb](https://github.com/karma-runner/karma/commit/439bddb))
+* **runner:** serve context in JSON format for JS-only environments ([189feff](https://github.com/karma-runner/karma/commit/189feff))
+* **runner:** Use favicon in static runner pages ([6cded4f](https://github.com/karma-runner/karma/commit/6cded4f))
+* **server:** add 'listening' event with port number ([82cd0df](https://github.com/karma-runner/karma/commit/82cd0df))
+* **server:** add listen address option so that IPv6 and loopback interfaces can be used ([8e5bee6](https://github.com/karma-runner/karma/commit/8e5bee6)), closes [#2477](https://github.com/karma-runner/karma/issues/2477)
+* **server:** Add public api to force a file list refresh. ([b3c462a](https://github.com/karma-runner/karma/commit/b3c462a))
+* **server:** improve public api ([82cbbad](https://github.com/karma-runner/karma/commit/82cbbad)), closes [#1037](https://github.com/karma-runner/karma/issues/1037) [#1482](https://github.com/karma-runner/karma/issues/1482) [#1467](https://github.com/karma-runner/karma/issues/1467)
+* **static:** Support media queries ([94e7b50](https://github.com/karma-runner/karma/commit/94e7b50))
+* **stopper:** Enable programically detached server ([f10fd81](https://github.com/karma-runner/karma/commit/f10fd81))
+* **watcher:** Allow using braces in watcher ([e046379](https://github.com/karma-runner/karma/commit/e046379)), closes [#1249](https://github.com/karma-runner/karma/issues/1249)
+* **watcher:** Debounce autoWatchBatchDelay ([2f8c049](https://github.com/karma-runner/karma/commit/2f8c049)), closes [#2331](https://github.com/karma-runner/karma/issues/2331)
+* **web-server:** add support for custom headers in files served ([4301bea](https://github.com/karma-runner/karma/commit/4301bea))
+* **web-server:** allow injection of custom middleware. ([2e963c3](https://github.com/karma-runner/karma/commit/2e963c3)), closes [#1612](https://github.com/karma-runner/karma/issues/1612)
+* update of supported node versions ([e79463b](https://github.com/karma-runner/karma/commit/e79463b))
+* upgrade dependencies to their latest versions ([08242a0](https://github.com/karma-runner/karma/commit/08242a0))
+* **web-server:** allow overriding of default http module ([1e7514d](https://github.com/karma-runner/karma/commit/1e7514d)), closes [#2424](https://github.com/karma-runner/karma/issues/2424)
+* **web-server:** Allow Range headers in web server. ([a567b6f](https://github.com/karma-runner/karma/commit/a567b6f)), closes [#2140](https://github.com/karma-runner/karma/issues/2140)
+* **web-server:** Allow running on https ([1696c78](https://github.com/karma-runner/karma/commit/1696c78))
+* Upgrade to socket.io 1.3 ([603872c](https://github.com/karma-runner/karma/commit/603872c)), closes [#1257](https://github.com/karma-runner/karma/issues/1257) [#1258](https://github.com/karma-runner/karma/issues/1258) [#1220](https://github.com/karma-runner/karma/issues/1220)
+* upstreamProxy config option to deal with proxies that adjust the base path, etc ([55755e4](https://github.com/karma-runner/karma/commit/55755e4))
+* **web-server:** Serve all files under urlRoot ([1319b32](https://github.com/karma-runner/karma/commit/1319b32))
+* **web-server:** Use isbinaryfile for binary file detection ([f938a8e](https://github.com/karma-runner/karma/commit/f938a8e)), closes [#1070](https://github.com/karma-runner/karma/issues/1070)
+
+
+### Reverts
+
+* "Merge pull request [#1791](https://github.com/karma-runner/karma/issues/1791) from budde377/feature-adding-no-colors-to-run-command" ([96ebdc4](https://github.com/karma-runner/karma/commit/96ebdc4)), closes [#1894](https://github.com/karma-runner/karma/issues/1894) [#1895](https://github.com/karma-runner/karma/issues/1895)
+
+
+### BREAKING CHANGES
+
+* **context:** Our `context.html` and `debug.html` structures have changed to lean on `context.js` and `debug.js`.
+* **server:** The public api interface has changed to a constructor form. To upgrade
+change
+
+```javascript
+var server = require(‘karma’).server
+server.start(config, done)
+```
+
+to
+
+```javascript
+var Server = require(‘karma’).Server
+var server = new Server(config, done)
+server.start()
+```
+
+
+
## [2.0.2](https://github.com/karma-runner/karma/compare/v2.0.1...v2.0.2) (2018-04-19)
diff --git a/appveyor.yml b/appveyor.yml
index 150f49eb8..12f756f1c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -5,6 +5,7 @@ environment:
- nodejs_version: "4"
- nodejs_version: "6"
- nodejs_version: "8"
+ - nodejs_version: "10"
matrix:
fast_finish: true
@@ -13,9 +14,6 @@ install:
# Install Node.js
- ps: Install-Product node $env:nodejs_version
- # Upgrade npm
- - npm install -g npm
-
# Output our current versions for debugging
- node --version
- npm --version
diff --git a/client/karma.js b/client/karma.js
index 6a456c759..ff4217c62 100644
--- a/client/karma.js
+++ b/client/karma.js
@@ -2,7 +2,7 @@ var stringify = require('../common/stringify')
var constant = require('./constants')
var util = require('../common/util')
-var Karma = function (socket, iframe, opener, navigator, location) {
+function Karma (socket, iframe, opener, navigator, location) {
var startEmitted = false
var reloadingContext = false
var self = this
@@ -47,7 +47,7 @@ var Karma = function (socket, iframe, opener, navigator, location) {
}
var childWindow = null
- var navigateContextTo = function (url) {
+ function navigateContextTo (url) {
if (self.config.useIframe === false) {
// run in new window
if (self.config.runInParent === false) {
@@ -98,7 +98,7 @@ var Karma = function (socket, iframe, opener, navigator, location) {
this.stringify = stringify
- var clearContext = function () {
+ function clearContext () {
reloadingContext = true
navigateContextTo('about:blank')
diff --git a/client/updater.js b/client/updater.js
index c5c0cf425..b1562e1cf 100644
--- a/client/updater.js
+++ b/client/updater.js
@@ -1,7 +1,7 @@
var VERSION = require('./constants').VERSION
-var StatusUpdater = function (socket, titleElement, bannerElement, browsersElement) {
- var updateBrowsersInfo = function (browsers) {
+function StatusUpdater (socket, titleElement, bannerElement, browsersElement) {
+ function updateBrowsersInfo (browsers) {
if (!browsersElement) {
return
}
@@ -14,7 +14,7 @@ var StatusUpdater = function (socket, titleElement, bannerElement, browsersEleme
browsersElement.innerHTML = items.join('\n')
}
- var updateBanner = function (status) {
+ function updateBanner (status) {
return function (param) {
if (!titleElement || !bannerElement) {
return
diff --git a/common/stringify.js b/common/stringify.js
index 6df1d3e97..491a08d87 100644
--- a/common/stringify.js
+++ b/common/stringify.js
@@ -1,10 +1,11 @@
var serialize = require('dom-serialize')
var instanceOf = require('./util').instanceOf
-var isNode = function (obj) {
+
+function isNode (obj) {
return (obj.tagName || obj.nodeName) && obj.nodeType
}
-var stringify = function stringify (obj, depth) {
+function stringify (obj, depth) {
if (depth === 0) {
return '...'
}
@@ -14,6 +15,8 @@ var stringify = function stringify (obj, depth) {
}
switch (typeof obj) {
+ case 'symbol':
+ return obj.toString()
case 'string':
return "'" + obj + "'"
case 'undefined':
@@ -25,8 +28,8 @@ var stringify = function stringify (obj, depth) {
return obj.toString().replace(/\{[\s\S]*\}/, '{ ... }')
} catch (err) {
if (err instanceof TypeError) {
- // Proxy(function abc(...) { ... })
- return 'Proxy(function ' + (obj.name || '') + '(...) { ... })'
+ // Support older browsers
+ return 'function ' + (obj.name || '') + '() { ... }'
} else {
throw err
}
diff --git a/config.tpl.coffee b/config.tpl.coffee
index 7b340dcc2..c7f15de80 100644
--- a/config.tpl.coffee
+++ b/config.tpl.coffee
@@ -25,7 +25,8 @@ module.exports = (config) ->
# preprocess matching files before serving them to the browser
# available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
- preprocessors: %PREPROCESSORS%
+ preprocessors: {%PREPROCESSORS%
+ }
# test results reporter to use
diff --git a/config.tpl.js b/config.tpl.js
index ede8f4c22..d50e11359 100644
--- a/config.tpl.js
+++ b/config.tpl.js
@@ -25,7 +25,8 @@ module.exports = function(config) {
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
- preprocessors: %PREPROCESSORS%,
+ preprocessors: {%PREPROCESSORS%
+ },
// test results reporter to use
diff --git a/config.tpl.ls b/config.tpl.ls
index abca7e0b3..c185ae4dd 100644
--- a/config.tpl.ls
+++ b/config.tpl.ls
@@ -25,8 +25,8 @@ module.exports = (config) ->
# preprocess matching files before serving them to the browser
# available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
- preprocessors: %PREPROCESSORS%
-
+ preprocessors: {%PREPROCESSORS%
+ }
# test results reporter to use
# possible values: 'dots', 'progress'
diff --git a/config.tpl.ts b/config.tpl.ts
index 918889aba..1a90a3284 100644
--- a/config.tpl.ts
+++ b/config.tpl.ts
@@ -25,7 +25,8 @@ module.exports = (config) => {
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
- preprocessors: %PREPROCESSORS%,
+ preprocessors: {%PREPROCESSORS%
+ },
// test results reporter to use
diff --git a/context/karma.js b/context/karma.js
index aac5a396f..2403ee898 100644
--- a/context/karma.js
+++ b/context/karma.js
@@ -2,7 +2,7 @@
var stringify = require('../common/stringify')
// Define our context Karma constructor
-var ContextKarma = function (callParentKarmaMethod) {
+function ContextKarma (callParentKarmaMethod) {
// Define local variables
var hasError = false
var self = this
@@ -30,7 +30,7 @@ var ContextKarma = function (callParentKarmaMethod) {
}
// Define our start handler
- var UNIMPLEMENTED_START = function () {
+ function UNIMPLEMENTED_START () {
this.error('You need to include some adapter that implements __karma__.start method!')
}
// all files loaded, let's start the execution
@@ -98,7 +98,7 @@ var ContextKarma = function (callParentKarmaMethod) {
}
// If we want to overload our console, then do it
- var getConsole = function (currentWindow) {
+ function getConsole (currentWindow) {
return currentWindow.console || {
log: function () {},
info: function () {},
diff --git a/docs/about/01-versioning.md b/docs/about/01-versioning.md
index b5b326628..7c5ce6802 100644
--- a/docs/about/01-versioning.md
+++ b/docs/about/01-versioning.md
@@ -1,20 +1,12 @@
-Karma uses [Semantic Versioning]. There are some special rules at the moment,
-as we have not yet released a `1.0.0`.
+Karma uses [Semantic Versioning].
-* Minor versions could introduce breaking changes.
-* Patch versions are expected to be compatible at all times.
+It is recommended that you add Karma by running:
-It is recommended that you add the following entry to your `package.json`
-file, either manually
-```javascript
-{
- "devDependencies": {
- "karma": "^0.13.0"
- }
-}
+```bash
+$ yarn add --dev karma
```
-or by running
+or:
```bash
$ npm --save-dev install karma
diff --git a/docs/config/01-configuration-file.md b/docs/config/01-configuration-file.md
index 8b40bbc49..02ab11bdb 100644
--- a/docs/config/01-configuration-file.md
+++ b/docs/config/01-configuration-file.md
@@ -458,7 +458,7 @@ The plugin must provide an express/connect middleware function (details about th
**Example:**
```javascript
-var CustomMiddlewareFactory = function (config) {
+function CustomMiddlewareFactory (config) {
return function (request, response, /* next */) {
response.writeHead(200)
return response.end("content!")
@@ -794,18 +794,6 @@ All of Karma's urls get prefixed with the `urlRoot`. This is helpful when using
sometimes you might want to proxy a url that is already taken by Karma.
-## jsVersion
-**Type:** Number
-
-**Default:** `0`
-
-**Description:** The JavaScript version to use in the Firefox browser.
-
-If `> 0`, Karma will add a JavaScript version tag to the included JavaScript files.
-
-Note: This will only be applied to the Firefox browser up to version 58. Support for JavaScript version was [removed](https://bugzilla.mozilla.org/show_bug.cgi?id=1428745) in Firefox 59. This property is deprecated and will be removed in the next major release of Karma.
-
-
[plugins]: plugins.html
[config/files]: files.html
[config/browsers]: browsers.html
diff --git a/docs/config/02-files.md b/docs/config/02-files.md
index 141fe36df..7cd632082 100644
--- a/docs/config/02-files.md
+++ b/docs/config/02-files.md
@@ -17,12 +17,12 @@
## Included, served, watched
-Each pattern is either a simple string or an object with four properties:
+Each pattern is either a simple string or an object with the following properties:
### `pattern`
* **Type.** String
-* **No Default.**
-* **Description.** The pattern to use for matching. This property is mandatory.
+* **No Default.** This property is mandatory.
+* **Description.** The pattern to use for matching.
### `type`
* **Type.** String
@@ -143,6 +143,7 @@ proxies: {
* In-memory caching of files.
* Watching for updates in the files.
* Proxies to alter file paths.
+* Support for custom middlewares (the `middleware` configuration option).
[glob]: https://github.com/isaacs/node-glob
diff --git a/docs/intro/03-how-it-works.md b/docs/intro/03-how-it-works.md
index 61a1ce111..eafdec9da 100644
--- a/docs/intro/03-how-it-works.md
+++ b/docs/intro/03-how-it-works.md
@@ -14,7 +14,28 @@ The server collects the results from all of the captured browsers and presents t
This is only a very brief overview, as the internals of how Karma works aren't entirely necessary when using Karma.
-However, if you are interested in learning more, Karma itself originates from a university thesis, which goes into detail about the design
+## Outline of workflow.
+
+Here is roughly how Karma works:
+
+After starting up, Karma loads plugins and the configuration file, then starts its local web server which listens for connections.
+Any browser already waiting on websockets from the server will reconnect immediately. As part of loading the plugins, test reporters
+register for 'browser' events so they are ready for test results.
+
+Then karma launches zero, one, or more browsers, setting their start page the Karma server URL.
+
+When the browsers connect, Karma serves a 'client.html' page; when this page runs in the browser it connects back to the server via websockets.
+
+Once the server sees the websocket connection, it instructs the client -- over the websocket -- to execute tests. The client page opens an iframe with a 'context.html' page from the server. The server generates this context.html page using the configuration. This page includes the test framework adapter, the code to be tested, and the test code.
+
+When the browser loads this context page, the onload event handler connects the context page to the client page via postMessage. The framework adapter is in charge at this point: it runs the test, reporting errors or success by messaging through the client page.
+
+Messages sent to the client page are forwarded through the websocket to the Karma server. The server re-dispatches these messages as 'browser' events. The reporters listening to 'browser' events get the data; they may print it, save it to files, or forward the data to another service.
+Since the data is sent by the test framework adapter to the reporter, adapters and reporters almost always come in pairs, like karma-jasmine and karma-jasmine-reporter. The detailed content of test-result data is of no concern to other parts of karma: only the reporter needs to know it format.
+
+Karma has many variations and options that may cause different workflow with different configurations.
+
+If you are interested in learning more about the design, Karma itself originates from a university thesis, which goes into detail about the design
and implementation, and it is available to read [right here].
[right here]: https://github.com/karma-runner/karma/raw/master/thesis.pdf
diff --git a/gruntfile.js b/gruntfile.js
index 0a1b6f1ce..3ae8f22bc 100644
--- a/gruntfile.js
+++ b/gruntfile.js
@@ -131,6 +131,7 @@ module.exports = function (grunt) {
}
})
+ grunt.loadNpmTasks('grunt-check-clean')
grunt.loadTasks('tasks')
require('load-grunt-tasks')(grunt)
@@ -141,6 +142,7 @@ module.exports = function (grunt) {
grunt.registerTask('release', 'Build, bump and publish to NPM.', function (type) {
grunt.task.run([
+ 'check_clean',
'npm-contributors',
'bump:' + (type || 'patch') + ':bump-only',
'build',
diff --git a/lib/config.js b/lib/config.js
index cd60dc078..488ae41fe 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -431,7 +431,7 @@ function parseConfig (configFilePath, cliOptions) {
if (config.hostname === null && config.listenAddress !== null) {
log.warn('ListenAddress was set to %s but hostname was left as the default: ' +
'%s. If your browsers fail to connect, consider changing the hostname option.',
- config.listenAddress, defaultHostname)
+ config.listenAddress, defaultHostname)
}
// restore values that weren't overwritten by the user
if (config.hostname === null) {
diff --git a/lib/events.js b/lib/events.js
index 5e0876d33..29951849b 100644
--- a/lib/events.js
+++ b/lib/events.js
@@ -1,75 +1,58 @@
-var events = require('events')
-var util = require('util')
+'use strict'
-var helper = require('./helper')
+const EventEmitter = require('events').EventEmitter
+const helper = require('./helper')
-var bindAllEvents = function (object, context) {
- context = context || this
+function bufferEvents (emitter, eventsToBuffer) {
+ const listeners = []
+ const eventsToReply = []
- var bindMethod = function (method) {
- context.on(helper.camelToSnake(method.substr(2)), function () {
- var args = Array.prototype.slice.call(arguments, 0)
- args.push(context)
- object[method].apply(object, args)
- })
+ function genericListener () {
+ eventsToReply.push(Array.from(arguments))
}
- for (var method in object) {
- if (helper.isFunction(object[method]) && method.substr(0, 2) === 'on') {
- bindMethod(method)
- }
- }
-}
-
-var bufferEvents = function (emitter, eventsToBuffer) {
- var listeners = []
- var eventsToReply = []
- var genericListener = function () {
- eventsToReply.push(Array.prototype.slice.call(arguments))
- }
-
- eventsToBuffer.forEach(function (eventName) {
- var listener = genericListener.bind(null, eventName)
+ eventsToBuffer.forEach((eventName) => {
+ const listener = genericListener.bind(null, eventName)
listeners.push(listener)
emitter.on(eventName, listener)
})
return function () {
- if (!eventsToReply) {
- return
- }
-
- // remove all buffering listeners
- listeners.forEach(function (listener, i) {
+ listeners.forEach((listener, i) => {
emitter.removeListener(eventsToBuffer[i], listener)
})
- // reply
- eventsToReply.forEach(function (args) {
- events.EventEmitter.prototype.emit.apply(emitter, args)
+ eventsToReply.forEach((args) => {
+ EventEmitter.prototype.emit.apply(emitter, args)
})
- // free-up
- listeners = eventsToReply = null
+ listeners.length = 0
+ eventsToReply.length = 0
}
}
-// TODO(vojta): log.debug all events
-var EventEmitter = function () {
- this.bind = bindAllEvents
+class KarmaEventEmitter extends EventEmitter {
+ bind (object) {
+ Object.keys(object).forEach((method) => {
+ if (method.startsWith('on') && helper.isFunction(object[method])) {
+ this.on(helper.camelToSnake(method.substr(2)), function () {
+ object[method].apply(object, Array.from(arguments).concat(this))
+ })
+ }
+ })
+ }
- this.emitAsync = function (name) {
+ emitAsync (name) {
// TODO(vojta): allow passing args
// TODO(vojta): ignore/throw if listener call done() multiple times
- var pending = this.listeners(name).length
- var deferred = helper.defer()
- var done = function () {
+ let pending = this.listeners(name).length
+ const deferred = helper.defer()
+
+ this.emit(name, () => {
if (!--pending) {
deferred.resolve()
}
- }
-
- this.emit(name, done)
+ })
if (!pending) {
deferred.resolve()
@@ -79,9 +62,5 @@ var EventEmitter = function () {
}
}
-util.inherits(EventEmitter, events.EventEmitter)
-
-// PUBLISH
-exports.EventEmitter = EventEmitter
-exports.bindAll = bindAllEvents
+exports.EventEmitter = KarmaEventEmitter
exports.bufferEvents = bufferEvents
diff --git a/lib/executor.js b/lib/executor.js
index a20451b1b..55694c4b8 100644
--- a/lib/executor.js
+++ b/lib/executor.js
@@ -1,60 +1,70 @@
-var log = require('./logger').create()
+'use strict'
-var Executor = function (capturedBrowsers, config, emitter) {
- var self = this
- var executionScheduled = false
- var pendingCount = 0
- var runningBrowsers
+const log = require('./logger').create()
- var schedule = function () {
- var nonReady = []
+class Executor {
+ constructor (capturedBrowsers, config, emitter) {
+ this.capturedBrowsers = capturedBrowsers
+ this.config = config
+ this.emitter = emitter
- if (!capturedBrowsers.length) {
- log.warn('No captured browser, open %s//%s:%s%s', config.protocol, config.hostname,
- config.port, config.urlRoot)
+ this.executionScheduled = false
+ this.pendingCount = 0
+ this.runningBrowsers = null
+
+ // bind all the events
+ this.emitter.on('run_complete', () => this.onRunComplete())
+ this.emitter.on('browser_complete', () => this.onBrowserComplete())
+ }
+
+ schedule () {
+ const nonReady = []
+
+ if (!this.capturedBrowsers.length) {
+ log.warn('No captured browser, open %s//%s:%s%s', this.config.protocol, this.config.hostname,
+ this.config.port, this.config.urlRoot)
return false
}
- if (capturedBrowsers.areAllReady(nonReady)) {
+ if (this.capturedBrowsers.areAllReady(nonReady)) {
log.debug('All browsers are ready, executing')
- log.debug('Captured %s browsers', capturedBrowsers.length)
- executionScheduled = false
- capturedBrowsers.clearResults()
- capturedBrowsers.setAllToExecuting()
- pendingCount = capturedBrowsers.length
- runningBrowsers = capturedBrowsers.clone()
- emitter.emit('run_start', runningBrowsers)
- self.socketIoSockets.emit('execute', config.client)
+ log.debug('Captured %s browsers', this.capturedBrowsers.length)
+ this.executionScheduled = false
+ this.capturedBrowsers.clearResults()
+ this.capturedBrowsers.setAllToExecuting()
+ this.pendingCount = this.capturedBrowsers.length
+ this.runningBrowsers = this.capturedBrowsers.clone()
+ this.emitter.emit('run_start', this.runningBrowsers)
+ this.socketIoSockets.emit('execute', this.config.client)
return true
}
log.info('Delaying execution, these browsers are not ready: ' + nonReady.join(', '))
- executionScheduled = true
+ this.executionScheduled = true
return false
}
- this.schedule = schedule
-
- this.onRunComplete = function () {
- if (executionScheduled) {
- schedule()
+ onRunComplete () {
+ if (this.executionScheduled) {
+ this.schedule()
}
}
- this.onBrowserComplete = function () {
- pendingCount--
+ onBrowserComplete () {
+ this.pendingCount--
- if (!pendingCount) {
+ if (!this.pendingCount) {
// Ensure run_complete is emitted in the next tick
// so it is never emitted before browser_complete
- setTimeout(function () {
- emitter.emit('run_complete', runningBrowsers, runningBrowsers.getResults())
+ setTimeout(() => {
+ this.emitter.emit('run_complete', this.runningBrowsers, this.runningBrowsers.getResults())
}, 0)
}
}
+}
- // bind all the events
- emitter.bind(this)
+Executor.factory = function (capturedBrowsers, config, emitter) {
+ return new Executor(capturedBrowsers, config, emitter)
}
module.exports = Executor
diff --git a/lib/file-list.js b/lib/file-list.js
index 070e65189..13129cd32 100644
--- a/lib/file-list.js
+++ b/lib/file-list.js
@@ -1,29 +1,25 @@
-// File List
-// =========
-//
-// The List is an object for tracking all files that karma knows about
-// currently.
+'use strict'
// Dependencies
// ------------
-var Promise = require('bluebird')
-var mm = require('minimatch')
-var Glob = require('glob').Glob
-var fs = Promise.promisifyAll(require('graceful-fs'))
-var pathLib = require('path')
-var _ = require('lodash')
+const Promise = require('bluebird')
+const mm = require('minimatch')
+const Glob = require('glob').Glob
+const fs = Promise.promisifyAll(require('graceful-fs'))
+const pathLib = require('path')
+const _ = require('lodash')
-var File = require('./file')
-var Url = require('./url')
-var helper = require('./helper')
-var log = require('./logger').create('watcher')
-var createPatternObject = require('./config').createPatternObject
+const File = require('./file')
+const Url = require('./url')
+const helper = require('./helper')
+const log = require('./logger').create('watcher')
+const createPatternObject = require('./config').createPatternObject
// Constants
// ---------
-var GLOB_OPTS = {
+const GLOB_OPTS = {
cwd: '/',
follow: true,
nodir: true,
@@ -33,211 +29,203 @@ var GLOB_OPTS = {
// Helper Functions
// ----------------
-function byPath (a, b) {
+const byPath = (a, b) => {
if (a.path > b.path) return 1
if (a.path < b.path) return -1
return 0
}
-// Constructor
-//
-// patterns - Array
-// excludes - Array
-// emitter - EventEmitter
-// preprocess - Function
-// batchInterval - Number
-var List = function (patterns, excludes, emitter, preprocess, autoWatchBatchDelay) {
- // Store options
- this._patterns = patterns
- this._excludes = excludes
- this._emitter = emitter
- this._preprocess = Promise.promisify(preprocess)
- this._autoWatchBatchDelay = autoWatchBatchDelay
-
- // The actual list of files
- this.buckets = new Map()
-
- // Internal tracker if we are refreshing.
- // When a refresh is triggered this gets set
- // to the promise that `this._refresh` returns.
- // So we know we are refreshing when this promise
- // is still pending, and we are done when it's either
- // resolved or rejected.
- this._refreshing = Promise.resolve()
-
- var self = this
-
- // Emit the `file_list_modified` event.
- // This function is debounced to the value of `autoWatchBatchDelay`
- // to avoid reloading while files are still being modified.
- function emit () {
- self._emitter.emit('file_list_modified', self.files)
- }
- var debouncedEmit = _.debounce(emit, self._autoWatchBatchDelay)
- self._emitModified = function (immediate) {
- immediate ? emit() : debouncedEmit()
- }
-}
-
-// Private Interface
-// -----------------
-
-// Is the given path matched by any exclusion filter
-//
-// path - String
-//
-// Returns `undefined` if no match, otherwise the matching
-// pattern.
-List.prototype._isExcluded = function (path) {
- return _.find(this._excludes, function (pattern) {
- return mm(path, pattern)
- })
-}
+/**
+ * The List is an object for tracking all files that karma knows about
+ * currently.
+ */
+class FileList {
+ /**
+ * @param {Array} patterns
+ * @param {Array} excludes
+ * @param {EventEmitter} emitter
+ * @param {Function} preprocess
+ * @param {number} autoWatchBatchDelay
+ */
+ constructor (patterns, excludes, emitter, preprocess, autoWatchBatchDelay) {
+ // Store options
+ this._patterns = patterns
+ this._excludes = excludes
+ this._emitter = emitter
+ this._preprocess = Promise.promisify(preprocess)
+ this._autoWatchBatchDelay = autoWatchBatchDelay
+
+ // The actual list of files
+ this.buckets = new Map()
+
+ // Internal tracker if we are refreshing.
+ // When a refresh is triggered this gets set
+ // to the promise that `this._refresh` returns.
+ // So we know we are refreshing when this promise
+ // is still pending, and we are done when it's either
+ // resolved or rejected.
+ this._refreshing = Promise.resolve()
+
+ // Emit the `file_list_modified` event.
+ // This function is debounced to the value of `autoWatchBatchDelay`
+ // to avoid reloading while files are still being modified.
+ const emit = () => {
+ this._emitter.emit('file_list_modified', this.files)
+ }
-// Find the matching include pattern for the given path.
-//
-// path - String
-//
-// Returns the match or `undefined` if none found.
-List.prototype._isIncluded = function (path) {
- return _.find(this._patterns, function (pattern) {
- return mm(path, pattern.pattern)
- })
-}
+ const debouncedEmit = _.debounce(emit, this._autoWatchBatchDelay)
+ this._emitModified = (immediate) => {
+ immediate ? emit() : debouncedEmit()
+ }
+ }
-// Find the given path in the bucket corresponding
-// to the given pattern.
-//
-// path - String
-// pattern - Object
-//
-// Returns a File or undefined
-List.prototype._findFile = function (path, pattern) {
- if (!path || !pattern) return
- if (!this.buckets.has(pattern.pattern)) return
-
- return _.find(Array.from(this.buckets.get(pattern.pattern)), function (file) {
- return file.originalPath === path
- })
-}
+ // Private Interface
+ // -----------------
+
+ // Is the given path matched by any exclusion filter
+ //
+ // path - String
+ //
+ // Returns `undefined` if no match, otherwise the matching
+ // pattern.
+ _isExcluded (path) {
+ return _.find(this._excludes, (pattern) => mm(path, pattern))
+ }
-// Is the given path already in the files list.
-//
-// path - String
-//
-// Returns a boolean.
-List.prototype._exists = function (path) {
- var self = this
-
- var patterns = this._patterns.filter(function (pattern) {
- return mm(path, pattern.pattern)
- })
-
- return !!_.find(patterns, function (pattern) {
- return self._findFile(path, pattern)
- })
-}
+ // Find the matching include pattern for the given path.
+ //
+ // path - String
+ //
+ // Returns the match or `undefined` if none found.
+ _isIncluded (path) {
+ return _.find(this._patterns, (pattern) => mm(path, pattern.pattern))
+ }
-// Check if we are currently refreshing
-List.prototype._isRefreshing = function () {
- return this._refreshing.isPending()
-}
+ // Find the given path in the bucket corresponding
+ // to the given pattern.
+ //
+ // path - String
+ // pattern - Object
+ //
+ // Returns a File or undefined
+ _findFile (path, pattern) {
+ if (!path || !pattern) return
+ if (!this.buckets.has(pattern.pattern)) return
+
+ return _.find(Array.from(this.buckets.get(pattern.pattern)), (file) => {
+ return file.originalPath === path
+ })
+ }
-// Do the actual work of refreshing
-List.prototype._refresh = function () {
- var self = this
- var buckets = this.buckets
- var matchedFiles = new Set()
+ // Is the given path already in the files list.
+ //
+ // path - String
+ //
+ // Returns a boolean.
+ _exists (path) {
+ const patterns = this._patterns.filter((pattern) => mm(path, pattern.pattern))
- var promise = Promise.map(this._patterns, function (patternObject) {
- var pattern = patternObject.pattern
- var type = patternObject.type
+ return !!_.find(patterns, (pattern) => this._findFile(path, pattern))
+ }
- if (helper.isUrlAbsolute(pattern)) {
- buckets.set(pattern, new Set([new Url(pattern, type)]))
- return Promise.resolve()
- }
+ // Check if we are currently refreshing
+ _isRefreshing () {
+ return this._refreshing.isPending()
+ }
- var mg = new Glob(pathLib.normalize(pattern), GLOB_OPTS)
- var files = mg.found
- buckets.set(pattern, new Set())
+ // Do the actual work of refreshing
+ _refresh () {
+ const buckets = this.buckets
+ const matchedFiles = new Set()
- if (_.isEmpty(files)) {
- log.warn('Pattern "%s" does not match any file.', pattern)
- return
- }
+ let promise
+ promise = Promise.map(this._patterns, (patternObject) => {
+ const pattern = patternObject.pattern
+ const type = patternObject.type
- return Promise.map(files, function (path) {
- if (self._isExcluded(path)) {
- log.debug('Excluded file "%s"', path)
+ if (helper.isUrlAbsolute(pattern)) {
+ buckets.set(pattern, new Set([new Url(pattern, type)]))
return Promise.resolve()
}
- if (matchedFiles.has(path)) {
- return Promise.resolve()
+ const mg = new Glob(pathLib.normalize(pattern), GLOB_OPTS)
+ const files = mg.found
+ buckets.set(pattern, new Set())
+
+ if (_.isEmpty(files)) {
+ log.warn('Pattern "%s" does not match any file.', pattern)
+ return
}
- matchedFiles.add(path)
+ return Promise.map(files, (path) => {
+ if (this._isExcluded(path)) {
+ log.debug('Excluded file "%s"', path)
+ return Promise.resolve()
+ }
+
+ if (matchedFiles.has(path)) {
+ return Promise.resolve()
+ }
- var mtime = mg.statCache[path].mtime
- var doNotCache = patternObject.nocache
- var type = patternObject.type
- var file = new File(path, mtime, doNotCache, type)
+ matchedFiles.add(path)
- if (file.doNotCache) {
- log.debug('Not preprocessing "%s" due to nocache')
- return Promise.resolve(file)
- }
+ const mtime = mg.statCache[path].mtime
+ const doNotCache = patternObject.nocache
+ const type = patternObject.type
+ const file = new File(path, mtime, doNotCache, type)
- return self._preprocess(file).then(function () {
- return file
- })
- })
- .then(function (files) {
- files = _.compact(files)
+ if (file.doNotCache) {
+ log.debug('Not preprocessing "%s" due to nocache', pattern)
+ return Promise.resolve(file)
+ }
- if (_.isEmpty(files)) {
- log.warn('All files matched by "%s" were excluded or matched by prior matchers.', pattern)
- } else {
- buckets.set(pattern, new Set(files))
- }
+ return this._preprocess(file).then(() => {
+ return file
+ })
+ })
+ .then((files) => {
+ files = _.compact(files)
+
+ if (_.isEmpty(files)) {
+ log.warn('All files matched by "%s" were excluded or matched by prior matchers.', pattern)
+ } else {
+ buckets.set(pattern, new Set(files))
+ }
+ })
})
- })
- .then(function () {
- if (self._refreshing !== promise) {
- return self._refreshing
- }
- self.buckets = buckets
- self._emitModified(true)
- return self.files
- })
+ .then(() => {
+ if (this._refreshing !== promise) {
+ return this._refreshing
+ }
+ this.buckets = buckets
+ this._emitModified(true)
+ return this.files
+ })
- return promise
-}
+ return promise
+ }
-// Public Interface
-// ----------------
+ // Public Interface
+ // ----------------
-Object.defineProperty(List.prototype, 'files', {
- get: function () {
- var self = this
- var uniqueFlat = function (list) {
+ get files () {
+ const uniqueFlat = (list) => {
return _.uniq(_.flatten(list), 'path')
}
- var expandPattern = function (p) {
- return Array.from(self.buckets.get(p.pattern) || []).sort(byPath)
+ const expandPattern = (p) => {
+ return Array.from(this.buckets.get(p.pattern) || []).sort(byPath)
}
- var served = this._patterns.filter(function (pattern) {
+ const served = this._patterns.filter((pattern) => {
return pattern.served
})
- .map(expandPattern)
+ .map(expandPattern)
- var lookup = {}
- var included = {}
- this._patterns.forEach(function (p) {
+ const lookup = {}
+ const included = {}
+ this._patterns.forEach((p) => {
// This needs to be here sadly, as plugins are modifiying
// the _patterns directly resulting in elements not being
// instantiated properly
@@ -245,9 +233,9 @@ Object.defineProperty(List.prototype, 'files', {
p = createPatternObject(p)
}
- var bucket = expandPattern(p)
- bucket.forEach(function (file) {
- var other = lookup[file.path]
+ const bucket = expandPattern(p)
+ bucket.forEach((file) => {
+ const other = lookup[file.path]
if (other && other.compare(p) < 0) return
lookup[file.path] = p
if (p.included) {
@@ -263,148 +251,144 @@ Object.defineProperty(List.prototype, 'files', {
included: _.values(included)
}
}
-})
-
-// Reglob all patterns to update the list.
-//
-// Returns a promise that is resolved when the refresh
-// is completed.
-List.prototype.refresh = function () {
- this._refreshing = this._refresh()
- return this._refreshing
-}
-
-// Set new patterns and excludes and update
-// the list accordingly
-//
-// patterns - Array, the new patterns.
-// excludes - Array, the new exclude patterns.
-//
-// Returns a promise that is resolved when the refresh
-// is completed.
-List.prototype.reload = function (patterns, excludes) {
- this._patterns = patterns
- this._excludes = excludes
-
- // Wait until the current refresh is done and then do a
- // refresh to ensure a refresh actually happens
- return this.refresh()
-}
-// Add a new file from the list.
-// This is called by the watcher
-//
-// path - String, the path of the file to update.
-//
-// Returns a promise that is resolved when the update
-// is completed.
-List.prototype.addFile = function (path) {
- var self = this
-
- // Ensure we are not adding a file that should be excluded
- var excluded = this._isExcluded(path)
- if (excluded) {
- log.debug('Add file "%s" ignored. Excluded by "%s".', path, excluded)
-
- return Promise.resolve(this.files)
+ // Reglob all patterns to update the list.
+ //
+ // Returns a promise that is resolved when the refresh
+ // is completed.
+ refresh () {
+ this._refreshing = this._refresh()
+ return this._refreshing
}
- var pattern = this._isIncluded(path)
-
- if (!pattern) {
- log.debug('Add file "%s" ignored. Does not match any pattern.', path)
- return Promise.resolve(this.files)
+ // Set new patterns and excludes and update
+ // the list accordingly
+ //
+ // patterns - Array, the new patterns.
+ // excludes - Array, the new exclude patterns.
+ //
+ // Returns a promise that is resolved when the refresh
+ // is completed.
+ reload (patterns, excludes) {
+ this._patterns = patterns
+ this._excludes = excludes
+
+ // Wait until the current refresh is done and then do a
+ // refresh to ensure a refresh actually happens
+ return this.refresh()
}
- if (this._exists(path)) {
- log.debug('Add file "%s" ignored. Already in the list.', path)
- return Promise.resolve(this.files)
- }
+ // Add a new file from the list.
+ // This is called by the watcher
+ //
+ // path - String, the path of the file to update.
+ //
+ // Returns a promise that is resolved when the update
+ // is completed.
+ addFile (path) {
+ // Ensure we are not adding a file that should be excluded
+ const excluded = this._isExcluded(path)
+ if (excluded) {
+ log.debug('Add file "%s" ignored. Excluded by "%s".', path, excluded)
+
+ return Promise.resolve(this.files)
+ }
- var file = new File(path)
- this.buckets.get(pattern.pattern).add(file)
-
- return Promise.all([
- fs.statAsync(path),
- this._refreshing
- ]).spread(function (stat) {
- file.mtime = stat.mtime
- return self._preprocess(file)
- })
- .then(function () {
- log.info('Added file "%s".', path)
- self._emitModified()
- return self.files
- })
-}
+ const pattern = this._isIncluded(path)
-// Update the `mtime` of a file.
-// This is called by the watcher
-//
-// path - String, the path of the file to update.
-//
-// Returns a promise that is resolved when the update
-// is completed.
-List.prototype.changeFile = function (path) {
- var self = this
-
- var pattern = this._isIncluded(path)
- var file = this._findFile(path, pattern)
-
- if (!pattern || !file) {
- log.debug('Changed file "%s" ignored. Does not match any file in the list.', path)
- return Promise.resolve(this.files)
- }
+ if (!pattern) {
+ log.debug('Add file "%s" ignored. Does not match any pattern.', path)
+ return Promise.resolve(this.files)
+ }
- return Promise.all([
- fs.statAsync(path),
- this._refreshing
- ]).spread(function (stat) {
- if (stat.mtime <= file.mtime) throw new Promise.CancellationError()
-
- file.mtime = stat.mtime
- return self._preprocess(file)
- })
- .then(function () {
- log.info('Changed file "%s".', path)
- self._emitModified()
- return self.files
- })
- .catch(Promise.CancellationError, function () {
- return self.files
- })
-}
+ if (this._exists(path)) {
+ log.debug('Add file "%s" ignored. Already in the list.', path)
+ return Promise.resolve(this.files)
+ }
-// Remove a file from the list.
-// This is called by the watcher
-//
-// path - String, the path of the file to update.
-//
-// Returns a promise that is resolved when the update
-// is completed.
-List.prototype.removeFile = function (path) {
- var self = this
+ const file = new File(path)
+ this.buckets.get(pattern.pattern).add(file)
- return Promise.try(function () {
- var pattern = self._isIncluded(path)
- var file = self._findFile(path, pattern)
+ return Promise.all([
+ fs.statAsync(path),
+ this._refreshing
+ ]).spread((stat) => {
+ file.mtime = stat.mtime
+ return this._preprocess(file)
+ })
+ .then(() => {
+ log.info('Added file "%s".', path)
+ this._emitModified()
+ return this.files
+ })
+ }
+
+ // Update the `mtime` of a file.
+ // This is called by the watcher
+ //
+ // path - String, the path of the file to update.
+ //
+ // Returns a promise that is resolved when the update
+ // is completed.
+ changeFile (path) {
+ const pattern = this._isIncluded(path)
+ const file = this._findFile(path, pattern)
if (!pattern || !file) {
- log.debug('Removed file "%s" ignored. Does not match any file in the list.', path)
- return self.files
+ log.debug('Changed file "%s" ignored. Does not match any file in the list.', path)
+ return Promise.resolve(this.files)
}
- self.buckets.get(pattern.pattern).delete(file)
+ return Promise.all([
+ fs.statAsync(path),
+ this._refreshing
+ ]).spread((stat) => {
+ if (stat.mtime <= file.mtime) throw new Promise.CancellationError()
+
+ file.mtime = stat.mtime
+ return this._preprocess(file)
+ })
+ .then(() => {
+ log.info('Changed file "%s".', path)
+ this._emitModified()
+ return this.files
+ })
+ .catch(Promise.CancellationError, () => {
+ return this.files
+ })
+ }
+
+ // Remove a file from the list.
+ // This is called by the watcher
+ //
+ // path - String, the path of the file to update.
+ //
+ // Returns a promise that is resolved when the update
+ // is completed.
+ removeFile (path) {
+ return Promise.try(() => {
+ const pattern = this._isIncluded(path)
+ const file = this._findFile(path, pattern)
+
+ if (!pattern || !file) {
+ log.debug('Removed file "%s" ignored. Does not match any file in the list.', path)
+ return this.files
+ }
+
+ this.buckets.get(pattern.pattern).delete(file)
+
+ log.info('Removed file "%s".', path)
+ this._emitModified()
+ return this.files
+ })
+ }
+}
- log.info('Removed file "%s".', path)
- self._emitModified()
- return self.files
- })
+FileList.factory = function (patterns, excludes, emitter, preprocess, autoWatchBatchDelay) {
+ return new FileList(patterns, excludes, emitter, preprocess, autoWatchBatchDelay)
}
-// Inject dependencies
-List.$inject = ['config.files', 'config.exclude', 'emitter', 'preprocess',
+FileList.factory.$inject = ['config.files', 'config.exclude', 'emitter', 'preprocess',
'config.autoWatchBatchDelay']
-// PUBLIC
-module.exports = List
+module.exports = FileList
diff --git a/lib/file.js b/lib/file.js
index 0dd0a4827..094df9e0e 100644
--- a/lib/file.js
+++ b/lib/file.js
@@ -1,35 +1,30 @@
-// File
-// ====
-//
-// File object used for tracking files in `file-list.js`
+'use strict'
-// Dependencies
-// ------------
+/**
+ * File object used for tracking files in `file-list.js`.
+ */
+class File {
+ constructor (path, mtime, doNotCache, type) {
+ // used for serving (processed path, eg some/file.coffee -> some/file.coffee.js)
+ this.path = path
-var _ = require('lodash')
+ // original absolute path, id of the file
+ this.originalPath = path
-// Constructor
-var File = function (path, mtime, doNotCache, type) {
- // used for serving (processed path, eg some/file.coffee -> some/file.coffee.js)
- this.path = path
+ // where the content is stored (processed)
+ this.contentPath = path
- // original absolute path, id of the file
- this.originalPath = path
+ this.mtime = mtime
+ this.isUrl = false
- // where the content is stored (processed)
- this.contentPath = path
+ this.doNotCache = doNotCache === undefined ? false : doNotCache
- this.mtime = mtime
- this.isUrl = false
+ this.type = type
+ }
- this.doNotCache = _.isUndefined(doNotCache) ? false : doNotCache
-
- this.type = type
-}
-
-File.prototype.toString = function () {
- return this.path
+ toString () {
+ return this.path
+ }
}
-// PUBLIC
module.exports = File
diff --git a/lib/helper.js b/lib/helper.js
index e0b32c8ac..cdf051f01 100644
--- a/lib/helper.js
+++ b/lib/helper.js
@@ -1,26 +1,29 @@
-var fs = require('graceful-fs')
-var path = require('path')
-var _ = require('lodash')
-var useragent = require('useragent')
-var Promise = require('bluebird')
-var mm = require('minimatch')
-
-exports.browserFullNameToShort = function (fullName) {
- var agent = useragent.parse(fullName)
- var isKnown = agent.family !== 'Other' && agent.os.family !== 'Other'
+'use strict'
+
+const fs = require('graceful-fs')
+const path = require('path')
+const _ = require('lodash')
+const useragent = require('useragent')
+const Promise = require('bluebird')
+const mm = require('minimatch')
+
+exports.browserFullNameToShort = (fullName) => {
+ const agent = useragent.parse(fullName)
+ const isKnown = agent.family !== 'Other' && agent.os.family !== 'Other'
return isKnown ? agent.toAgent() + ' (' + agent.os + ')' : fullName
}
-exports.isDefined = function (value) {
+exports.isDefined = (value) => {
return !_.isUndefined(value)
}
-var parser = function (pattern, out) {
+
+const parser = (pattern, out) => {
if (pattern.length === 0) return out
- var p = /^(\[[^\]]*\]|[*+@?]\((.+?)\))/g
- var matches = p.exec(pattern)
+ const p = /^(\[[^\]]*\]|[*+@?]\((.+?)\))/g
+ const matches = p.exec(pattern)
if (!matches) {
- var c = pattern[0]
- var t = 'word'
+ const c = pattern[0]
+ let t = 'word'
if (c === '*') {
t = 'star'
} else if (c === '?') {
@@ -38,7 +41,7 @@ var parser = function (pattern, out) {
return parser(pattern.substring(matches[0].length), out)
}
-var gsParser = function (pattern, out) {
+const gsParser = (pattern, out) => {
if (pattern === '**') {
out.glob_star++
return out
@@ -46,18 +49,18 @@ var gsParser = function (pattern, out) {
return parser(pattern, out)
}
-var compareWeightObject = function (w1, w2) {
+const compareWeightObject = (w1, w2) => {
return exports.mmComparePatternWeights(
[w1.glob_star, w1.star, w1.ext_glob, w1.range, w1.optional],
[w2.glob_star, w2.star, w2.ext_glob, w2.range, w2.optional]
)
}
-exports.mmPatternWeight = function (pattern) {
- var m = new mm.Minimatch(pattern)
+exports.mmPatternWeight = (pattern) => {
+ const m = new mm.Minimatch(pattern)
if (!m.globParts) return [0, 0, 0, 0, 0, 0]
- var result = m.globParts.reduce(function (prev, p) {
- var r = p.reduce(function (prev, p) {
+ const result = m.globParts.reduce((prev, p) => {
+ const r = p.reduce((prev, p) => {
return gsParser(p, prev)
}, {glob_star: 0, ext_glob: 0, word: 0, star: 0, optional: 0, range: 0})
if (prev === undefined) return r
@@ -67,8 +70,8 @@ exports.mmPatternWeight = function (pattern) {
return [result.glob_sets, result.glob_star, result.star, result.ext_glob, result.range, result.optional]
}
-exports.mmComparePatternWeights = function (weight1, weight2) {
- var n1, n2, diff
+exports.mmComparePatternWeights = (weight1, weight2) => {
+ let n1, n2, diff
n1 = weight1[0]
n2 = weight2[0]
diff = n1 - n2
@@ -82,28 +85,28 @@ exports.isObject = _.isObject
exports.isArray = _.isArray
exports.isNumber = _.isNumber
-var ABS_URL = /^https?:\/\//
-exports.isUrlAbsolute = function (url) {
+const ABS_URL = /^https?:\/\//
+exports.isUrlAbsolute = (url) => {
return ABS_URL.test(url)
}
-exports.camelToSnake = function (camelCase) {
- return camelCase.replace(/[A-Z]/g, function (match, pos) {
+exports.camelToSnake = (camelCase) => {
+ return camelCase.replace(/[A-Z]/g, (match, pos) => {
return (pos > 0 ? '_' : '') + match.toLowerCase()
})
}
-exports.ucFirst = function (word) {
+exports.ucFirst = (word) => {
return word.charAt(0).toUpperCase() + word.substr(1)
}
-exports.dashToCamel = function (dash) {
- var words = dash.split('-')
+exports.dashToCamel = (dash) => {
+ const words = dash.split('-')
return words.shift() + words.map(exports.ucFirst).join('')
}
-exports.arrayRemove = function (collection, item) {
- var idx = collection.indexOf(item)
+exports.arrayRemove = (collection, item) => {
+ const idx = collection.indexOf(item)
if (idx !== -1) {
collection.splice(idx, 1)
@@ -114,15 +117,15 @@ exports.arrayRemove = function (collection, item) {
}
exports.merge = function () {
- var args = Array.prototype.slice.call(arguments, 0)
+ const args = Array.prototype.slice.call(arguments, 0)
args.unshift({})
return _.merge.apply({}, args)
}
-exports.formatTimeInterval = function (time) {
- var mins = Math.floor(time / 60000)
- var secs = (time - mins * 60000) / 1000
- var str = secs + (secs === 1 ? ' sec' : ' secs')
+exports.formatTimeInterval = (time) => {
+ const mins = Math.floor(time / 60000)
+ const secs = (time - mins * 60000) / 1000
+ let str = secs + (secs === 1 ? ' sec' : ' secs')
if (mins) {
str = mins + (mins === 1 ? ' min ' : ' mins ') + str
@@ -131,20 +134,20 @@ exports.formatTimeInterval = function (time) {
return str
}
-var replaceWinPath = function (path) {
+const replaceWinPath = (path) => {
return _.isString(path) ? path.replace(/\\/g, '/') : path
}
exports.normalizeWinPath = process.platform === 'win32' ? replaceWinPath : _.identity
-exports.mkdirIfNotExists = function mkdir (directory, done) {
+exports.mkdirIfNotExists = (directory, done) => {
// TODO(vojta): handle if it's a file
/* eslint-disable handle-callback-err */
- fs.stat(directory, function (err, stat) {
+ fs.stat(directory, (err, stat) => {
if (stat && stat.isDirectory()) {
done()
} else {
- mkdir(path.dirname(directory), function () {
+ exports.mkdirIfNotExists(path.dirname(directory), () => {
fs.mkdir(directory, done)
})
}
@@ -152,17 +155,17 @@ exports.mkdirIfNotExists = function mkdir (directory, done) {
/* eslint-enable handle-callback-err */
}
-exports.defer = function () {
- var resolve
- var reject
- var promise = new Promise(function () {
- resolve = arguments[0]
- reject = arguments[1]
+exports.defer = () => {
+ let res
+ let rej
+ const promise = new Promise((resolve, reject) => {
+ res = resolve
+ rej = reject
})
return {
- resolve: resolve,
- reject: reject,
+ resolve: res,
+ reject: rej,
promise: promise
}
}
diff --git a/lib/index.js b/lib/index.js
index 9d09178b0..4d906a437 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,20 +1,20 @@
-// index module
+'use strict'
-var constants = require('./constants')
-var Server = require('./server')
-var runner = require('./runner')
-var stopper = require('./stopper')
-var launcher = require('./launcher')
-var cfg = require('./config')
+const constants = require('./constants')
+const Server = require('./server')
+const runner = require('./runner')
+const stopper = require('./stopper')
+const launcher = require('./launcher')
+const cfg = require('./config')
// TODO: remove in 1.0
-var oldServer = {
+const oldServer = {
start: function (cliOptions, done) {
console.error('WARN `start` method is deprecated since 0.13. It will be removed in 0.14. Please use \n' +
' server = new Server(config, [done])\n' +
' server.start()\n' +
'instead.')
- var server = new Server(cliOptions, done)
+ const server = new Server(cliOptions, done)
server.start()
}
}
diff --git a/lib/init.js b/lib/init.js
index a257e05b2..f8fae6055 100755
--- a/lib/init.js
+++ b/lib/init.js
@@ -1,30 +1,26 @@
-var readline = require('readline')
-var path = require('path')
-var glob = require('glob')
-var mm = require('minimatch')
-var exec = require('child_process').exec
+'use strict'
-var helper = require('./helper')
-var logger = require('./logger')
+const readline = require('readline')
+const path = require('path')
+const glob = require('glob')
+const mm = require('minimatch')
+const exec = require('child_process').exec
-var log = logger.create('init')
+const helper = require('./helper')
+const logger = require('./logger')
-var StateMachine = require('./init/state_machine')
-var COLOR_SCHEME = require('./init/color_schemes')
-var formatters = require('./init/formatters')
+const log = logger.create('init')
+const logQueue = require('./init/log-queue')
+
+const StateMachine = require('./init/state_machine')
+const COLOR_SCHEME = require('./init/color_schemes')
+const formatters = require('./init/formatters')
// TODO(vojta): coverage
// TODO(vojta): html preprocessors
// TODO(vojta): SauceLabs
// TODO(vojta): BrowserStack
-var logQueue = []
-var printLogQueue = function () {
- while (logQueue.length) {
- logQueue.shift()()
- }
-}
-
var NODE_MODULES_DIR = path.resolve(__dirname, '../..')
// Karma is not in node_modules, probably a symlink,
@@ -33,7 +29,7 @@ if (!/node_modules$/.test(NODE_MODULES_DIR)) {
NODE_MODULES_DIR = path.resolve('node_modules')
}
-var installPackage = function (pkgName) {
+function installPackage (pkgName) {
// Do not install if already installed.
try {
require(NODE_MODULES_DIR + '/' + pkgName)
@@ -42,7 +38,7 @@ var installPackage = function (pkgName) {
log.debug('Missing plugin "%s". Installing...', pkgName)
- var options = {
+ const options = {
cwd: path.resolve(NODE_MODULES_DIR, '..')
}
@@ -65,22 +61,22 @@ var installPackage = function (pkgName) {
})
}
-var validatePattern = function (pattern) {
+function validatePattern (pattern) {
if (!glob.sync(pattern).length) {
log.warn('There is no file matching this pattern.\n')
}
}
-var validateBrowser = function (name) {
+function validateBrowser (name) {
// TODO(vojta): check if the path resolves to a binary
installPackage('karma-' + name.toLowerCase().replace('canary', '') + '-launcher')
}
-var validateFramework = function (name) {
+function validateFramework (name) {
installPackage('karma-' + name)
}
-var validateRequireJs = function (useRequire) {
+function validateRequireJs (useRequire) {
if (useRequire) {
validateFramework('requirejs')
}
@@ -127,9 +123,7 @@ var questions = [{
hint: 'This will generate test-main.js/coffee that configures RequireJS and starts the tests.',
options: ['no', 'yes'],
boolean: true,
- condition: function (answers) {
- return answers.requirejs
- }
+ condition: (answers) => answers.requirejs
}, {
id: 'includedFiles',
question: 'Which files do you want to include with