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