The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .codeclimate.yml
├── .eslintignore
├── .eslintrc.json
├── .gitattributes
├── .github
    ├── ISSUE_TEMPLATE
    │   └── bug_report.md
    └── workflows
    │   ├── lint.yml
    │   ├── release.yml
    │   └── test.yml
├── .gitignore
├── .mailmap
├── .npmignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── LICENSE
├── README.md
├── SECURITY.md
├── bin
    └── karma
├── client
    ├── .eslintrc
    ├── constants.js
    ├── karma.js
    ├── main.js
    └── updater.js
├── commitlint.config.js
├── common
    ├── stringify.js
    └── util.js
├── config.tpl.coffee
├── config.tpl.js
├── config.tpl.ls
├── config.tpl.ts
├── context
    ├── karma.js
    └── main.js
├── cucumber.js
├── docs
    ├── about
    │   ├── 01-versioning.md
    │   └── 03-migration.md
    ├── config
    │   ├── 01-configuration-file.md
    │   ├── 02-files.md
    │   ├── 03-browsers.md
    │   ├── 04-preprocessors.md
    │   └── 05-plugins.md
    ├── dev
    │   ├── 01-contributing.md
    │   ├── 02-making-changes.md
    │   ├── 03-maintaining.md
    │   ├── 04-public-api.md
    │   ├── 05-plugins.md
    │   └── 06-git-commit-msg.md
    ├── index.md
    ├── intro
    │   ├── 01-installation.md
    │   ├── 02-configuration.md
    │   ├── 03-how-it-works.md
    │   ├── 04-faq.md
    │   └── 05-troubleshooting.md
    └── plus
    │   ├── 01-requirejs.md
    │   ├── 02-travis.md
    │   ├── 03-jenkins.md
    │   ├── 04-semaphore.md
    │   ├── 05-cloud9.md
    │   ├── 06-angularjs.md
    │   ├── 07-yeoman.md
    │   ├── 08-emberjs.md
    │   ├── 09-codio.md
    │   └── 10-teamcity.md
├── lib
    ├── browser.js
    ├── browser_collection.js
    ├── browser_result.js
    ├── cli.js
    ├── completion.js
    ├── config.js
    ├── constants.js
    ├── detached.js
    ├── emitter_wrapper.js
    ├── events.js
    ├── executor.js
    ├── file-list.js
    ├── file.js
    ├── helper.js
    ├── index.js
    ├── init.js
    ├── init
    │   ├── color_schemes.js
    │   ├── formatters.js
    │   ├── log-queue.js
    │   └── state_machine.js
    ├── launcher.js
    ├── launchers
    │   ├── base.js
    │   ├── capture_timeout.js
    │   ├── process.js
    │   └── retry.js
    ├── logger.js
    ├── middleware
    │   ├── common.js
    │   ├── karma.js
    │   ├── proxy.js
    │   ├── runner.js
    │   ├── source_files.js
    │   ├── stopper.js
    │   └── strip_host.js
    ├── plugin.js
    ├── preprocessor.js
    ├── reporter.js
    ├── reporters
    │   ├── base.js
    │   ├── base_color.js
    │   ├── dots.js
    │   ├── dots_color.js
    │   ├── multi.js
    │   ├── progress.js
    │   └── progress_color.js
    ├── runner.js
    ├── server.js
    ├── stopper.js
    ├── temp_dir.js
    ├── url.js
    ├── utils
    │   ├── crypto-utils.js
    │   ├── dns-utils.js
    │   ├── file-utils.js
    │   ├── net-utils.js
    │   ├── path-utils.js
    │   └── pattern-utils.js
    ├── watcher.js
    └── web-server.js
├── logo
    ├── banner.png
    ├── favicon.ico
    ├── logo.ai
    ├── logo.eps
    ├── logo.png
    └── logo.svg
├── package-lock.json
├── package.json
├── release.config.js
├── requirejs.config.tpl.coffee
├── requirejs.config.tpl.js
├── scripts
    ├── client.js
    ├── integration-tests.sh
    └── karma-completion.sh
├── static
    ├── client.html
    ├── client_with_context.html
    ├── context.html
    ├── context.js
    ├── debug.html
    ├── debug.js
    ├── favicon.ico
    └── karma.js
├── test
    ├── .eslintrc
    ├── client
    │   ├── .eslintrc
    │   ├── browser-exceptions.log
    │   ├── karma.conf.js
    │   ├── karma.spec.js
    │   ├── mocks.js
    │   ├── stringify.spec.js
    │   └── util.spec.js
    ├── e2e
    │   ├── .eslintrc
    │   ├── basic.feature
    │   ├── browser_console.feature
    │   ├── cli.feature
    │   ├── custom-context.feature
    │   ├── displayname.feature
    │   ├── error.feature
    │   ├── files.feature
    │   ├── headers.feature
    │   ├── helpful-logs.feature
    │   ├── launcher-error.feature
    │   ├── load.feature
    │   ├── middleware.feature
    │   ├── mocharepoter.feature
    │   ├── module-types.feature
    │   ├── pass-opts.feature
    │   ├── proxy.feature
    │   ├── reconnecting.feature
    │   ├── reporting.feature
    │   ├── restart-on-change.feature
    │   ├── runInParent.feature
    │   ├── step_definitions
    │   │   ├── core_steps.js
    │   │   ├── hooks.js
    │   │   └── utils.js
    │   ├── stop.feature
    │   ├── support
    │   │   ├── basic
    │   │   │   ├── plus.js
    │   │   │   └── test.js
    │   │   ├── behind-proxy
    │   │   │   ├── plus.js
    │   │   │   └── test.js
    │   │   ├── browser-console
    │   │   │   ├── log.js
    │   │   │   └── test.js
    │   │   ├── context
    │   │   │   ├── context2.html
    │   │   │   └── test.js
    │   │   ├── env.js
    │   │   ├── error
    │   │   │   ├── import-something-from-somewhere.js
    │   │   │   ├── test.js
    │   │   │   └── under-test.js
    │   │   ├── files
    │   │   │   ├── log_foo.js
    │   │   │   └── test.js
    │   │   ├── headers
    │   │   │   ├── foo.js
    │   │   │   └── test.js
    │   │   ├── launcher-error
    │   │   │   ├── fake-browser.sh
    │   │   │   └── specs.js
    │   │   ├── middleware
    │   │   │   ├── middleware.js
    │   │   │   └── test.js
    │   │   ├── mocha
    │   │   │   ├── plus.js
    │   │   │   └── test.js
    │   │   ├── modules
    │   │   │   ├── __tests__
    │   │   │   │   ├── minus.test.mjs
    │   │   │   │   └── plus.test.js
    │   │   │   ├── minus.mjs
    │   │   │   └── plus.js
    │   │   ├── pass-opts
    │   │   │   └── test.js
    │   │   ├── proxy.js
    │   │   ├── proxy
    │   │   │   ├── .tern-port
    │   │   │   ├── foo.js
    │   │   │   ├── plugin.js
    │   │   │   └── test.js
    │   │   ├── reconnecting
    │   │   │   ├── plus.js
    │   │   │   └── test.js
    │   │   ├── reporting
    │   │   │   └── test.js
    │   │   ├── tag
    │   │   │   ├── tag.js
    │   │   │   ├── test-with-version.js
    │   │   │   └── test-without-version.js
    │   │   ├── timeout
    │   │   │   ├── fake-browser.sh
    │   │   │   └── specs.js
    │   │   └── world.js
    │   ├── tag.feature
    │   ├── timeout.feature
    │   └── upstream-proxy.feature
    ├── mocha.opts
    └── unit
    │   ├── browser.spec.js
    │   ├── browser_collection.spec.js
    │   ├── browser_result.spec.js
    │   ├── certificates
    │       ├── server.crt
    │       └── server.key
    │   ├── cli.spec.js
    │   ├── completion.spec.js
    │   ├── config.spec.js
    │   ├── emitter_wrapper.spec.js
    │   ├── events.spec.js
    │   ├── executor.spec.js
    │   ├── file-list.spec.js
    │   ├── file.spec.js
    │   ├── fixtures
    │       ├── format-error-property.js
    │       └── format-error-root.js
    │   ├── helper.spec.js
    │   ├── index.spec.js
    │   ├── init.spec.js
    │   ├── init
    │       ├── formatters.spec.js
    │       └── state_machine.spec.js
    │   ├── launcher.spec.js
    │   ├── launchers
    │       ├── base.spec.js
    │       ├── capture_timeout.spec.js
    │       ├── process.spec.js
    │       └── retry.spec.js
    │   ├── logger.spec.js
    │   ├── middleware
    │       ├── karma.spec.js
    │       ├── proxy.spec.js
    │       ├── runner.spec.js
    │       ├── source_files.spec.js
    │       └── strip_host.spec.js
    │   ├── mocha-globals.js
    │   ├── mocks
    │       └── timer.js
    │   ├── plugin.spec.js
    │   ├── preprocessor.spec.js
    │   ├── reporter.spec.js
    │   ├── reporters
    │       ├── base.spec.js
    │       └── progress.spec.js
    │   ├── runner.spec.js
    │   ├── server.spec.js
    │   ├── url.spec.js
    │   ├── utils
    │       ├── crypto-utils.spec.js
    │       ├── net-utils.spec.js
    │       ├── path-utils.spec.js
    │       └── pattern-utils.spec.js
    │   ├── watcher.spec.js
    │   └── web-server.spec.js
├── thesis.pdf
├── tools
    ├── update-contributors.js
    └── update-docs.js
└── wallaby.js


/.codeclimate.yml:
--------------------------------------------------------------------------------
 1 | ---
 2 | engines:
 3 |   rubocop:
 4 |     enabled: false
 5 |   coffeelint:
 6 |     enabled: true
 7 |   eslint:
 8 |     enabled: true
 9 |   csslint:
10 |     enabled: true
11 | ratings:
12 |   paths:
13 |   - "**.coffee"
14 |   - "**.js"
15 |   - "**.css"
16 | exclude_paths:
17 | - node_modules/**/*
18 | - test/**/*
19 | 


--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | test/e2e/support/sandbox
2 | test/e2e/support/error/under-test.js
3 | test/unit/fixtures/bundled.js
4 | static/karma.js
5 | static/context.js
6 | 


--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 |   "extends": "standard",
3 |   "rules": {
4 |     "arrow-parens": [2, "always"],
5 |     "space-before-function-paren": ["error", "always"]
6 |   }
7 | }
8 | 


--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
 1 | # See https://help.github.com/articles/dealing-with-line-endings
 2 | 
 3 | # By default, normalize all files to unix line endings when commiting.
 4 | * text
 5 | 
 6 | # Denote all files that are truly binary and should not be modified.
 7 | *.png binary
 8 | *.jpg binary
 9 | *.pdf binary
10 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Bug report
 3 | about: Create an actionable bug report
 4 | title: ''
 5 | labels: ''
 6 | assignees: ''
 7 | 
 8 | ---
 9 | 
10 | Please read https://karma-runner.github.io/4.0/intro/troubleshooting.html first
11 | 


--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
 1 | name: Lint
 2 | 
 3 | on:
 4 |   pull_request:
 5 |     branches:
 6 |       - master
 7 | 
 8 | jobs:
 9 |   linux:
10 |     name: Linux - Lint
11 |     runs-on: ubuntu-latest
12 |     steps:
13 |       - uses: actions/checkout@v2
14 |         with:
15 |           fetch-depth: 0
16 |       - uses: actions/setup-node@v2
17 |         with:
18 |           node-version: 16
19 |           cache: npm
20 |       - run: npm ci
21 |       - run: |
22 |           npm run commitlint -- \
23 |           --verbose \
24 |           --from `git merge-base origin/master $GITHUB_SHA`
25 |       - run: npm run lint
26 | 


--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
 1 | name: Release
 2 | 
 3 | on:
 4 |   push:
 5 |     branches:
 6 |       - master
 7 | 
 8 | jobs:
 9 |   main:
10 |     name: Test, Tag Commit and Release to NPM
11 |     runs-on: ubuntu-latest
12 |     env:
13 |       BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
14 |       BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
15 |       SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
16 |       SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
17 |       NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
18 |       GITHUB_TOKEN: ${{ secrets.KARMARUNNERBOT_GITHUB_TOKEN }}
19 |       KARMA_TEST_NO_FALLBACK: 1
20 |     steps:
21 |       - uses: actions/checkout@v2
22 |         with:
23 |           token: ${{ env.GITHUB_TOKEN }}
24 |       - uses: actions/setup-node@v2
25 |         with:
26 |           node-version: 16
27 |           cache: npm
28 |       - run: npm ci
29 |       - run: npm run lint
30 |       - run: npm run build:check
31 |       - run: npm run test:unit
32 |       - run: npm run test:e2e
33 |       - run: npm run test:client
34 |       - run: npm run test:integration
35 |       - run: npm run semantic-release
36 | 


--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
 1 | name: Test
 2 | 
 3 | on:
 4 |   pull_request:
 5 |     branches:
 6 |       - master
 7 | 
 8 | jobs:
 9 |   main:
10 |     name: Unit (Client and Server), E2E and Integration Test
11 |     runs-on: ubuntu-latest
12 |     env:
13 |       BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
14 |       BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
15 |       SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
16 |       SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
17 |     steps:
18 |       - uses: actions/checkout@v2
19 |         with:
20 |           fetch-depth: 0
21 |       - uses: actions/setup-node@v2
22 |         with:
23 |           node-version: 16
24 |           cache: npm
25 |       - run: npm ci
26 |       - run: npm run build:check
27 |       - run: npm run test:unit
28 |       - run: npm run test:e2e
29 |       - run: npm run test:client
30 |       - run: npm run test:integration
31 |   linux:
32 |     name: "Node ${{ matrix.node }} on Linux: Server Unit and E2E Test"
33 |     runs-on: ubuntu-latest
34 |     strategy:
35 |       matrix:
36 |         node: [10, 12, 14, 18]
37 |     steps:
38 |       - uses: actions/checkout@v2
39 |       - uses: actions/setup-node@v2
40 |         with:
41 |           node-version: ${{ matrix.node }}
42 |           cache: npm
43 |       - run: npm ci
44 |       - run: npm run test:unit
45 |       - run: npm run test:e2e
46 |   windows:
47 |     name: "Node ${{ matrix.node }} on Windows: Server Unit and Client Unit Test"
48 |     runs-on: windows-latest
49 |     strategy:
50 |       matrix:
51 |         node: [10, 12, 14, 16, 18]
52 |     steps:
53 |       - uses: actions/checkout@v2
54 |       - uses: actions/setup-node@v2
55 |         with:
56 |           node-version: ${{ matrix.node }}
57 |           cache: npm
58 |       - run: npm ci
59 |       - run: npm run test:unit
60 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | node_modules
 2 | npm-debug.log
 3 | .idea/*
 4 | *.iml
 5 | docs/_build
 6 | *.swp
 7 | *.swo
 8 | test/e2e/support/sandbox
 9 | test/e2e/coverage/coverage
10 | test/e2e/coverageQunit/coverage
11 | test/e2e/coverageRequirejs/coverage
12 | test/e2e/coffee-coverage/coverage
13 | test-results.xml
14 | test/unit/test.log
15 | test/unit/fixtures/bundled.js
16 | .DS_Store
17 | 


--------------------------------------------------------------------------------
/.mailmap:
--------------------------------------------------------------------------------
1 | <bitwiseman@gmail.com> <lnewman@book.com>
2 | <vojta.jina@gmail.com> <vojta@google.com>
3 | <friedel.ziegelmayer@gmail.com> <dignifiedquire@gmail.com>
4 | Michał Gołębiowski-Owczarek <m.goleb@gmail.com>
5 | 


--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
 1 | .*
 2 | *.tgz
 3 | 
 4 | tmp
 5 | test
 6 | tasks
 7 | /tools/
 8 | docs
 9 | client
10 | logo
11 | integration-tests
12 | 
13 | TODO.md
14 | CONTRIBUTING.md
15 | Gruntfile.coffee
16 | credentials
17 | Karma.sublime-*
18 | 
19 | static/karma.src.js
20 | static/karma.wrapper
21 | test-results.xml
22 | thesis.pdf
23 | mocha-watch.sh
24 | mocha-watch-debug.sh
25 | 


--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
 1 | # Contributor Code of Conduct
 2 | 
 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
 4 | 
 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
 6 | 
 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
 8 | 
 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10 | 
11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12 | 
13 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.0.0, available at <https://www.contributor-covenant.org/version/1/0/0/code-of-conduct/>
14 | 


--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
 1 | # Contributing to Karma
 2 | If you are thinking about making Karma better, or you just want to hack on it, that’s great!
 3 | 
 4 | > Check out the docs on how to [get started][docs_contributing] and please follow
 5 | > the [code of conduct](CODE_OF_CONDUCT.md).
 6 | 
 7 | 
 8 | ## Got a Question or Problem?
 9 | 
10 | If you have questions about how to use Karma, please direct these to the [Gitter][gitter]
11 | discussion list or [Stack Overflow][stackoverflow].
12 | 
13 | ## Found an Issue?
14 | If you find a bug in the source code or a mistake in the documentation, you can help us by
15 | submitting an issue to our [GitHub Repository][github_newissue]. Even better you can submit a Pull Request
16 | with a fix.
17 | 
18 | **Working on your first Pull Request?** You can learn how from this *free* series
19 | [How to Contribute to an Open Source Project on GitHub][egghead_series]
20 | 
21 | [docs_contributing]: https://karma-runner.github.io/latest/dev/contributing.html
22 | [gitter]: https://gitter.im/karma-runner/karma
23 | [stackoverflow]: https://stackoverflow.com/questions/tagged/karma-runner
24 | [github_newissue]: https://github.com/karma-runner/karma/issues/new
25 | [egghead_series]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github
26 | 


--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
 1 | ### Expected behaviour
 2 | 
 3 | ### Actual behaviour
 4 | 
 5 | ### Environment Details
 6 | 
 7 | - Karma version (output of `karma --version`):
 8 | - Relevant part of your `karma.config.js` file
 9 | 
10 | ### Steps to reproduce the behaviour
11 | 
12 | 1.
13 | 2.
14 | 3.
15 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License
 2 | 
 3 | Copyright (C) 2011-2021 Google, Inc.
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
 6 | this software and associated documentation files (the "Software"), to deal in
 7 | the Software without restriction, including without limitation the rights to
 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 9 | of the Software, and to permit persons to whom the Software is furnished to do
10 | so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 | 


--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
 1 | # Security Policy
 2 | 
 3 | ## Supported Versions
 4 | 
 5 | Only the latest version of the project are currently being supported with security updates.
 6 | 
 7 | ## Reporting a Vulnerability
 8 | 
 9 | To report a security issue, please email karma-runner-eng+security@google.com
10 | with a description of the issue, the steps you took to create the issue,
11 | affected versions, and if known, mitigations for the issue.
12 | 


--------------------------------------------------------------------------------
/bin/karma:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 
3 | require('../lib/cli').run();
4 | 


--------------------------------------------------------------------------------
/client/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "env": {
3 |     "browser": true
4 |   }
5 | }
6 | 


--------------------------------------------------------------------------------
/client/constants.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |   VERSION: '%KARMA_VERSION%',
3 |   KARMA_URL_ROOT: '%KARMA_URL_ROOT%',
4 |   KARMA_PROXY_PATH: '%KARMA_PROXY_PATH%',
5 |   BROWSER_SOCKET_TIMEOUT: '%BROWSER_SOCKET_TIMEOUT%',
6 |   CONTEXT_URL: 'context.html'
7 | }
8 | 


--------------------------------------------------------------------------------
/client/main.js:
--------------------------------------------------------------------------------
 1 | /* global io */
 2 | /* eslint-disable no-new */
 3 | 
 4 | var Karma = require('./karma')
 5 | var StatusUpdater = require('./updater')
 6 | var util = require('../common/util')
 7 | var constants = require('./constants')
 8 | 
 9 | var KARMA_URL_ROOT = constants.KARMA_URL_ROOT
10 | var KARMA_PROXY_PATH = constants.KARMA_PROXY_PATH
11 | var BROWSER_SOCKET_TIMEOUT = constants.BROWSER_SOCKET_TIMEOUT
12 | 
13 | // Connect to the server using socket.io https://socket.io/
14 | var socket = io(location.host, {
15 |   reconnectionDelay: 500,
16 |   reconnectionDelayMax: Infinity,
17 |   timeout: BROWSER_SOCKET_TIMEOUT,
18 |   path: KARMA_PROXY_PATH + KARMA_URL_ROOT.slice(1) + 'socket.io',
19 |   'sync disconnect on unload': true,
20 |   useNativeTimers: true
21 | })
22 | 
23 | // instantiate the updater of the view
24 | var updater = new StatusUpdater(socket, util.elm('title'), util.elm('banner'), util.elm('browsers'))
25 | window.karma = new Karma(updater, socket, util.elm('context'), window.open,
26 |   window.navigator, window.location, window.document)
27 | 


--------------------------------------------------------------------------------
/client/updater.js:
--------------------------------------------------------------------------------
 1 | var VERSION = require('./constants').VERSION
 2 | 
 3 | function StatusUpdater (socket, titleElement, bannerElement, browsersElement) {
 4 |   function updateBrowsersInfo (browsers) {
 5 |     if (!browsersElement) {
 6 |       return
 7 |     }
 8 |     var status
 9 | 
10 |     // clear browsersElement
11 |     while (browsersElement.firstChild) {
12 |       browsersElement.removeChild(browsersElement.firstChild)
13 |     }
14 | 
15 |     for (var i = 0; i < browsers.length; i++) {
16 |       status = browsers[i].isConnected ? 'idle' : 'executing'
17 |       var li = document.createElement('li')
18 |       li.setAttribute('class', status)
19 |       li.textContent = browsers[i].name + ' is ' + status
20 |       browsersElement.appendChild(li)
21 |     }
22 |   }
23 | 
24 |   var connectionText = 'never-connected'
25 |   var testText = 'loading'
26 |   var pingText = ''
27 | 
28 |   function updateBanner () {
29 |     if (!titleElement || !bannerElement) {
30 |       return
31 |     }
32 |     titleElement.textContent = 'Karma v ' + VERSION + ' - ' + connectionText + '; test: ' + testText + '; ' + pingText
33 |     bannerElement.className = connectionText === 'connected' ? 'online' : 'offline'
34 |   }
35 | 
36 |   function updateConnectionStatus (connectionStatus) {
37 |     connectionText = connectionStatus || connectionText
38 |     updateBanner()
39 |   }
40 |   function updateTestStatus (testStatus) {
41 |     testText = testStatus || testText
42 |     updateBanner()
43 |   }
44 |   function updatePingStatus (pingStatus) {
45 |     pingText = pingStatus || pingText
46 |     updateBanner()
47 |   }
48 | 
49 |   socket.on('connect', function () {
50 |     updateConnectionStatus('connected')
51 |   })
52 |   socket.on('disconnect', function () {
53 |     updateConnectionStatus('disconnected')
54 |   })
55 |   socket.on('reconnecting', function (sec) {
56 |     updateConnectionStatus('reconnecting in ' + sec + ' seconds')
57 |   })
58 |   socket.on('reconnect', function () {
59 |     updateConnectionStatus('reconnected')
60 |   })
61 |   socket.on('reconnect_failed', function () {
62 |     updateConnectionStatus('reconnect_failed')
63 |   })
64 | 
65 |   socket.on('info', updateBrowsersInfo)
66 |   socket.on('disconnect', function () {
67 |     updateBrowsersInfo([])
68 |   })
69 | 
70 |   socket.on('ping', function () {
71 |     updatePingStatus('ping...')
72 |   })
73 |   socket.on('pong', function (latency) {
74 |     updatePingStatus('ping ' + latency + 'ms')
75 |   })
76 | 
77 |   return { updateTestStatus: updateTestStatus }
78 | }
79 | 
80 | module.exports = StatusUpdater
81 | 


--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = { extends: ['@commitlint/config-angular'] }
2 | 


--------------------------------------------------------------------------------
/common/stringify.js:
--------------------------------------------------------------------------------
  1 | var serialize = null
  2 | try {
  3 |   serialize = require('dom-serialize')
  4 | } catch (e) {
  5 |   // Ignore failure on IE8
  6 | }
  7 | 
  8 | var instanceOf = require('./util').instanceOf
  9 | 
 10 | function isNode (obj) {
 11 |   return (obj.tagName || obj.nodeName) && obj.nodeType
 12 | }
 13 | 
 14 | function stringify (obj, depth) {
 15 |   if (depth === 0) {
 16 |     return '...'
 17 |   }
 18 | 
 19 |   if (obj === null) {
 20 |     return 'null'
 21 |   }
 22 | 
 23 |   switch (typeof obj) {
 24 |     case 'symbol':
 25 |       return obj.toString()
 26 |     case 'string':
 27 |       return "'" + obj + "'"
 28 |     case 'undefined':
 29 |       return 'undefined'
 30 |     case 'function':
 31 |       try {
 32 |         // function abc(a, b, c) { /* code goes here */ }
 33 |         //   -> function abc(a, b, c) { ... }
 34 |         return obj.toString().replace(/\{[\s\S]*\}/, '{ ... }')
 35 |       } catch (err) {
 36 |         if (err instanceof TypeError) {
 37 |           // Support older browsers
 38 |           return 'function ' + (obj.name || '') + '() { ... }'
 39 |         } else {
 40 |           throw err
 41 |         }
 42 |       }
 43 |     case 'boolean':
 44 |       return obj ? 'true' : 'false'
 45 |     case 'object':
 46 |       var strs = []
 47 |       if (instanceOf(obj, 'Array')) {
 48 |         strs.push('[')
 49 |         for (var i = 0, ii = obj.length; i < ii; i++) {
 50 |           if (i) {
 51 |             strs.push(', ')
 52 |           }
 53 |           strs.push(stringify(obj[i], depth - 1))
 54 |         }
 55 |         strs.push(']')
 56 |       } else if (instanceOf(obj, 'Date')) {
 57 |         return obj.toString()
 58 |       } else if (instanceOf(obj, 'Text')) {
 59 |         return obj.nodeValue
 60 |       } else if (instanceOf(obj, 'Comment')) {
 61 |         return '<!--' + obj.nodeValue + '-->'
 62 |       } else if (obj.outerHTML) {
 63 |         return obj.outerHTML
 64 |       } else if (isNode(obj)) {
 65 |         if (serialize) {
 66 |           return serialize(obj)
 67 |         } else {
 68 |           return 'Skipping stringify, no support for dom-serialize'
 69 |         }
 70 |       } else if (instanceOf(obj, 'Error')) {
 71 |         return obj.toString() + '\n' + obj.stack
 72 |       } else {
 73 |         var constructor = 'Object'
 74 |         if (obj.constructor && typeof obj.constructor === 'function') {
 75 |           constructor = obj.constructor.name
 76 |         }
 77 | 
 78 |         strs.push(constructor)
 79 |         strs.push('{')
 80 |         var first = true
 81 |         for (var key in obj) {
 82 |           if (Object.prototype.hasOwnProperty.call(obj, key)) {
 83 |             if (first) {
 84 |               first = false
 85 |             } else {
 86 |               strs.push(', ')
 87 |             }
 88 | 
 89 |             strs.push(key + ': ' + stringify(obj[key], depth - 1))
 90 |           }
 91 |         }
 92 |         strs.push('}')
 93 |       }
 94 |       return strs.join('')
 95 |     default:
 96 |       return obj
 97 |   }
 98 | }
 99 | 
100 | module.exports = stringify
101 | 


--------------------------------------------------------------------------------
/common/util.js:
--------------------------------------------------------------------------------
 1 | exports.instanceOf = function (value, constructorName) {
 2 |   return Object.prototype.toString.apply(value) === '[object ' + constructorName + ']'
 3 | }
 4 | 
 5 | exports.elm = function (id) {
 6 |   return document.getElementById(id)
 7 | }
 8 | 
 9 | exports.generateId = function (prefix) {
10 |   return prefix + Math.floor(Math.random() * 10000)
11 | }
12 | 
13 | exports.isUndefined = function (value) {
14 |   return typeof value === 'undefined'
15 | }
16 | 
17 | exports.isDefined = function (value) {
18 |   return !exports.isUndefined(value)
19 | }
20 | 
21 | exports.parseQueryParams = function (locationSearch) {
22 |   var params = {}
23 |   var pairs = locationSearch.slice(1).split('&')
24 |   var keyValue
25 | 
26 |   for (var i = 0; i < pairs.length; i++) {
27 |     keyValue = pairs[i].split('=')
28 |     params[decodeURIComponent(keyValue[0])] = decodeURIComponent(keyValue[1])
29 |   }
30 | 
31 |   return params
32 | }
33 | 


--------------------------------------------------------------------------------
/config.tpl.coffee:
--------------------------------------------------------------------------------
 1 | # Karma configuration
 2 | # Generated on %DATE%
 3 | 
 4 | module.exports = (config) ->
 5 |   config.set
 6 | 
 7 |     # base path that will be used to resolve all patterns (eg. files, exclude)
 8 |     basePath: '%BASE_PATH%'
 9 | 
10 | 
11 |     # frameworks to use
12 |     # available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
13 |     frameworks: [%FRAMEWORKS%]
14 | 
15 | 
16 |     # list of files / patterns to load in the browser
17 |     files: [%FILES%
18 |     ]
19 | 
20 | 
21 |     # list of files / patterns to exclude
22 |     exclude: [%EXCLUDE%
23 |     ]
24 | 
25 | 
26 |     # preprocess matching files before serving them to the browser
27 |     # available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
28 |     preprocessors: {%PREPROCESSORS%
29 |     }
30 | 
31 | 
32 |     # test results reporter to use
33 |     # possible values: 'dots', 'progress'
34 |     # available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
35 |     reporters: ['progress']
36 | 
37 | 
38 |     # web server port
39 |     port: 9876
40 | 
41 | 
42 |     # enable / disable colors in the output (reporters and logs)
43 |     colors: true
44 | 
45 | 
46 |     # level of logging
47 |     # possible values:
48 |     # - config.LOG_DISABLE
49 |     # - config.LOG_ERROR
50 |     # - config.LOG_WARN
51 |     # - config.LOG_INFO
52 |     # - config.LOG_DEBUG
53 |     logLevel: config.LOG_INFO
54 | 
55 | 
56 |     # enable / disable watching file and executing tests whenever any file changes
57 |     autoWatch: %AUTO_WATCH%
58 | 
59 | 
60 |     # start these browsers
61 |     # available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
62 |     browsers: [%BROWSERS%]
63 | 
64 | 
65 |     # Continuous Integration mode
66 |     # if true, Karma captures browsers, runs the tests and exits
67 |     singleRun: false
68 | 
69 |     # Concurrency level
70 |     # how many browser instances should be started simultaneously
71 |     concurrency: Infinity
72 | 


--------------------------------------------------------------------------------
/config.tpl.js:
--------------------------------------------------------------------------------
 1 | // Karma configuration
 2 | // Generated on %DATE%
 3 | 
 4 | module.exports = function(config) {
 5 |   config.set({
 6 | 
 7 |     // base path that will be used to resolve all patterns (eg. files, exclude)
 8 |     basePath: '%BASE_PATH%',
 9 | 
10 | 
11 |     // frameworks to use
12 |     // available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
13 |     frameworks: [%FRAMEWORKS%],
14 | 
15 | 
16 |     // list of files / patterns to load in the browser
17 |     files: [%FILES%
18 |     ],
19 | 
20 | 
21 |     // list of files / patterns to exclude
22 |     exclude: [%EXCLUDE%
23 |     ],
24 | 
25 | 
26 |     // preprocess matching files before serving them to the browser
27 |     // available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
28 |     preprocessors: {%PREPROCESSORS%
29 |     },
30 | 
31 | 
32 |     // test results reporter to use
33 |     // possible values: 'dots', 'progress'
34 |     // available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
35 |     reporters: ['progress'],
36 | 
37 | 
38 |     // web server port
39 |     port: 9876,
40 | 
41 | 
42 |     // enable / disable colors in the output (reporters and logs)
43 |     colors: true,
44 | 
45 | 
46 |     // level of logging
47 |     // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
48 |     logLevel: config.LOG_INFO,
49 | 
50 | 
51 |     // enable / disable watching file and executing tests whenever any file changes
52 |     autoWatch: %AUTO_WATCH%,
53 | 
54 | 
55 |     // start these browsers
56 |     // available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
57 |     browsers: [%BROWSERS%],
58 | 
59 | 
60 |     // Continuous Integration mode
61 |     // if true, Karma captures browsers, runs the tests and exits
62 |     singleRun: false,
63 | 
64 |     // Concurrency level
65 |     // how many browser instances should be started simultaneously
66 |     concurrency: Infinity
67 |   })
68 | }
69 | 


--------------------------------------------------------------------------------
/config.tpl.ls:
--------------------------------------------------------------------------------
 1 | # Karma configuration
 2 | # Generated on %DATE%
 3 | 
 4 | module.exports = (config) ->
 5 |   config.set do
 6 | 
 7 |     # base path that will be used to resolve all patterns (eg. files, exclude)
 8 |     basePath: '%BASE_PATH%'
 9 | 
10 | 
11 |     # frameworks to use
12 |     # available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
13 |     frameworks: [%FRAMEWORKS%]
14 | 
15 | 
16 |     # list of files / patterns to load in the browser
17 |     files: [%FILES%
18 |     ]
19 | 
20 | 
21 |     # list of files / patterns to exclude
22 |     exclude: [%EXCLUDE%
23 |     ]
24 | 
25 | 
26 |     # preprocess matching files before serving them to the browser
27 |     # available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
28 |     preprocessors: {%PREPROCESSORS%
29 |     }
30 | 
31 |     # test results reporter to use
32 |     # possible values: 'dots', 'progress'
33 |     # available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
34 |     reporters: ['progress']
35 | 
36 | 
37 |     # web server port
38 |     port: 9876
39 | 
40 | 
41 |     # enable / disable colors in the output (reporters and logs)
42 |     colors: true
43 | 
44 | 
45 |     # level of logging
46 |     # possible values:
47 |     # - config.LOG_DISABLE
48 |     # - config.LOG_ERROR
49 |     # - config.LOG_WARN
50 |     # - config.LOG_INFO
51 |     # - config.LOG_DEBUG
52 |     logLevel: config.LOG_INFO
53 | 
54 | 
55 |     # enable / disable watching file and executing tests whenever any file changes
56 |     autoWatch: %AUTO_WATCH%
57 | 
58 | 
59 |     # satart these browsers
60 |     # available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
61 |     browsers: [%BROWSERS%]
62 | 
63 | 
64 |     # Continuous Integration mode
65 |     # if true, Karma captures browsers, runs the tests and exits
66 |     singleRun: false
67 | 
68 |     # Concurrency level
69 |     # how many browser instances should be started simultaneously
70 |     concurrency: Infinity
71 | 


--------------------------------------------------------------------------------
/config.tpl.ts:
--------------------------------------------------------------------------------
 1 | // Karma configuration
 2 | // Generated on %DATE%
 3 | 
 4 | module.exports = (config) => {
 5 |   config.set({
 6 | 
 7 |     // base path that will be used to resolve all patterns (eg. files, exclude)
 8 |     basePath: '%BASE_PATH%',
 9 | 
10 | 
11 |     // frameworks to use
12 |     // available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
13 |     frameworks: [%FRAMEWORKS%],
14 | 
15 | 
16 |     // list of files / patterns to load in the browser
17 |     files: [%FILES%
18 |     ],
19 | 
20 | 
21 |     // list of files / patterns to exclude
22 |     exclude: [%EXCLUDE%
23 |     ],
24 | 
25 | 
26 |     // preprocess matching files before serving them to the browser
27 |     // available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
28 |     preprocessors: {%PREPROCESSORS%
29 |     },
30 | 
31 | 
32 |     // test results reporter to use
33 |     // possible values: 'dots', 'progress'
34 |     // available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
35 |     reporters: ['progress'],
36 | 
37 | 
38 |     // web server port
39 |     port: 9876,
40 | 
41 | 
42 |     // enable / disable colors in the output (reporters and logs)
43 |     colors: true,
44 | 
45 | 
46 |     // level of logging
47 |     // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
48 |     logLevel: config.LOG_INFO,
49 | 
50 | 
51 |     // enable / disable watching file and executing tests whenever any file changes
52 |     autoWatch: %AUTO_WATCH%,
53 | 
54 | 
55 |     // start these browsers
56 |     // available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
57 |     browsers: [%BROWSERS%],
58 | 
59 | 
60 |     // Continuous Integration mode
61 |     // if true, Karma captures browsers, runs the tests and exits
62 |     singleRun: false,
63 | 
64 |     // Concurrency level
65 |     // how many browser instances should be started simultaneously
66 |     concurrency: Infinity
67 |   })
68 | }
69 | 


--------------------------------------------------------------------------------
/context/main.js:
--------------------------------------------------------------------------------
 1 | // Load in our dependencies
 2 | var ContextKarma = require('./karma')
 3 | 
 4 | // Resolve our parent window
 5 | var parentWindow = window.opener || window.parent
 6 | 
 7 | // Define a remote call method for Karma
 8 | var callParentKarmaMethod = ContextKarma.getDirectCallParentKarmaMethod(parentWindow)
 9 | 
10 | // If we don't have access to the window, then use `postMessage`
11 | // DEV: In Electron, we don't have access to the parent window due to it being in a separate process
12 | // DEV: We avoid using this in Internet Explorer as they only support strings
13 | //   https://caniuse.com/?search=postmessage
14 | var haveParentAccess = false
15 | try { haveParentAccess = !!parentWindow.window } catch (err) { /* Ignore errors (likely permission errors) */ }
16 | if (!haveParentAccess) {
17 |   callParentKarmaMethod = ContextKarma.getPostMessageCallParentKarmaMethod(parentWindow)
18 | }
19 | 
20 | // Define a window-scoped Karma
21 | window.__karma__ = new ContextKarma(callParentKarmaMethod)
22 | 


--------------------------------------------------------------------------------
/cucumber.js:
--------------------------------------------------------------------------------
 1 | // Shared configuration for Cucumber.js tests.
 2 | // See https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#profiles
 3 | const options = [
 4 |   '--format progress',
 5 |   '--require test/e2e/support/env.js',
 6 |   '--require test/e2e/support/world.js',
 7 |   '--require test/e2e/step_definitions/core_steps.js',
 8 |   '--require test/e2e/step_definitions/hooks.js'
 9 | ]
10 | 
11 | module.exports = {
12 |   default: options.join(' ')
13 | }
14 | 


--------------------------------------------------------------------------------
/docs/about/01-versioning.md:
--------------------------------------------------------------------------------
 1 | Karma uses [Semantic Versioning].
 2 | 
 3 | It is recommended that you add Karma by running:
 4 | 
 5 | ```bash
 6 | $ yarn add --dev karma
 7 | ```
 8 | 
 9 | or: 
10 | 
11 | ```bash
12 | $ npm --save-dev install karma
13 | ```
14 | 
15 | [Semantic Versioning]: https://semver.org/
16 | 


--------------------------------------------------------------------------------
/docs/about/03-migration.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | pageTitle: Migration from v0.10
 3 | ---
 4 | 
 5 | The good thing is that you don't have to migrate everything at once.
 6 | You can leave all the existing projects using an older version of Karma and only use the latest
 7 | version for the new projects. Alternatively, you can migrate the existing projects one at a time...
 8 | 
 9 | 
10 | Anyway, this migration should be easy ;-) so let's get started...
11 | 
12 | ```bash
13 | cd <path-to-your-project>
14 | npm install karma --save-dev
15 | ```
16 | This will install the latest version of Karma and also update `package.json` of your project.
17 | 
18 | 
19 | ## Install missing plugins
20 | Karma does not ship with any "default" plugins anymore.
21 | For existing projects, this should not cause any problems as npm (when updating Karma to 0.10 using
22 | `npm install karma --save-dev`) added these "default" plugins into `package.json` as regular dependencies.
23 | For new projects, just remember you have to install all the plugins you need. These are the "default" plugins that were removed:
24 | - karma-jasmine
25 | - karma-requirejs
26 | - karma-coffee-preprocessor
27 | - karma-html2js-preprocessor
28 | - karma-chrome-launcher
29 | - karma-firefox-launcher
30 | - karma-phantomjs-launcher
31 | - karma-script-launcher
32 | 
33 | 
34 | ## Install CLI interface
35 | Karma does not put the `karma` command in your system PATH anymore.
36 | If you want to use the `karma` command, please install the command line interface (`karma-cli`).
37 | 
38 | You probably have the `karma` package installed globally, in which case you should remove it first:
39 | ```bash
40 | npm remove -g karma
41 | ```
42 | 
43 | And then install the command line interface:
44 | ```bash
45 | npm install -g karma-cli
46 | ```
47 | 
48 | 
49 | ## Default configuration
50 | `autoWatch` is true by default, so if you don't wanna use it make sure you set it to `false`.
51 | But hey, give it a shot first, it's really awesome to run your tests on every save!
52 | 
53 | 
54 | ## npm complaining
55 | In some cases, npm can run into dependency tree issues during the migration process. If you are faced with an "unsatisfied peer dependency" error, removing all of the packages (`rm -rf ./node_modules`) and installing them again should clear up the issue.
56 | 
57 | If you have any other issues, please ask on the [mailing list].
58 | 
59 | 
60 | [mailing list]: https://groups.google.com/forum/?fromgroups#!forum/karma-users
61 | 


--------------------------------------------------------------------------------
/docs/config/05-plugins.md:
--------------------------------------------------------------------------------
 1 | Karma can be easily extended through plugins. In fact, all the existing preprocessors, reporters, browser launchers and frameworks are plugins. 
 2 | 
 3 | You can install [existing plugins] from npm or you can write [your own plugins][developing plugins] for Karma.
 4 | 
 5 | ## Installing Plugins
 6 | 
 7 | The recommended way to install plugins is to add them as project dependencies in your `package.json`:
 8 | 
 9 | ```json
10 | {
11 |   "devDependencies": {
12 |     "karma": "~0.10",
13 |     "karma-mocha": "~0.0.1",
14 |     "karma-growl-reporter": "~0.0.1",
15 |     "karma-firefox-launcher": "~0.0.1"
16 |   }
17 | }
18 | ```
19 | 
20 | Therefore, a simple way to install a plugin is:
21 | 
22 | ```bash
23 | npm install karma-<plugin name> --save-dev
24 | ```
25 | 
26 | ## Loading Plugins
27 | 
28 | By default, Karma loads plugins from all sibling npm packages which have a name starting with `karma-*`.
29 | 
30 | You can also override this behavior and explicitly list plugins you want to load via the `plugins` configuration setting:
31 | 
32 | ```javascript
33 | config.set({
34 |   plugins: [
35 |     // Load a plugin you installed from npm.
36 |     require('karma-jasmine'),
37 | 
38 |     // Load a plugin from the file in your project.
39 |     require('./my-custom-plugin'),
40 |   
41 |     // Define a plugin inline.
42 |     { 'framework:xyz': ['factory', factoryFn] },
43 | 
44 |     // Specify a module name or path which Karma will require() and load its 
45 |     // default export as a plugin.
46 |     'karma-chrome-launcher',
47 |     './my-fancy-plugin'
48 |   ]
49 | })
50 | ```
51 | 
52 | ## Activating Plugins
53 | 
54 | Adding a plugin to the `plugins` array only makes Karma aware of the plugin, but it does not activate it. Depending on the plugin type you'll need to add a plugin name into `frameworks`, `reporters`, `preprocessors`, `middleware` or `browsers` configuration key to activate it. For the detailed information refer to the corresponding plugin documentation or check out [Developing plugins][developing plugins] guide for more in-depth explanation of how plugins work.
55 | 
56 | [existing plugins]: https://www.npmjs.com/search?q=keywords:karma-plugin
57 | [developing plugins]: ../dev/plugins.html
58 | 


--------------------------------------------------------------------------------
/docs/dev/01-contributing.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | pageTitle: Contributing to Karma
 3 | ---
 4 | 
 5 | **Working on your first Pull Request?** You can learn how from this *free* series
 6 | [How to Contribute to an Open Source Project on GitHub]
 7 | 
 8 | You want to contribute to Karma? That is truly great!
 9 | Here are some tips to get you started...
10 | 
11 | ### Help others
12 | The best way to start contributing to any open source project is to help other people.
13 | You can answer questions on the [Gitter] or [Stack Overflow].
14 | Either find something you already know the answer for, or something you feel interested in and
15 | dig into it a little bit to find the answer.
16 | 
17 | Soon, you will realize you know Karma pretty well...
18 | 
19 | 
20 | ### Improve the documentation
21 | You don’t feel like hacking on the code, but still want to help?
22 | Improving the documentation is very valuable as it will help many others.
23 | 
24 | All the source code is in [`docs/`].
25 | 
26 | 
27 | ### Fix something that bothers you
28 | Did you find a bug that really bothers you? It’s more likely it bothers other users too, and maybe
29 | it’s not that hard to fix it! Try to find an existing issue. If it does not exist yet, create one.
30 | Look into the code and let others know what solution you are thinking about.
31 | Then, send a pull request and let other contributors review.
32 | 
33 | [Here](./making-changes.html) is some more info on how to set up your workspace and send a pull
34 | request.
35 | 
36 | 
37 | ### Fix something else
38 | You want to contribute some code but not sure where to start? That's cool. Fortunately,
39 | there are many issues labeled as "PR please". These are typically fairly simple issues with
40 | a well-known, waiting just for you...
41 | 
42 | [Here](https://github.com/karma-runner/karma/issues?labels=PR+please&page=1&state=open) is a list
43 | of all the issues for the core repo. In the same way, each plugin has "PR please" label as well...
44 | 
45 | 
46 | ### Review others work
47 | Check out the list of outstanding pull requests if there is something you might be interested in.
48 | Maybe somebody is trying to fix that stupid bug that bothers you. Review the PR.
49 | Do you have any better ideas how to fix this problem? Let us know...
50 | 
51 | ### I want to help more
52 | Check out [Maintaining Karma]. Becoming a Karma maintainer is simple.
53 | You just do it. There is no test to pass ;-)
54 | 
55 | [gitter]: https://gitter.im/karma-runner/karma
56 | [Stack Overflow]: https://stackoverflow.com/questions/tagged/karma-runner
57 | [`docs/`]: https://github.com/karma-runner/karma/tree/master/docs
58 | [Maintaining Karma]: ./maintaining.html
59 | [How to Contribute to an Open Source Project on GitHub]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github
60 | 


--------------------------------------------------------------------------------
/docs/dev/02-making-changes.md:
--------------------------------------------------------------------------------
  1 | <!---
  2 | TODO:
  3 | - add more info about updating PR
  4 |   - rebasing/squashing changes
  5 |   - making sure CI is green
  6 | - how to run tests on sauce labs
  7 | - how to set up plugins
  8 | -->
  9 | 
 10 | If you are thinking about making Karma better, or you just want to hack on it, that’s great!
 11 | Here are some tips on how to set up a Karma workspace and how to send a good pull request.
 12 | 
 13 | ## Setting up the Workspace
 14 | 
 15 | * Make sure you have a [GitHub account](https://github.com/signup/free).
 16 | * [Fork the repository] on GitHub.
 17 | * Clone your fork
 18 |   ```bash
 19 |   $ git clone https://github.com/<your-username>/karma.git
 20 |   $ cd karma
 21 |   ```
 22 | * Install for development
 23 |   ```bash
 24 |   $ npm install
 25 |   ```
 26 | 
 27 | ## Testing and Building
 28 | - Run the tests via:
 29 |   ```bash
 30 |   $ npm test
 31 |   # or you can run test suits individually
 32 |   $ npm run test:unit
 33 |   $ npm run test:e2e
 34 |   $ npm run test:client
 35 |   ```
 36 | 
 37 | - Lint the code via:
 38 |   ```bash
 39 |   $ npm run lint
 40 |   # or you can also apply auto-fixes where possible
 41 |   $ npm run lint:fix
 42 |   ```
 43 | 
 44 | - Build the client code via:
 45 |   ```bash
 46 |   $ npm run build
 47 |   # or use the watch mode
 48 |   $ npm run build:watch
 49 |   ```
 50 | 
 51 | ## Changing the Code
 52 | Checkout a new branch and name it accordingly to what you intend to do:
 53 | - Features get the prefix `feature-`.
 54 | - Bug fixes get the prefix `fix-`.
 55 | - Improvements to the documentation get the prefix `docs-`.
 56 | ```bash
 57 | $ git checkout -b <branch_name>
 58 | ```
 59 | 
 60 | Open your favorite editor, make some changes, run the tests, change the code, run the tests,
 61 | change the code, run the tests, etc.
 62 | 
 63 | - Please follow http://nodeguide.com/style.html (with exception of 100 characters per line).
 64 | 
 65 | 
 66 | ## Sending a Pull Request
 67 | 
 68 | - Commit your changes (please follow [commit message conventions]):
 69 |   ```bash
 70 |   $ git commit -m "..."
 71 |   ```
 72 | - Verify that the last commit follows the conventions:
 73 |   ```bash
 74 |   $ npm run commit:check
 75 |   ```
 76 | - Push to your GitHub repo:
 77 |   ```bash
 78 |   $ git push origin <branch_name>
 79 |   ```
 80 | - Go to the GitHub page and click "Open a Pull request".
 81 | - Write a good description of the change.
 82 | 
 83 | After sending a pull request, other developers will review and discuss your change.
 84 | Please address all the comments. Once everything is all right, one of the maintainers will merge
 85 | your changes in.
 86 | 
 87 | 
 88 | ## Contributor License Agreement
 89 | Please sign our Contributor License Agreement (CLA) before sending pull requests.
 90 | For any code changes to be accepted, the CLA must be signed. It's a quick process, we promise!
 91 | - For individuals, we have a [simple click-through form].
 92 | - For corporations we'll need you to print, sign and one of scan+email, fax or mail [the form].
 93 | 
 94 | ## Additional Resources
 95 | 
 96 | - [Mailing List](https://groups.google.com/forum/#!forum/karma-users)
 97 | - [Issue tracker](https://github.com/karma-runner/karma/issues)
 98 | - [General GitHub documentation](https://docs.github.com/)
 99 | - [GitHub pull request documentation](https://docs.github.com/github/collaborating-with-issues-and-pull-requests/about-pull-requests#about-pull-requests)
100 | 
101 | [commit message conventions]: git-commit-msg.html
102 | [simple click-through form]: https://code.google.com/legal/individual-cla-v1.0.html
103 | [the form]: https://code.google.com/legal/corporate-cla-v1.0.html
104 | [Fork the repository]: https://github.com/karma-runner/karma/fork
105 | 


--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: homepage
3 | pageTitle: Spectacular Test Runner for Javascript
4 | ---
5 | 


--------------------------------------------------------------------------------
/docs/intro/01-installation.md:
--------------------------------------------------------------------------------
 1 | Karma runs on [Node.js] and is available as an [npm] package.
 2 | 
 3 | ## Installing Node.js
 4 | 
 5 | On Mac or Linux we recommend using [NVM](https://github.com/creationix/nvm). On Windows, download Node.js
 6 | from [the official site](https://nodejs.org/) or use the [NVM PowerShell Module](https://www.powershellgallery.com/packages/nvm).
 7 | 
 8 | Karma works on all [LTS releases](https://nodejs.org/en/about/releases/) of Node.js.
 9 | 
10 | ## Installing Karma and plugins
11 | 
12 | The recommended approach is to install Karma (and all the plugins your project needs) locally in
13 | the project's directory.
14 | 
15 | ```bash
16 | # Install Karma:
17 | $ npm install karma --save-dev
18 | 
19 | # Install plugins that your project needs:
20 | $ npm install karma-jasmine karma-chrome-launcher jasmine-core --save-dev
21 | 
22 | ```
23 | 
24 | This will install `karma`, `karma-jasmine`, `karma-chrome-launcher` and `jasmine-core` packages into `node_modules` in your current
25 | working directory and also save these as `devDependencies` in `package.json`, so that any
26 | other developer working on the project will only have to do `npm install` in order to get all these
27 | dependencies installed.
28 | 
29 | ```bash
30 | # Run Karma:
31 | $ ./node_modules/karma/bin/karma start
32 | ```
33 | 
34 | ## Commandline Interface
35 | Typing `./node_modules/karma/bin/karma start` sucks and so you might find it useful to install `karma-cli` globally. You will need to do this if you want to run Karma on Windows from the command line.
36 | 
37 | ```bash
38 | $ npm install -g karma-cli
39 | ```
40 | 
41 | Then, you can run Karma simply by `karma` from anywhere and it will always run the local version.
42 | 
43 | 
44 | [Node.js]: https://nodejs.org/
45 | [npm]: https://www.npmjs.com/package/karma
46 | [NVM]: https://github.com/creationix/nvm
47 | [FAQ]: ./faq.html
48 | 


--------------------------------------------------------------------------------
/docs/intro/02-configuration.md:
--------------------------------------------------------------------------------
 1 | In order to serve you well, Karma needs to know about your project in order to test it
 2 | and this is done via a configuration file. This page explains how to create such a configuration file.
 3 | 
 4 | See [configuration file docs] for more information about the syntax and all the available options.
 5 | 
 6 | ## Generating the config file
 7 | 
 8 | The configuration file can be generated using `karma init`:
 9 | ```bash
10 | $ karma init my.conf.js
11 | 
12 | Which testing framework do you want to use?
13 | Press tab to list possible options. Enter to move to the next question.
14 | > jasmine
15 | 
16 | Do you want to use Require.js?
17 | This will add Require.js plugin.
18 | Press tab to list possible options. Enter to move to the next question.
19 | > no
20 | 
21 | Do you want to capture a browser automatically?
22 | Press tab to list possible options. Enter empty string to move to the next question.
23 | > Chrome
24 | > Firefox
25 | >
26 | 
27 | What is the location of your source and test files?
28 | You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
29 | Press Enter to move to the next question.
30 | > *.js
31 | > test/**/*.js
32 | >
33 | 
34 | Should any of the files included by the previous patterns be excluded?
35 | You can use glob patterns, eg. "**/*.swp".
36 | Press Enter to move to the next question.
37 | >
38 | 
39 | Do you want Karma to watch all the files and run the tests on change?
40 | Press tab to list possible options.
41 | > yes
42 | 
43 | Config file generated at "/Users/vojta/Code/karma/my.conf.js".
44 | ```
45 | 
46 | The configuration file can be written in CoffeeScript as well.
47 | In fact, if you execute `karma init` with a `*.coffee` extension such as `karma init karma.conf.coffee`, it will generate a CoffeeScript file.
48 | 
49 | Of course, you can write the config file by hand or copy-paste it from another project ;-)
50 | 
51 | ## Starting Karma
52 | When starting Karma, the configuration file path can be passed in as the first argument.
53 | 
54 | By default, Karma will look for `karma.conf.js` or `karma.conf.coffee` in the current directory.
55 | ```bash
56 | # Start Karma using your configuration:
57 | $ karma start my.conf.js
58 | ```
59 | 
60 | For more detailed information about the Karma configuration file, such as available options and features,
61 | please read the [configuration file docs].
62 | 
63 | ## Command line arguments
64 | Some configurations, which are already present within the configuration file, can be overridden by specifying the configuration
65 | as a command line argument for when Karma is executed.
66 | 
67 | ```bash
68 | karma start my.conf.js --log-level debug --single-run
69 | ```
70 | 
71 | Try `karma start --help` if you want to see all available options.
72 | 
73 | 
74 | ## Integrating with Grunt/Gulp
75 | - [grunt-karma]
76 | - [gulp-karma]
77 | 
78 | 
79 | [configuration file docs]: ../config/configuration-file.html
80 | [Grunt]: https://gruntjs.com/
81 | [grunt-karma]: https://github.com/karma-runner/grunt-karma
82 | [Gulp]: https://gulpjs.com
83 | [gulp-karma]: https://github.com/karma-runner/gulp-karma
84 | 


--------------------------------------------------------------------------------
/docs/intro/03-how-it-works.md:
--------------------------------------------------------------------------------
 1 | Karma is essentially a tool which spawns a web server that executes source code against test code for each of the browsers connected.
 2 | The results of each test against each browser are examined and displayed via the command line to the developer
 3 | such that they can see which browsers and tests passed or failed.
 4 | 
 5 | A browser can be captured either
 6 | - manually, by visiting the URL where the Karma server is listening (typically `http://localhost:9876/`),
 7 | - or automatically by letting Karma know which browsers to start when Karma is run (see [browsers]).
 8 | 
 9 | Karma also watches all the files, specified within the configuration file, and whenever any file changes, it triggers the test run by
10 | sending a signal to the testing server to inform all of the captured browsers to run the test code again.
11 | Each browser then loads the source files inside an IFrame, executes the tests and reports the results back to the server.
12 | 
13 | The server collects the results from all of the captured browsers and presents them to the developer.
14 | 
15 | This is only a very brief overview, as the internals of how Karma works aren't entirely necessary when using Karma.
16 | 
17 | ## Outline of workflow.
18 | 
19 | Here is roughly how Karma works:
20 | 
21 | After starting up, Karma loads plugins and the configuration file, then starts its local web server which listens for connections.
22 | Any browser already waiting on websockets from the server will reconnect immediately. As part of loading the plugins, test reporters
23 | register for 'browser' events so they are ready for test results.
24 | 
25 | Then karma launches zero, one, or more browsers, setting their start page the Karma server URL.
26 | 
27 | 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.
28 | 
29 | 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.
30 | 
31 | 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.
32 | 
33 | 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.
34 | 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 its format.
35 | 
36 | Karma has many variations and options that may cause different workflow with different configurations.
37 | 
38 | If you are interested in learning more about the design, Karma itself originates from a university thesis, which goes into detail about the design
39 | and implementation, and it is available to read [right here].
40 | 
41 | [right here]: https://github.com/karma-runner/karma/raw/master/thesis.pdf
42 | [browsers]: ../config/browsers.html
43 | 


--------------------------------------------------------------------------------
/docs/intro/04-faq.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | pageTitle: Frequently Asked Questions
 3 | menuTitle: FAQ
 4 | ---
 5 | 
 6 | The list below is a collection of common questions regarding Karma and its use.
 7 | If you have any other questions in mind, please visit the [mailing list] to let the community know.
 8 | 
 9 | 
10 | ### Can I use Karma with testing framework X?
11 | Yes. There are plugins for most of the common testing frameworks (such as Jasmine, Mocha, QUnit).
12 | If there is no plugin for the testing framework you like, go ahead and write one. It is simple -
13 | you can start by looking into the source code of the existing ones.
14 | 
15 | 
16 | ### Can I use Karma to do end to end testing?
17 | Karma has primarily been designed for low level (unit) testing. If it's an AngularJS app, you can
18 | use Karma with the [karma-ng-scenario] plugin. However, we recommend [Protractor] for high-level testing.
19 | 
20 | 
21 | ### Can I use Karma on Continuous Integration server ?
22 | Of course! Check out the docs for [Jenkins], [Semaphore], [TeamCity] or [Travis].
23 | 
24 | 
25 | ### Which version of Karma should I use?
26 | The latest stable version from npm (`npm install karma`). See [versioning] for more detailed information about Karma's release channels.
27 | 
28 | 
29 | ### Which version of Node.js does Karma run with?
30 | Karma works on all LTS versions of Node.js as specified by the [Node.js Release Working Group](https://github.com/nodejs/Release/blob/master/README.md). The Node.js version numbers are set in the package.json. Older versions of karma work with older versions of Node.js, but are not maintained or updated.
31 | 
32 | [mailing list]: https://groups.google.com/d/forum/karma-users
33 | [karma-ng-scenario]: https://github.com/karma-runner/karma-ng-scenario
34 | [Protractor]: https://github.com/angular/protractor
35 | [Jenkins]: ../plus/jenkins.html
36 | [Semaphore]: ../plus/semaphore.html
37 | [TeamCity]: ../plus/teamcity.html
38 | [Travis]: ../plus/travis.html
39 | [versioning]: ../about/versioning.html
40 | [browsers]: ../config/browsers.html
41 | 


--------------------------------------------------------------------------------
/docs/plus/02-travis.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | pageTitle: Travis CI
 3 | menuTitle: Travis CI
 4 | ---
 5 | 
 6 | [Travis CI] is a popular continuous integration service that
 7 | integrates with your [Github] repository to automatically run your
 8 | tests when the code is pushed. Integration is done by adding a simple
 9 | [YAML] file to your project root; Travis and Github take care of the
10 | rest. Whenever tested, the Travis results will appear in your Github pull requests and your
11 | history will be available within their control panel. This article assumes you
12 | already have Travis account.
13 | 
14 | ## Configure Travis
15 | Create a file in your project root called `.travis.yml` with the
16 | following YAML content:
17 | 
18 | ```ruby
19 | language: node_js
20 | node_js:
21 |   - "4"
22 | ```
23 | 
24 | ## Set up a Test Command
25 | If you do not already have a `package.json` in your project root, create one now. Travis runs `npm test` to trigger your tests, so this
26 | is where you tell Travis how to run your tests.
27 | 
28 | ```json
29 | // ...snip...
30 | "devDependencies": {
31 |   "karma": "~0.12"
32 | },
33 | // ...snip...
34 | "scripts": {
35 |    "test": "karma start --single-run --browsers PhantomJS"
36 | }
37 | // ...snip...
38 | ```
39 | 
40 | Travis will run `npm install` before every suite, so this is your
41 | chance to specify any modules your app needs that Travis does not know
42 | about like Karma.
43 | 
44 | ## Configure Travis with Firefox
45 | Travis supports running a real browser (Firefox) with a virtual
46 | screen. Just update your `.travis.yml` to set up the virtual screen
47 | like this (if you're using Xenial):
48 | ```ruby
49 | language: node_js
50 | node_js:
51 |   - "4"
52 | dist: xenial
53 | services:
54 |   - xvfb
55 | ```
56 | 
57 | Or this, for Trusty and below:
58 | ```ruby
59 | language: node_js
60 | node_js:
61 |   - "4"
62 | before_script:
63 |   - export DISPLAY=:99.0
64 |   - sh -e /etc/init.d/xvfb start
65 | ```
66 | 
67 | And now, you can run your tests on Firefox, just change the `npm test`
68 | command to
69 | ```bash
70 | karma start --browsers Firefox --single-run
71 | ```
72 | 
73 | ## Notes
74 | 
75 | * Travis' Node environment has very little available. If the startup
76 |   process in Travis fails to check for missing module information and be sure to add them to your `package.json` dependencies.
77 | * Travis does not run on your local network so any code that attempts
78 |   to connect to resources should be stubbed out using [Nock].
79 | * There are more options available to your `.travis.yml`, such as
80 |   running scripts before the install or test run. There are hints in
81 |   the Travis docs for [GUI apps] configuration.
82 | 
83 | 
84 | [Travis CI]: https://travis-ci.org/
85 | [Github]: https://github.com/
86 | [YAML]: https://yaml.org/
87 | [PhantomJS]: https://phantomjs.org/
88 | [GUI apps]: https://docs.travis-ci.com/user/gui-and-headless-browsers/
89 | [Nock]: https://github.com/nock/nock
90 | 


--------------------------------------------------------------------------------
/docs/plus/03-jenkins.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | pageTitle: Jenkins CI
 3 | menuTitle: Jenkins CI
 4 | ---
 5 | 
 6 | [Jenkins CI] is one of the most popular continuous integration servers
 7 | in the market today. At some point while developing your [AngularJS]
 8 | project (hopefully early on), you might want to have automated tests run
 9 | off your code versioning system. Jenkins will help you with this task.
10 | This tutorial assumes you have Jenkins already setup and running
11 | on your CI environment.
12 | 
13 | ## Install Prerequisites
14 | You need the following tools installed on your Jenkins CI server:
15 | 
16 | * Node
17 | * Karma
18 | 
19 | The following Jenkins plugin is optional, but the next guidelines are based on it:
20 | * [EnvInject] - it makes things easier under certain linux distributions and user permissions.
21 | 
22 | ## Configure Karma
23 |  Make the following additions and changes to your `karma.conf.js`
24 |  file as needed:
25 | 
26 | ```javascript
27 | singleRun: true,
28 | reporters: ['dots', 'junit'],
29 | junitReporter: {
30 |   outputFile: 'test-results.xml'
31 | },
32 |  ```
33 | 
34 | Please note the `test-results.xml` files will be written to subdirectories
35 | named after the browsers the tests were run in inside the present working
36 | directory (and you will need to tell Jenkins where to find them).
37 | 
38 | ## Create a new Jenkins Job
39 | In Jenkins, start a new job for Angular/Karma with the basic
40 | settings (Name, description, parameters, source code repo to pull
41 | from, etc.)
42 | 
43 | ## Configure the Build Environment
44 | First go to the job page and click on configure. Then in the Build
45 | Environment sub-section, check the “Inject environment
46 | variables to the build process' checkbox. A few textboxes will
47 | appear and in the “Properties Content” box set the following:
48 | 
49 | ```bash
50 | $ PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
51 | $ PHANTOMJS_BIN=/usr/local/bin/phantomjs #or wherever PhantomJS happens to be installed
52 | ```
53 | 
54 | Further down the page, in the Post-build Actions sub-section add a
55 | `Publish JUnit test result report` from the Post-build action drop
56 | down menu. When the textbox labeled Test report XMLs appears, enter
57 | the path to where the `test-results.xml` files are relative to the root of your
58 | Jenkins job workspace (you can use wildcards for this, so `**/test-results.xml`
59 | will find the file even if it was stored inside a browser-specific
60 | subdirectory).
61 | 
62 | 
63 | 
64 | [Jenkins CI]: https://www.jenkins.io/
65 | [AngularJS]: https://angularjs.org
66 | [EnvInject]: https://plugins.jenkins.io/envinject/
67 | 


--------------------------------------------------------------------------------
/docs/plus/04-semaphore.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | pageTitle: Semaphore CI
 3 | menuTitle: Semaphore CI
 4 | ---
 5 | 
 6 | [Semaphore] is a popular continuous integration service that
 7 | supports a [wide range of programming languages]. Up-to-date 
 8 | versions of [Firefox], [PhantomJS] and [Node.js] make it a good 
 9 | testing ground for JavaScript applications. This article assumes 
10 | you already have a Semaphore account.
11 | 
12 | ## Configure Your Project
13 | 
14 | If you do not already have a `package.json` in your project root,
15 | create one now. This will both document your configuration and
16 | make it easy to run your tests. Here's an example:
17 | 
18 | ```json
19 | // ...snip...
20 | "devDependencies": {
21 |   "karma": "~0.10"
22 | },
23 | // ...snip...
24 | "scripts": {
25 |    "test": "./node_modules/.bin/karma start --single-run --browsers PhantomJS"
26 | }
27 | // ...snip...
28 | ```
29 | 
30 | Another option is to use Firefox as your test browser. To do this, change
31 | the last part to:
32 | 
33 | ```json
34 | "scripts": {
35 |    "test": "./node_modules/.bin/karma start --single-run --browsers Firefox"
36 | }
37 | ```
38 | 
39 | Now running `npm test` within your project will run your tests with Karma.
40 | 
41 | ## Add Your Project to Semaphore
42 | 
43 | Follow the process as shown in the [screencast] on the Semaphore docs.
44 | 
45 | After the analysis is finished, ignore the Ruby version Semaphore has set
46 | for you, choose to customize your build commands and use these:
47 | 
48 | ```bash
49 | npm install
50 | npm test
51 | ```
52 | 
53 | That's it - proceed to your first build. In case you're using Firefox as
54 | your test browser, Semaphore will automatically run it on a virtual screen
55 | during your builds.
56 | 
57 | Also, if necessary, build commands can be further [customized] at any time.
58 | 
59 | 
60 | [screencast]: https://semaphoreci.com/docs/adding-github-bitbucket-project-to-semaphore.html
61 | [Semaphore]: https://semaphoreci.com
62 | [wide range of programming languages]: https://semaphoreci.com/docs/supported-stack.html
63 | [Firefox]: https://semaphoreci.com/docs/firefox.html
64 | [PhantomJS]: https://semaphoreci.com/docs/phantomjs.html
65 | [Node.js]: https://semaphoreci.com/docs/languages/javascript/javascript-support-on-semaphore.html
66 | [platform]: https://semaphoreci.com/docs/supported-stack.html
67 | [customized]: https://semaphoreci.com/docs/customizing-build-commands.html
68 | 


--------------------------------------------------------------------------------
/docs/plus/05-cloud9.md:
--------------------------------------------------------------------------------
 1 | [Cloud9 IDE] is an open source web-based cloud integrated development environment that supports
 2 | several programming languages, with a focus on the web stack (specifically JavaScript and NodeJS).
 3 | It is written almost entirely in JavaScript and uses NodeJS on the back-end.
 4 | 
 5 | ## Configuration
 6 | 
 7 | First, make sure the `karma.conf.js` includes the following entries:
 8 | 
 9 | ```javascript
10 | hostname: process.env.IP,
11 | port: process.env.PORT
12 | ```
13 | 
14 | ## Capture the browser manually on the local machine
15 | 
16 | You can use any of your local browsers.
17 | 
18 | ```bash
19 | # Start Karma without browsers:
20 | $ karma start --no-browsers
21 | ```
22 | 
23 | Now, open `http://<projectName>.<cloud9User>.c9.io/` in your browser.
24 | 
25 | ## Run Karma unit tests with PhantomJS
26 | 
27 | It is also possible to run headless PhantomJS on the Cloud9 server.
28 | 
29 | ```bash
30 | # Install the PhantomJS plugin:
31 | $ npm install karma-phantomjs-launcher
32 | 
33 | # Start Karma:
34 | $ karma start --browsers PhantomJS
35 | ```
36 | 
37 | [Cloud9 IDE]: https://c9.io/
38 | 


--------------------------------------------------------------------------------
/docs/plus/06-angularjs.md:
--------------------------------------------------------------------------------
1 | ---
2 | pageTitle: AngularJS
3 | menuTitle: AngularJS
4 | ---
5 | 
6 | If you're using [AngularJS](https://angularjs.org), check out the [AngularJS Generator](https://github.com/yeoman/generator-angular), which makes use of the [Karma Generator](https://github.com/yeoman/generator-karma) to setup a fully featured, testing-ready project.
7 | 


--------------------------------------------------------------------------------
/docs/plus/07-yeoman.md:
--------------------------------------------------------------------------------
1 | [Yeoman](https://yeoman.io/) is a set of tools to make building web apps easier. Yeoman has support for Karma via the [Karma Generator](https://github.com/yeoman/generator-karma).
2 | 


--------------------------------------------------------------------------------
/docs/plus/08-emberjs.md:
--------------------------------------------------------------------------------
  1 | ---
  2 | pageTitle: Ember.js
  3 | menuTitle: Ember.js
  4 | ---
  5 | 
  6 | To execute javascript unit and integration tests with ember.js follow the steps below:
  7 | 
  8 | 1. [install karma]
  9 | 
 10 | 2. install the qunit plugin
 11 | 
 12 |    ```bash
 13 |    npm install karma-qunit --save-dev
 14 |    ```
 15 | 
 16 | 3. install the ember preprocessor plugin
 17 | 
 18 |    ```bash
 19 |    npm install karma-ember-preprocessor --save-dev
 20 |    ```
 21 | 
 22 | 4. generate a configuration file for karma
 23 |    ```bash
 24 |    karma init
 25 |    ```
 26 |    note -the above will walk you through the basic setup. An example configuration file that works with ember.js/qunit and phantomjs is below
 27 | 
 28 |    ```javascript
 29 |    module.exports = function(config) {
 30 |      config.set({
 31 |        basePath: 'js',
 32 |  
 33 |        files: [
 34 |          'vendor/jquery/jquery.min.js',
 35 |          'vendor/handlebars/handlebars.js',
 36 |          'vendor/ember/ember.js',
 37 |          'app.js',
 38 |          'tests/*.js',
 39 |          'templates/*.handlebars'
 40 |        ],
 41 |  
 42 |        browsers: ['PhantomJS'],
 43 |        singleRun: true,
 44 |        autoWatch: false,
 45 |  
 46 |        frameworks: ['qunit'],
 47 |  
 48 |        plugins: [
 49 |          'karma-qunit',
 50 |          'karma-ember-preprocessor',
 51 |          'karma-phantomjs-launcher'
 52 |        ],
 53 |  
 54 |        preprocessors: {
 55 |          '**/*.handlebars': 'ember'
 56 |        }
 57 |      });
 58 |    };
 59 |    ```
 60 | 
 61 |    Note - the `files` section above should include all dependencies, ie- jQuery/handlebars/ember.js along with the js and handlebars files required to deploy and run your production ember.js application
 62 | 
 63 |    Note - when testing ember applications, it is important that karma does not try to run the tests until the ember application has finished initialization. You will need to include a small bootstrap file in the `files` section above to enforce this.  Here's an example:
 64 |    ```javascript
 65 |    __karma__.loaded = function() {};
 66 |  
 67 |    App.setupForTesting();
 68 |    App.injectTestHelpers();
 69 |  
 70 |    //this gate/check is required given that standard practice in Ember tests to is to call
 71 |    //Ember.reset() in the afterEach/tearDown for each test.  Doing so, causes the application
 72 |    //to 're-initialize', resulting in repeated calls to the initialize function below
 73 |    var karma_started = false;
 74 |    App.initializer({
 75 |        name: "run tests",
 76 |        initialize: function(container, application) {
 77 |            if (!karma_started) {
 78 |                karma_started = true;
 79 |                __karma__.start();
 80 |            }
 81 |        }
 82 |    });
 83 |    ```
 84 | 
 85 | 5. add a simple Qunit test
 86 | 
 87 |    ```javascript
 88 |    test('one should equal one', function() {
 89 |        equal(1, 1, 'error: one did not equal one');
 90 |    });
 91 |    ```
 92 | 
 93 | 6. run the tests with karma from the command line
 94 |    ```bash
 95 |    karma start
 96 |    ```
 97 | 
 98 | A simple unit / integration tested example app showing karma / qunit / ember in action can be found [here]
 99 | 
100 | [install karma]: ../intro/installation.html
101 | [here]: https://github.com/toranb/ember-testing-example
102 | 


--------------------------------------------------------------------------------
/docs/plus/09-codio.md:
--------------------------------------------------------------------------------
 1 | [Codio] is a web-based cloud integrated development environment that supports almost any programming language. Every project gets its individual Box: an instantly available server-side development environment with full terminal access. Unlimited panels and tabs, and a plethora of productivity features. 
 2 | 
 3 | ## Customize your Codio Project
 4 | 
 5 | Next to the help menu you will see the "Configure" option, if you don't see it click the little arrow near the end and then select "Configure".
 6 | 
 7 | 
 8 | This opens a .codio file which you can customize and use rather than entering commands in Terminal. Replace the existing text with the text below:
 9 | 
10 | 
11 |     {
12 |     // Configure your Run and Preview buttons here.
13 | 
14 |     // Run button configuration
15 |       "commands": {
16 |         "Karma Start": "karma start --no-browsers"
17 |         "Karam Run": "karma run"
18 |       },
19 | 
20 |     // Preview button configuration
21 |       "preview": {
22 |         "Karma Preview": "http://{{domain}}:8080"
23 |       }
24 |     }
25 | 
26 | 
27 | *If you wish, you can change the port for the `Karma Preview` entry, but make a note of the change as you will need to include that port in the `karma.config js` file later.*
28 | 
29 | 
30 | ## Configuration
31 | 
32 | - Edit the `karma.conf.js` and add the following:
33 | 
34 | 
35 |         // hostname for the server
36 |         hostname: require('os').hostname() + '.codio.io',
37 | 
38 | 
39 | - Review webserver port entry to ensure same port defined as entered in .codio file
40 | 
41 |     
42 |         // web server port
43 |         port: 8080,
44 |  
45 | 
46 | ## Capture the browser manually on the local machine
47 | 
48 | You can use your local browser.
49 | 
50 | - Either:
51 | 
52 |     - Open a Terminal window and enter
53 | 
54 |             $ karma start --no-browsers
55 | or 
56 |     - Select `Karma Start` from the Run menu (the 2nd from the right button).
57 | 
58 | 
59 | - Select `Karma Preview` from the Preview menu (the right-hand button).
60 | 
61 | - Switch back into your Codio project and either: 
62 | 
63 |     - Open a new Terminal window and enter
64 | 
65 |             $ karma run
66 | or 
67 |     - Select `Karma Run` from the Run menu (the 2nd from the right button).
68 | 
69 | and your test will execute
70 | 
71 | 
72 | [Codio]: https://codio.com
73 | 


--------------------------------------------------------------------------------
/docs/plus/10-teamcity.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | pageTitle: TeamCity
 3 | menuTitle: TeamCity
 4 | ---
 5 | 
 6 | Running Karma in your [TeamCity] build is as simple as adding command line build
 7 | step to perform the task. That is basically it.
 8 | 
 9 | ## Install Prerequisites
10 | The only prerequisite is `Node` (with `npm`) installed on the agent(s) you are going to use to
11 | run build on.
12 | You may decide to install Karma and Karma-related packages on the agent globally to reuse the same
13 | Karma installation by different builds.
14 | 
15 | ## Configure project
16 | Add `karma-teamcity-reporter` as a dependency to your project:
17 | 
18 |     npm i --save-dev karma-teamcity-reporter
19 | 
20 | It is also a good idea to check that you have all karma npm dependencies listed in your
21 | `package.json` file (e.g. `karma-jasmine`, `karma-phantomjs-launcher` and so on) to have them
22 | being installed during the build.
23 | 
24 | ## Create a new TeamCity build step
25 | Add new build step to the build configuration: use Command Line runner and fill in `Custom
26 | script` text area. If you had decided not to install *all* your npm dependencies globally
27 | add `npm install` at the beginning of the script. Then add command to run Karma, e.g.:
28 | 
29 |     karma start --reporters teamcity --single-run --browsers PhantomJS --colors false
30 | 
31 | Running Karma with all these options provided via command line allows to run Karma in
32 | TeamCity build and locally in development environment (with options from configuration
33 | file).
34 | 
35 | [TeamCity]: https://www.jetbrains.com/teamcity/
36 | 


--------------------------------------------------------------------------------
/lib/browser_collection.js:
--------------------------------------------------------------------------------
  1 | 'use strict'
  2 | 
  3 | const BrowserResult = require('./browser_result')
  4 | const helper = require('./helper')
  5 | 
  6 | class BrowserCollection {
  7 |   constructor (emitter, browsers = []) {
  8 |     this.browsers = browsers
  9 |     this.emitter = emitter
 10 |   }
 11 | 
 12 |   add (browser) {
 13 |     this.browsers.push(browser)
 14 |     this.emitter.emit('browsers_change', this)
 15 |   }
 16 | 
 17 |   remove (browser) {
 18 |     if (helper.arrayRemove(this.browsers, browser)) {
 19 |       this.emitter.emit('browsers_change', this)
 20 |       return true
 21 |     }
 22 |     return false
 23 |   }
 24 | 
 25 |   getById (browserId) {
 26 |     return this.browsers.find((browser) => browser.id === browserId) || null
 27 |   }
 28 | 
 29 |   getNonReady () {
 30 |     return this.browsers.filter((browser) => !browser.isConnected())
 31 |   }
 32 | 
 33 |   areAllReady () {
 34 |     return this.browsers.every((browser) => browser.isConnected())
 35 |   }
 36 | 
 37 |   serialize () {
 38 |     return this.browsers.map((browser) => browser.serialize())
 39 |   }
 40 | 
 41 |   calculateExitCode (results, singleRunBrowserNotCaptured, config) {
 42 |     config = config || {}
 43 |     if (results.disconnected || singleRunBrowserNotCaptured) {
 44 |       return 1
 45 |     }
 46 |     if (results.skipped && config.failOnSkippedTests) {
 47 |       return 1
 48 |     }
 49 |     if (results.success + results.failed === 0 && !!config.failOnEmptyTestSuite) {
 50 |       return 1
 51 |     }
 52 |     if (results.error) {
 53 |       return 1
 54 |     }
 55 |     if (config.failOnFailingTestSuite === false) {
 56 |       return 0 // Tests executed without infrastructure error, exit with 0 independent of test status.
 57 |     }
 58 |     return results.failed ? 1 : 0
 59 |   }
 60 | 
 61 |   getResults (singleRunBrowserNotCaptured, config) {
 62 |     const results = { success: 0, failed: 0, skipped: 0, error: false, disconnected: false, exitCode: 0 }
 63 |     this.browsers.forEach((browser) => {
 64 |       results.success += browser.lastResult.success
 65 |       results.failed += browser.lastResult.failed
 66 |       results.skipped += browser.lastResult.skipped
 67 |       results.error = results.error || browser.lastResult.error
 68 |       results.disconnected = results.disconnected || browser.lastResult.disconnected
 69 |     })
 70 | 
 71 |     results.exitCode = this.calculateExitCode(results, singleRunBrowserNotCaptured, config)
 72 |     return results
 73 |   }
 74 | 
 75 |   clearResults () {
 76 |     this.browsers.forEach((browser) => {
 77 |       browser.lastResult = new BrowserResult()
 78 |     })
 79 |   }
 80 | 
 81 |   clone () {
 82 |     return new BrowserCollection(this.emitter, this.browsers.slice())
 83 |   }
 84 | 
 85 |   // Array APIs
 86 |   map (callback, context) {
 87 |     return this.browsers.map(callback, context)
 88 |   }
 89 | 
 90 |   forEach (callback, context) {
 91 |     return this.browsers.forEach(callback, context)
 92 |   }
 93 | 
 94 |   get length () {
 95 |     return this.browsers.length
 96 |   }
 97 | }
 98 | 
 99 | BrowserCollection.factory = function (emitter) {
100 |   return new BrowserCollection(emitter)
101 | }
102 | 
103 | module.exports = BrowserCollection
104 | 


--------------------------------------------------------------------------------
/lib/browser_result.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | class BrowserResult {
 4 |   constructor (total = 0) {
 5 |     this.startTime = Date.now()
 6 | 
 7 |     this.total = total
 8 |     this.skipped = this.failed = this.success = 0
 9 |     this.netTime = this.totalTime = 0
10 |     this.disconnected = this.error = false
11 |   }
12 | 
13 |   totalTimeEnd () {
14 |     this.totalTime = Date.now() - this.startTime
15 |   }
16 | 
17 |   add (result) {
18 |     if (result.skipped) {
19 |       this.skipped++
20 |     } else if (result.success) {
21 |       this.success++
22 |     } else {
23 |       this.failed++
24 |     }
25 | 
26 |     this.netTime += result.time
27 |   }
28 | }
29 | 
30 | module.exports = BrowserResult
31 | 


--------------------------------------------------------------------------------
/lib/constants.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const fs = require('graceful-fs')
 4 | const path = require('path')
 5 | 
 6 | const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '/../package.json')).toString())
 7 | 
 8 | exports.VERSION = pkg.version
 9 | 
10 | exports.DEFAULT_PORT = process.env.PORT || 9876
11 | exports.DEFAULT_HOSTNAME = process.env.IP || 'localhost'
12 | exports.DEFAULT_LISTEN_ADDR = process.env.LISTEN_ADDR || '0.0.0.0'
13 | 
14 | // log levels
15 | exports.LOG_DISABLE = 'OFF'
16 | exports.LOG_ERROR = 'ERROR'
17 | exports.LOG_WARN = 'WARN'
18 | exports.LOG_INFO = 'INFO'
19 | exports.LOG_DEBUG = 'DEBUG'
20 | exports.LOG_LOG = 'LOG'
21 | exports.LOG_PRIORITIES = [
22 |   exports.LOG_DISABLE,
23 |   exports.LOG_ERROR,
24 |   exports.LOG_WARN,
25 |   exports.LOG_LOG,
26 |   exports.LOG_INFO,
27 |   exports.LOG_DEBUG
28 | ]
29 | 
30 | // Default patterns for the pattern layout.
31 | exports.COLOR_PATTERN = '%[%d{DATETIME}:%p [%c]: %]%m'
32 | exports.NO_COLOR_PATTERN = '%d{DATETIME}:%p [%c]: %m'
33 | 
34 | // Default console appender
35 | exports.CONSOLE_APPENDER = {
36 |   type: 'console',
37 |   layout: {
38 |     type: 'pattern',
39 |     pattern: exports.COLOR_PATTERN
40 |   }
41 | }
42 | 
43 | exports.EXIT_CODE = '\x1FEXIT'
44 | 


--------------------------------------------------------------------------------
/lib/detached.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const fs = require('fs')
 4 | 
 5 | const Server = require('./server')
 6 | const configurationFile = process.argv[2]
 7 | const fileContents = fs.readFileSync(configurationFile, 'utf-8')
 8 | fs.unlink(configurationFile, function () {})
 9 | const data = JSON.parse(fileContents)
10 | const server = new Server(data)
11 | server.start(data)
12 | 


--------------------------------------------------------------------------------
/lib/emitter_wrapper.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | class EmitterWrapper {
 4 |   constructor (emitter) {
 5 |     this.listeners = {}
 6 |     this.emitter = emitter
 7 |   }
 8 | 
 9 |   addListener (event, listener) {
10 |     this.emitter.addListener(event, listener)
11 |     this.listeners[event] = this.listeners[event] || []
12 |     this.listeners[event].push(listener)
13 |     return this
14 |   }
15 | 
16 |   on (event, listener) {
17 |     return this.addListener(event, listener)
18 |   }
19 | 
20 |   removeAllListeners (event) {
21 |     const events = event ? [event] : Object.keys(this.listeners)
22 |     events.forEach((event) => {
23 |       this.listeners[event].forEach((listener) => {
24 |         this.emitter.removeListener(event, listener)
25 |       })
26 |       delete this.listeners[event]
27 |     })
28 | 
29 |     return this
30 |   }
31 | }
32 | 
33 | module.exports = EmitterWrapper
34 | 


--------------------------------------------------------------------------------
/lib/events.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const EventEmitter = require('events').EventEmitter
 4 | const helper = require('./helper')
 5 | 
 6 | function bufferEvents (emitter, eventsToBuffer) {
 7 |   const listeners = []
 8 |   const eventsToReply = []
 9 | 
10 |   function genericListener () {
11 |     eventsToReply.push(Array.from(arguments))
12 |   }
13 | 
14 |   eventsToBuffer.forEach((eventName) => {
15 |     const listener = genericListener.bind(null, eventName)
16 |     listeners.push(listener)
17 |     emitter.on(eventName, listener)
18 |   })
19 | 
20 |   return function () {
21 |     listeners.forEach((listener, i) => {
22 |       emitter.removeListener(eventsToBuffer[i], listener)
23 |     })
24 | 
25 |     eventsToReply.forEach((args) => {
26 |       EventEmitter.prototype.emit.apply(emitter, args)
27 |     })
28 | 
29 |     listeners.length = 0
30 |     eventsToReply.length = 0
31 |   }
32 | }
33 | 
34 | class KarmaEventEmitter extends EventEmitter {
35 |   bind (object) {
36 |     for (const method in object) {
37 |       if (method.startsWith('on') && helper.isFunction(object[method])) {
38 |         this.on(helper.camelToSnake(method.slice(2)), function () {
39 |           // We do not use an arrow function here, to supply the caller as this.
40 |           object[method].apply(object, Array.from(arguments).concat(this))
41 |         })
42 |       }
43 |     }
44 |   }
45 | 
46 |   emitAsync (name) {
47 |     // TODO(vojta): allow passing args
48 |     // TODO(vojta): ignore/throw if listener call done() multiple times
49 |     let pending = this.listeners(name).length
50 |     const deferred = helper.defer()
51 | 
52 |     this.emit(name, () => {
53 |       if (!--pending) {
54 |         deferred.resolve()
55 |       }
56 |     })
57 | 
58 |     if (!pending) {
59 |       deferred.resolve()
60 |     }
61 | 
62 |     return deferred.promise
63 |   }
64 | }
65 | 
66 | exports.EventEmitter = KarmaEventEmitter
67 | exports.bufferEvents = bufferEvents
68 | 


--------------------------------------------------------------------------------
/lib/executor.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const log = require('./logger').create()
 4 | 
 5 | class Executor {
 6 |   constructor (capturedBrowsers, config, emitter) {
 7 |     this.capturedBrowsers = capturedBrowsers
 8 |     this.config = config
 9 |     this.emitter = emitter
10 | 
11 |     this.executionScheduled = false
12 |     this.errorsScheduled = []
13 |     this.pendingCount = 0
14 |     this.runningBrowsers = null
15 | 
16 |     this.emitter.on('run_complete', () => this.onRunComplete())
17 |     this.emitter.on('browser_complete', () => this.onBrowserComplete())
18 |   }
19 | 
20 |   schedule () {
21 |     if (this.capturedBrowsers.length === 0) {
22 |       log.warn(`No captured browser, open ${this.config.protocol}//${this.config.hostname}:${this.config.port}${this.config.urlRoot}`)
23 |       return false
24 |     } else if (this.capturedBrowsers.areAllReady()) {
25 |       log.debug('All browsers are ready, executing')
26 |       log.debug(`Captured ${this.capturedBrowsers.length} browsers`)
27 |       this.executionScheduled = false
28 |       this.capturedBrowsers.clearResults()
29 |       this.pendingCount = this.capturedBrowsers.length
30 |       this.runningBrowsers = this.capturedBrowsers.clone()
31 |       this.emitter.emit('run_start', this.runningBrowsers)
32 |       this.socketIoSockets.emit('execute', this.config.client)
33 |       return true
34 |     } else {
35 |       log.info('Delaying execution, these browsers are not ready: ' + this.capturedBrowsers.getNonReady().join(', '))
36 |       this.executionScheduled = true
37 |       return false
38 |     }
39 |   }
40 | 
41 |   /**
42 |    * Schedule an error to be reported
43 |    * @param {string} errorMessage
44 |    * @returns {boolean} a boolean indicating whether or not the error was handled synchronously
45 |    */
46 |   scheduleError (errorMessage) {
47 |     // We don't want to interfere with any running test.
48 |     // Verify that no test is running before reporting the error.
49 |     if (this.capturedBrowsers.areAllReady()) {
50 |       log.warn(errorMessage)
51 |       const errorResult = {
52 |         success: 0,
53 |         failed: 0,
54 |         skipped: 0,
55 |         error: errorMessage,
56 |         exitCode: 1
57 |       }
58 |       const noBrowsersStartedTests = []
59 |       this.emitter.emit('run_start', noBrowsersStartedTests) // A run cannot complete without being started
60 |       this.emitter.emit('run_complete', noBrowsersStartedTests, errorResult)
61 |       return true
62 |     } else {
63 |       this.errorsScheduled.push(errorMessage)
64 |       return false
65 |     }
66 |   }
67 | 
68 |   onRunComplete () {
69 |     if (this.executionScheduled) {
70 |       this.schedule()
71 |     }
72 |     if (this.errorsScheduled.length) {
73 |       const errorsToReport = this.errorsScheduled
74 |       this.errorsScheduled = []
75 |       errorsToReport.forEach((error) => this.scheduleError(error))
76 |     }
77 |   }
78 | 
79 |   onBrowserComplete () {
80 |     this.pendingCount--
81 | 
82 |     if (!this.pendingCount) {
83 |       // Ensure run_complete is emitted in the next tick
84 |       // so it is never emitted before browser_complete
85 |       setTimeout(() => {
86 |         this.emitter.emit('run_complete', this.runningBrowsers, this.runningBrowsers.getResults())
87 |       })
88 |     }
89 |   }
90 | }
91 | 
92 | Executor.factory = function (capturedBrowsers, config, emitter) {
93 |   return new Executor(capturedBrowsers, config, emitter)
94 | }
95 | 
96 | module.exports = Executor
97 | 


--------------------------------------------------------------------------------
/lib/file.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const path = require('path')
 4 | 
 5 | /**
 6 |  * File object used for tracking files in `file-list.js`.
 7 |  */
 8 | class File {
 9 |   constructor (path, mtime, doNotCache, type, isBinary, integrity) {
10 |     // used for serving (processed path, eg some/file.coffee -> some/file.coffee.js)
11 |     this.path = path
12 | 
13 |     // original absolute path, id of the file
14 |     this.originalPath = path
15 | 
16 |     // where the content is stored (processed)
17 |     this.contentPath = path
18 | 
19 |     // encodings format {[encodingType]: encodedContent}
20 |     //   example: {gzip: <Buffer 1f 8b 08...>}
21 |     this.encodings = Object.create(null)
22 | 
23 |     this.mtime = mtime
24 |     this.isUrl = false
25 | 
26 |     this.doNotCache = doNotCache === undefined ? false : doNotCache
27 | 
28 |     this.type = type
29 | 
30 |     // Tri state: null means probe file for binary.
31 |     this.isBinary = isBinary === undefined ? null : isBinary
32 | 
33 |     this.integrity = integrity
34 |   }
35 | 
36 |   /**
37 |    * Detect type from the file extension.
38 |    * @returns {string} detected file type or empty string
39 |    */
40 |   detectType () {
41 |     return path.extname(this.path).slice(1)
42 |   }
43 | 
44 |   toString () {
45 |     return this.path
46 |   }
47 | }
48 | 
49 | module.exports = File
50 | 


--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const constants = require('./constants')
 4 | const Server = require('./server')
 5 | const runner = require('./runner')
 6 | const stopper = require('./stopper')
 7 | const launcher = require('./launcher')
 8 | const cfg = require('./config')
 9 | 
10 | module.exports = {
11 |   constants: constants,
12 |   VERSION: constants.VERSION,
13 |   Server: Server,
14 |   runner: runner,
15 |   stopper: stopper,
16 |   launcher: launcher,
17 |   config: { parseConfig: cfg.parseConfig } // lets start with only opening up the `parseConfig` api
18 | }
19 | 


--------------------------------------------------------------------------------
/lib/init/color_schemes.js:
--------------------------------------------------------------------------------
 1 | const COLORS_ON = {
 2 |   RESET: '\x1B[39m',
 3 |   ANSWER: '\x1B[36m', // NYAN
 4 |   SUCCESS: '\x1B[32m', // GREEN
 5 |   QUESTION: '\x1B[1m', // BOLD
 6 |   question: function (str) {
 7 |     return this.QUESTION + str + '\x1B[22m'
 8 |   },
 9 |   success: function (str) {
10 |     return this.SUCCESS + str + this.RESET
11 |   }
12 | }
13 | 
14 | const COLORS_OFF = {
15 |   RESET: '',
16 |   ANSWER: '',
17 |   SUCCESS: '',
18 |   QUESTION: '',
19 |   question: function (str) {
20 |     return str
21 |   },
22 |   success: function (str) {
23 |     return str
24 |   }
25 | }
26 | 
27 | exports.ON = COLORS_ON
28 | exports.OFF = COLORS_OFF
29 | 


--------------------------------------------------------------------------------
/lib/init/formatters.js:
--------------------------------------------------------------------------------
  1 | 'use strict'
  2 | 
  3 | const path = require('path')
  4 | const FileUtils = require('../utils/file-utils')
  5 | 
  6 | function quote (value) {
  7 |   return `'${value}'`
  8 | }
  9 | 
 10 | function formatLine (items) {
 11 |   return items.map(quote).join(', ')
 12 | }
 13 | 
 14 | function formatMultiLines (items) {
 15 |   return items
 16 |     .map((file) => '\n      ' + file)
 17 |     .join(',')
 18 | }
 19 | 
 20 | function formatFiles (includedFiles, onlyServedFiles) {
 21 |   const lines = []
 22 |     .concat(includedFiles.map(quote))
 23 |     .concat(onlyServedFiles.map((file) => `{ pattern: ${quote(file)}, included: false }`))
 24 | 
 25 |   return formatMultiLines(lines)
 26 | }
 27 | 
 28 | function formatPreprocessors (preprocessors) {
 29 |   const lines = Object.keys(preprocessors)
 30 |     .map((pattern) => `${quote(pattern)}: [${formatLine(preprocessors[pattern])}]`)
 31 | 
 32 |   return formatMultiLines(lines)
 33 | }
 34 | 
 35 | function getConfigPath (file) {
 36 |   return path.join(__dirname, `/../../${file}`)
 37 | }
 38 | 
 39 | class JavaScriptFormatter {
 40 |   constructor () {
 41 |     this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.js')
 42 |     this.REQUIREJS_TEMPLATE_FILE = getConfigPath('requirejs.config.tpl.js')
 43 |   }
 44 | 
 45 |   generateConfigFile (answers) {
 46 |     const replacements = this.formatAnswers(answers)
 47 | 
 48 |     return FileUtils
 49 |       .readFile(this.TEMPLATE_FILE_PATH)
 50 |       .replace(/%(.*)%/g, (a, key) => replacements[key])
 51 |   }
 52 | 
 53 |   writeConfigFile (path, answers) {
 54 |     FileUtils.saveFile(path, this.generateConfigFile(answers))
 55 |   }
 56 | 
 57 |   writeRequirejsConfigFile (path) {
 58 |     FileUtils.copyFile(this.REQUIREJS_TEMPLATE_FILE, path)
 59 |   }
 60 | 
 61 |   formatAnswers (answers) {
 62 |     return {
 63 |       DATE: new Date(),
 64 |       BASE_PATH: answers.basePath,
 65 |       FRAMEWORKS: formatLine(answers.frameworks),
 66 |       FILES: formatFiles(answers.files, answers.onlyServedFiles),
 67 |       EXCLUDE: formatFiles(answers.exclude, []),
 68 |       AUTO_WATCH: answers.autoWatch ? 'true' : 'false',
 69 |       BROWSERS: formatLine(answers.browsers),
 70 |       PREPROCESSORS: formatPreprocessors(answers.preprocessors)
 71 |     }
 72 |   }
 73 | }
 74 | 
 75 | class CoffeeFormatter extends JavaScriptFormatter {
 76 |   constructor () {
 77 |     super()
 78 |     this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.coffee')
 79 |     this.REQUIREJS_TEMPLATE_FILE = getConfigPath('requirejs.config.tpl.coffee')
 80 |   }
 81 | }
 82 | 
 83 | class LiveFormatter extends JavaScriptFormatter {
 84 |   constructor () {
 85 |     super()
 86 |     this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.ls')
 87 |   }
 88 | }
 89 | 
 90 | class TypeFormatter extends JavaScriptFormatter {
 91 |   constructor () {
 92 |     super()
 93 |     this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.ts')
 94 |   }
 95 | }
 96 | 
 97 | exports.JavaScript = JavaScriptFormatter
 98 | exports.Coffee = CoffeeFormatter
 99 | exports.Live = LiveFormatter
100 | exports.Type = TypeFormatter
101 | 
102 | exports.createForPath = function (path) {
103 |   if (/\.coffee$/.test(path)) {
104 |     return new CoffeeFormatter()
105 |   }
106 | 
107 |   if (/\.ls$/.test(path)) {
108 |     return new LiveFormatter()
109 |   }
110 | 
111 |   if (/\.ts$/.test(path)) {
112 |     return new TypeFormatter()
113 |   }
114 | 
115 |   return new JavaScriptFormatter()
116 | }
117 | 


--------------------------------------------------------------------------------
/lib/init/log-queue.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const logQueue = []
 4 | function printLogQueue () {
 5 |   logQueue.forEach((log) => log())
 6 |   logQueue.length = 0
 7 | }
 8 | 
 9 | function push (log) {
10 |   logQueue.push(log)
11 | }
12 | 
13 | module.exports = {
14 |   printLogQueue, push
15 | }
16 | 


--------------------------------------------------------------------------------
/lib/launchers/capture_timeout.js:
--------------------------------------------------------------------------------
 1 | const log = require('../logger').create('launcher')
 2 | 
 3 | /**
 4 |  * Kill browser if it does not capture in given `captureTimeout`.
 5 |  */
 6 | function CaptureTimeoutLauncher (timer, captureTimeout) {
 7 |   if (!captureTimeout) {
 8 |     return
 9 |   }
10 | 
11 |   let pendingTimeoutId = null
12 | 
13 |   this.on('start', () => {
14 |     pendingTimeoutId = timer.setTimeout(() => {
15 |       pendingTimeoutId = null
16 |       if (this.state !== this.STATE_BEING_CAPTURED) {
17 |         return
18 |       }
19 | 
20 |       log.warn(`${this.name} has not captured in ${captureTimeout} ms, killing.`)
21 |       this.error = 'timeout'
22 |       this.kill()
23 |     }, captureTimeout)
24 |   })
25 | 
26 |   this.on('done', () => {
27 |     if (pendingTimeoutId) {
28 |       timer.clearTimeout(pendingTimeoutId)
29 |       pendingTimeoutId = null
30 |     }
31 |   })
32 | }
33 | 
34 | CaptureTimeoutLauncher.decoratorFactory = function (timer,
35 |   /* config.captureTimeout */ captureTimeout) {
36 |   return function (launcher) {
37 |     CaptureTimeoutLauncher.call(launcher, timer, captureTimeout)
38 |   }
39 | }
40 | 
41 | CaptureTimeoutLauncher.decoratorFactory.$inject = ['timer', 'config.captureTimeout']
42 | 
43 | module.exports = CaptureTimeoutLauncher
44 | 


--------------------------------------------------------------------------------
/lib/launchers/retry.js:
--------------------------------------------------------------------------------
 1 | const log = require('../logger').create('launcher')
 2 | 
 3 | function RetryLauncher (retryLimit) {
 4 |   this._retryLimit = retryLimit
 5 | 
 6 |   this.on('done', () => {
 7 |     if (!this.error) {
 8 |       return
 9 |     }
10 | 
11 |     if (this._retryLimit > 0) {
12 |       log.info(`Trying to start ${this.name} again (${retryLimit - this._retryLimit + 1}/${retryLimit}).`)
13 |       this.restart()
14 |       this._retryLimit--
15 |     } else if (this._retryLimit === 0) {
16 |       log.error(`${this.name} failed ${retryLimit} times (${this.error}). Giving up.`)
17 |     } else {
18 |       log.debug(`${this.name} failed (${this.error}). Not restarting.`)
19 |     }
20 |   })
21 | }
22 | 
23 | RetryLauncher.decoratorFactory = function (retryLimit) {
24 |   return function (launcher) {
25 |     RetryLauncher.call(launcher, retryLimit)
26 |   }
27 | }
28 | 
29 | RetryLauncher.decoratorFactory.$inject = ['config.retryLimit']
30 | 
31 | module.exports = RetryLauncher
32 | 


--------------------------------------------------------------------------------
/lib/middleware/source_files.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const querystring = require('querystring')
 4 | const common = require('./common')
 5 | 
 6 | const log = require('../logger').create('middleware:source-files')
 7 | 
 8 | function findByPath (files, path) {
 9 |   return Array.from(files).find((file) => file.path === path)
10 | }
11 | 
12 | function composeUrl (url, basePath, urlRoot) {
13 |   return url
14 |     .replace(urlRoot, '/')
15 |     .replace(/\?.*$/, '')
16 |     .replace(/^\/absolute/, '')
17 |     .replace(/^\/base/, basePath)
18 | }
19 | 
20 | // Source Files middleware is responsible for serving all the source files under the test.
21 | function createSourceFilesMiddleware (filesPromise, serveFile, basePath, urlRoot) {
22 |   return function (request, response, next) {
23 |     const requestedFilePath = composeUrl(request.url, basePath, urlRoot)
24 |     // When a path contains HTML-encoded characters (e.g %2F used by Jenkins for branches with /)
25 |     const requestedFilePathUnescaped = composeUrl(querystring.unescape(request.url), basePath, urlRoot)
26 | 
27 |     request.pause()
28 | 
29 |     log.debug(`Requesting ${request.url}`)
30 |     log.debug(`Fetching ${requestedFilePath}`)
31 | 
32 |     return filesPromise.then(function (files) {
33 |       // TODO(vojta): change served to be a map rather then an array
34 |       const file = findByPath(files.served, requestedFilePath) || findByPath(files.served, requestedFilePathUnescaped)
35 |       const rangeHeader = request.headers.range
36 | 
37 |       if (file) {
38 |         const acceptEncodingHeader = request.headers['accept-encoding']
39 |         const matchedEncoding = Object.keys(file.encodings).find(
40 |           (encoding) => new RegExp(`(^|.*, ?)${encoding}(,|$)`).test(acceptEncodingHeader)
41 |         )
42 |         const content = file.encodings[matchedEncoding] || file.content
43 | 
44 |         serveFile(file.contentPath || file.path, rangeHeader, response, function () {
45 |           if (/\?\w+/.test(request.url)) {
46 |             common.setHeavyCacheHeaders(response) // files with timestamps - cache one year, rely on timestamps
47 |           } else {
48 |             common.setNoCacheHeaders(response) // without timestamps - no cache (debug)
49 |           }
50 |           if (matchedEncoding) {
51 |             response.setHeader('Content-Encoding', matchedEncoding)
52 |           }
53 |         }, content, file.doNotCache)
54 |       } else {
55 |         next()
56 |       }
57 | 
58 |       request.resume()
59 |     })
60 |   }
61 | }
62 | 
63 | createSourceFilesMiddleware.$inject = [
64 |   'filesPromise', 'serveFile', 'config.basePath', 'config.urlRoot'
65 | ]
66 | 
67 | exports.create = createSourceFilesMiddleware
68 | 


--------------------------------------------------------------------------------
/lib/middleware/stopper.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Stopper middleware is responsible for communicating with `karma stop`.
 3 |  */
 4 | 
 5 | const log = require('../logger').create('middleware:stopper')
 6 | 
 7 | function createStopperMiddleware (urlRoot) {
 8 |   return function (request, response, next) {
 9 |     if (request.url !== urlRoot + 'stop') return next()
10 |     response.writeHead(200)
11 |     log.info('Stopping server')
12 |     response.end('OK')
13 |     process.kill(process.pid, 'SIGINT')
14 |   }
15 | }
16 | 
17 | createStopperMiddleware.$inject = ['config.urlRoot']
18 | exports.create = createStopperMiddleware
19 | 


--------------------------------------------------------------------------------
/lib/middleware/strip_host.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Strip hostname from request path
 3 |  * This to handle requests that uses (normally over proxies) an absoluteURI as request path
 4 |  */
 5 | 
 6 | function stripHostFromUrl (url) {
 7 |   return url.replace(/^https?:\/\/[a-z.:\d-]+\//, '/')
 8 | }
 9 | 
10 | // PUBLIC API
11 | exports.stripHost = stripHostFromUrl
12 | 


--------------------------------------------------------------------------------
/lib/plugin.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const fs = require('graceful-fs')
 4 | const path = require('path')
 5 | const helper = require('./helper')
 6 | 
 7 | const log = require('./logger').create('plugin')
 8 | 
 9 | const IGNORED_PACKAGES = ['karma-cli', 'karma-runner.github.com']
10 | 
11 | function resolve (plugins, emitter) {
12 |   const modules = []
13 | 
14 |   function requirePlugin (name) {
15 |     log.debug(`Loading plugin ${name}.`)
16 |     try {
17 |       modules.push(require(name))
18 |     } catch (e) {
19 |       if (e.code === 'MODULE_NOT_FOUND' && e.message.includes(name)) {
20 |         log.error(`Cannot find plugin "${name}".\n  Did you forget to install it?\n  npm install ${name} --save-dev`)
21 |       } else {
22 |         log.error(`Error during loading "${name}" plugin:\n  ${e.message}`)
23 |       }
24 |       emitter.emit('load_error', 'plug_in', name)
25 |     }
26 |   }
27 | 
28 |   plugins.forEach(function (plugin) {
29 |     if (helper.isString(plugin)) {
30 |       if (!plugin.includes('*')) {
31 |         requirePlugin(plugin)
32 |         return
33 |       }
34 |       const pluginDirectory = path.normalize(path.join(__dirname, '/../..'))
35 |       const regexp = new RegExp(`^${plugin.replace(/\*/g, '.*').replace(/\//g, '[/\\\\]')}`)
36 | 
37 |       log.debug(`Loading ${plugin} from ${pluginDirectory}`)
38 |       fs.readdirSync(pluginDirectory)
39 |         .map((e) => {
40 |           const modulePath = path.join(pluginDirectory, e)
41 |           if (e[0] === '@') {
42 |             return fs.readdirSync(modulePath).map((e) => path.join(modulePath, e))
43 |           }
44 |           return modulePath
45 |         })
46 |         .reduce((a, x) => a.concat(x), [])
47 |         .map((modulePath) => path.relative(pluginDirectory, modulePath))
48 |         .filter((moduleName) => !IGNORED_PACKAGES.includes(moduleName) && regexp.test(moduleName))
49 |         .forEach((pluginName) => requirePlugin(path.join(pluginDirectory, pluginName)))
50 |     } else if (helper.isObject(plugin)) {
51 |       log.debug(`Loading inline plugin defining ${Object.keys(plugin).join(', ')}.`)
52 |       modules.push(plugin)
53 |     } else {
54 |       log.error(`Invalid plugin ${plugin}`)
55 |       emitter.emit('load_error', 'plug_in', plugin)
56 |     }
57 |   })
58 | 
59 |   return modules
60 | }
61 | 
62 | /**
63 |   Create a function to handle errors in plugin loading.
64 |   @param {Object} injector, the dict of dependency injection objects.
65 |   @return function closed over injector, which reports errors.
66 | */
67 | function createInstantiatePlugin (injector) {
68 |   const emitter = injector.get('emitter')
69 |   // Cache to avoid report errors multiple times per plugin.
70 |   const pluginInstances = new Map()
71 |   return function instantiatePlugin (kind, name) {
72 |     if (pluginInstances.has(name)) {
73 |       return pluginInstances.get(name)
74 |     }
75 | 
76 |     let p
77 |     try {
78 |       p = injector.get(`${kind}:${name}`)
79 |       if (!p) {
80 |         log.error(`Failed to instantiate ${kind} ${name}`)
81 |         emitter.emit('load_error', kind, name)
82 |       }
83 |     } catch (e) {
84 |       if (e.message.includes(`No provider for "${kind}:${name}"`)) {
85 |         log.error(`Cannot load "${name}", it is not registered!\n  Perhaps you are missing some plugin?`)
86 |       } else {
87 |         log.error(`Cannot load "${name}"!\n  ` + e.stack)
88 |       }
89 |       emitter.emit('load_error', kind, name)
90 |     }
91 |     pluginInstances.set(name, p, `${kind}:${name}`)
92 |     return p
93 |   }
94 | }
95 | 
96 | createInstantiatePlugin.$inject = ['injector']
97 | 
98 | module.exports = { resolve, createInstantiatePlugin }
99 | 


--------------------------------------------------------------------------------
/lib/reporters/base_color.js:
--------------------------------------------------------------------------------
 1 | const { red, yellow, green, cyan } = require('@colors/colors/safe')
 2 | 
 3 | function BaseColorReporter () {
 4 |   this.USE_COLORS = true
 5 | 
 6 |   this.LOG_SINGLE_BROWSER = '%s: ' + cyan('%s') + '\n'
 7 |   this.LOG_MULTI_BROWSER = '%s %s: ' + cyan('%s') + '\n'
 8 | 
 9 |   this.SPEC_FAILURE = red('%s %s FAILED') + '\n'
10 |   this.SPEC_SLOW = yellow('%s SLOW %s: %s') + '\n'
11 |   this.ERROR = red('%s ERROR') + '\n'
12 | 
13 |   this.FINISHED_ERROR = red(' ERROR')
14 |   this.FINISHED_SUCCESS = green(' SUCCESS')
15 |   this.FINISHED_DISCONNECTED = red(' DISCONNECTED')
16 | 
17 |   this.X_FAILED = red(' (%d FAILED)')
18 | 
19 |   this.TOTAL_SUCCESS = green('TOTAL: %d SUCCESS') + '\n'
20 |   this.TOTAL_FAILED = red('TOTAL: %d FAILED, %d SUCCESS') + '\n'
21 | }
22 | 
23 | // PUBLISH
24 | module.exports = BaseColorReporter
25 | 


--------------------------------------------------------------------------------
/lib/reporters/dots.js:
--------------------------------------------------------------------------------
 1 | const BaseReporter = require('./base')
 2 | 
 3 | function DotsReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
 4 |   BaseReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
 5 | 
 6 |   const DOTS_WRAP = 80
 7 |   this.EXCLUSIVELY_USE_COLORS = false
 8 |   this.onRunStart = function () {
 9 |     this._browsers = []
10 |     this._dotsCount = 0
11 |   }
12 | 
13 |   this.onBrowserStart = function (browser) {
14 |     this._browsers.push(browser)
15 |   }
16 | 
17 |   this.writeCommonMsg = function (msg) {
18 |     if (this._dotsCount) {
19 |       this._dotsCount = 0
20 |       msg = '\n' + msg
21 |     }
22 | 
23 |     this.write(msg)
24 |   }
25 | 
26 |   this.specSuccess = function () {
27 |     this._dotsCount = (this._dotsCount + 1) % DOTS_WRAP
28 |     this.write(this._dotsCount ? '.' : '.\n')
29 |   }
30 | 
31 |   this.onBrowserComplete = function (browser) {
32 |     this.writeCommonMsg(this.renderBrowser(browser) + '\n')
33 |   }
34 | 
35 |   this.onRunComplete = function (browsers, results) {
36 |     if (browsers.length > 1 && !results.disconnected && !results.error) {
37 |       if (!results.failed) {
38 |         this.write(this.TOTAL_SUCCESS, results.success)
39 |       } else {
40 |         this.write(this.TOTAL_FAILED, results.failed, results.success)
41 |       }
42 |     }
43 |   }
44 | }
45 | 
46 | // PUBLISH
47 | module.exports = DotsReporter
48 | 


--------------------------------------------------------------------------------
/lib/reporters/dots_color.js:
--------------------------------------------------------------------------------
 1 | const DotsReporter = require('./dots')
 2 | const BaseColorReporter = require('./base_color')
 3 | 
 4 | function DotsColorReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
 5 |   DotsReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
 6 |   BaseColorReporter.call(this)
 7 |   this.EXCLUSIVELY_USE_COLORS = true
 8 | }
 9 | 
10 | // PUBLISH
11 | module.exports = DotsColorReporter
12 | 


--------------------------------------------------------------------------------
/lib/reporters/multi.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const helper = require('../helper')
 4 | 
 5 | class MultiReporter {
 6 |   constructor (reporters) {
 7 |     this._reporters = reporters
 8 |   }
 9 | 
10 |   addAdapter (adapter) {
11 |     this._reporters.forEach((reporter) => reporter.adapters.push(adapter))
12 |   }
13 | 
14 |   removeAdapter (adapter) {
15 |     this._reporters.forEach((reporter) => helper.arrayRemove(reporter.adapters, adapter))
16 |   }
17 | }
18 | 
19 | module.exports = MultiReporter
20 | 


--------------------------------------------------------------------------------
/lib/reporters/progress.js:
--------------------------------------------------------------------------------
 1 | const BaseReporter = require('./base')
 2 | 
 3 | function ProgressReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
 4 |   BaseReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
 5 | 
 6 |   this.EXCLUSIVELY_USE_COLORS = false
 7 |   this._browsers = []
 8 | 
 9 |   this.writeCommonMsg = function (msg) {
10 |     this.write(this._remove() + msg + this._render())
11 |   }
12 | 
13 |   this.specSuccess = function () {
14 |     this.write(this._refresh())
15 |   }
16 | 
17 |   this.onBrowserComplete = function () {
18 |     this.write(this._refresh())
19 |   }
20 | 
21 |   this.onRunStart = function () {
22 |     this._browsers = []
23 |     this._isRendered = false
24 |   }
25 | 
26 |   this.onBrowserStart = function (browser) {
27 |     this._browsers.push(browser)
28 | 
29 |     if (this._isRendered) {
30 |       this.write('\n')
31 |     }
32 | 
33 |     this.write(this._refresh())
34 |   }
35 | 
36 |   this._remove = function () {
37 |     if (!this._isRendered) {
38 |       return ''
39 |     }
40 | 
41 |     let cmd = ''
42 |     this._browsers.forEach(function () {
43 |       cmd += '\x1B[1A' + '\x1B[2K'
44 |     })
45 | 
46 |     this._isRendered = false
47 | 
48 |     return cmd
49 |   }
50 | 
51 |   this._render = function () {
52 |     this._isRendered = true
53 | 
54 |     return this._browsers.map(this.renderBrowser).join('\n') + '\n'
55 |   }
56 | 
57 |   this._refresh = function () {
58 |     return this._remove() + this._render()
59 |   }
60 | }
61 | 
62 | // PUBLISH
63 | module.exports = ProgressReporter
64 | 


--------------------------------------------------------------------------------
/lib/reporters/progress_color.js:
--------------------------------------------------------------------------------
 1 | const ProgressReporter = require('./progress')
 2 | const BaseColorReporter = require('./base_color')
 3 | 
 4 | function ProgressColorReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
 5 |   ProgressReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
 6 |   BaseColorReporter.call(this)
 7 |   this.EXCLUSIVELY_USE_COLORS = true
 8 | }
 9 | 
10 | // PUBLISH
11 | module.exports = ProgressColorReporter
12 | 


--------------------------------------------------------------------------------
/lib/stopper.js:
--------------------------------------------------------------------------------
 1 | const http = require('http')
 2 | const cfg = require('./config')
 3 | const logger = require('./logger')
 4 | const helper = require('./helper')
 5 | const { lookup } = require('./utils/dns-utils')
 6 | 
 7 | exports.stop = function (cliOptionsOrConfig, done) {
 8 |   cliOptionsOrConfig = cliOptionsOrConfig || {}
 9 |   const log = logger.create('stopper')
10 |   done = helper.isFunction(done) ? done : process.exit
11 | 
12 |   let config
13 |   if (cliOptionsOrConfig instanceof cfg.Config) {
14 |     config = cliOptionsOrConfig
15 |   } else {
16 |     logger.setupFromConfig({
17 |       colors: cliOptionsOrConfig.colors,
18 |       logLevel: cliOptionsOrConfig.logLevel
19 |     })
20 |     const deprecatedCliOptionsMessage =
21 |       'Passing raw CLI options to `stopper(config, done)` is deprecated. Use ' +
22 |       '`parseConfig(configFilePath, cliOptions, {promiseConfig: true, throwErrors: true})` ' +
23 |       'to prepare a processed `Config` instance and pass that as the ' +
24 |       '`config` argument instead.'
25 |     log.warn(deprecatedCliOptionsMessage)
26 |     try {
27 |       config = cfg.parseConfig(
28 |         cliOptionsOrConfig.configFile,
29 |         cliOptionsOrConfig,
30 |         {
31 |           promiseConfig: false,
32 |           throwErrors: true
33 |         }
34 |       )
35 |     } catch (parseConfigError) {
36 |       // TODO: change how `done` falls back to exit in next major version
37 |       //  SEE: https://github.com/karma-runner/karma/pull/3635#discussion_r565399378
38 |       done(1)
39 |     }
40 |   }
41 | 
42 |   const request = http.request({
43 |     hostname: config.hostname,
44 |     path: config.urlRoot + 'stop',
45 |     port: config.port,
46 |     method: 'GET',
47 |     lookup
48 |   })
49 | 
50 |   request.on('response', function (response) {
51 |     if (response.statusCode === 200) {
52 |       log.info('Server stopped.')
53 |       done(0)
54 |     } else {
55 |       log.error(`Server returned status code: ${response.statusCode}`)
56 |       done(1)
57 |     }
58 |   })
59 | 
60 |   request.on('error', function (e) {
61 |     if (e.code === 'ECONNREFUSED') {
62 |       log.error(`There is no server listening on port ${config.port}`)
63 |       done(1, e.code)
64 |     } else {
65 |       throw e
66 |     }
67 |   })
68 |   request.end()
69 | }
70 | 


--------------------------------------------------------------------------------
/lib/temp_dir.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const path = require('path')
 4 | const fs = require('graceful-fs')
 5 | const rimraf = require('rimraf')
 6 | const log = require('./logger').create('temp-dir')
 7 | 
 8 | const TEMP_DIR = require('os').tmpdir()
 9 | 
10 | module.exports = {
11 |   getPath (suffix) {
12 |     return path.normalize(TEMP_DIR + suffix)
13 |   },
14 | 
15 |   create (path) {
16 |     log.debug(`Creating temp dir at ${path}`)
17 | 
18 |     try {
19 |       fs.mkdirSync(path)
20 |     } catch (e) {
21 |       log.warn(`Failed to create a temp dir at ${path}`)
22 |     }
23 | 
24 |     return path
25 |   },
26 | 
27 |   remove (path, done) {
28 |     log.debug(`Cleaning temp dir ${path}`)
29 |     rimraf(path, done)
30 |   }
31 | }
32 | 


--------------------------------------------------------------------------------
/lib/url.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const path = require('path')
 4 | const { URL } = require('url')
 5 | 
 6 | /**
 7 |  * Url object used for tracking files in `file-list.js`.
 8 |  */
 9 | class Url {
10 |   constructor (path, type, integrity) {
11 |     this.path = path
12 |     this.originalPath = path
13 |     this.type = type
14 |     this.integrity = integrity
15 |     this.isUrl = true
16 |   }
17 | 
18 |   /**
19 |    * Detect type from the file extension in the path part of the URL.
20 |    * @returns {string} detected file type or empty string
21 |    */
22 |   detectType () {
23 |     return path.extname(new URL(this.path).pathname).slice(1)
24 |   }
25 | 
26 |   toString () {
27 |     return this.path
28 |   }
29 | }
30 | 
31 | module.exports = Url
32 | 


--------------------------------------------------------------------------------
/lib/utils/crypto-utils.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const crypto = require('crypto')
 4 | 
 5 | const CryptoUtils = {
 6 |   sha1 (data) {
 7 |     return crypto
 8 |       .createHash('sha1')
 9 |       .update(data)
10 |       .digest('hex')
11 |   }
12 | }
13 | 
14 | module.exports = CryptoUtils
15 | 


--------------------------------------------------------------------------------
/lib/utils/dns-utils.js:
--------------------------------------------------------------------------------
 1 | const dns = require('dns')
 2 | 
 3 | // Node >=17 has different DNS resolution (see
 4 | // https://github.com/nodejs/node/issues/40702), it resolves domains
 5 | // according to the OS settings instead of IPv4-address first. The Karma server
 6 | // only listens on IPv4 address (127.0.0.1) by default, but the requests are
 7 | // sent to `localhost` in several places and `localhost` is resolved into IPv6
 8 | // address (`::`). So the run/stop/proxy request is unable to reach the Karma
 9 | // server and produces an error. To mitigate this issue karma force the
10 | // IPv4-address first approach in Node >=17 as well.
11 | module.exports.lookup = (hostname, options, callback) => dns.lookup(hostname, { ...options, verbatim: false }, callback)
12 | 


--------------------------------------------------------------------------------
/lib/utils/file-utils.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const fs = require('graceful-fs')
 4 | 
 5 | const FileUtils = {
 6 |   readFile (path) {
 7 |     return fs.readFileSync(path).toString()
 8 |   },
 9 | 
10 |   saveFile (path, content) {
11 |     fs.writeFileSync(path, content)
12 |   },
13 | 
14 |   copyFile (src, dest) {
15 |     FileUtils.saveFile(dest, FileUtils.readFile(src))
16 |   },
17 | 
18 |   removeFileIfExists (src) {
19 |     if (fs.existsSync(src)) {
20 |       fs.unlinkSync(src)
21 |     }
22 |   }
23 | }
24 | 
25 | module.exports = FileUtils
26 | 


--------------------------------------------------------------------------------
/lib/utils/net-utils.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const net = require('net')
 4 | 
 5 | const NetUtils = {
 6 |   bindAvailablePort (port, listenAddress) {
 7 |     return new Promise((resolve, reject) => {
 8 |       const server = net.createServer()
 9 | 
10 |       server
11 |         .on('error', (err) => {
12 |           server.close()
13 |           if (err.code === 'EADDRINUSE' || err.code === 'EACCES') {
14 |             server.listen(++port, listenAddress)
15 |           } else {
16 |             reject(new Error(`Failed to bind ${port}: ` + (err.stack || err)))
17 |           }
18 |         })
19 |         .on('listening', () => {
20 |           resolve(server)
21 |         })
22 |         .listen(port, listenAddress)
23 |     })
24 |   }
25 | }
26 | 
27 | module.exports = NetUtils
28 | 


--------------------------------------------------------------------------------
/lib/utils/path-utils.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const path = require('path')
 4 | 
 5 | const PathUtils = {
 6 |   formatPathMapping (path, line, column) {
 7 |     return path + (line ? `:${line}` : '') + (column ? `:${column}` : '')
 8 |   },
 9 | 
10 |   calculateAbsolutePath (karmaRelativePath) {
11 |     return path.join(__dirname, '..', '..', karmaRelativePath)
12 |   }
13 | 
14 | }
15 | 
16 | module.exports = PathUtils
17 | 


--------------------------------------------------------------------------------
/lib/utils/pattern-utils.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const path = require('path')
 4 | 
 5 | const PatternUtils = {
 6 |   getBaseDir (pattern) {
 7 |     return pattern
 8 |       .replace(/[/\\][^/\\]*\*.*$/, '') // remove parts with *
 9 |       .replace(/[/\\][^/\\]*[!+]\(.*$/, '') // remove parts with !(...) and +(...)
10 |       .replace(/[/\\][^/\\]*\)\?.*$/, '') || path.sep // remove parts with (...)?
11 |   }
12 | }
13 | 
14 | module.exports = PatternUtils
15 | 


--------------------------------------------------------------------------------
/lib/watcher.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const mm = require('minimatch')
 4 | const braces = require('braces')
 5 | const PatternUtils = require('./utils/pattern-utils')
 6 | 
 7 | const helper = require('./helper')
 8 | const log = require('./logger').create('watcher')
 9 | 
10 | const DIR_SEP = require('path').sep
11 | 
12 | function watchPatterns (patterns, watcher) {
13 |   let expandedPatterns = []
14 |   patterns.map((pattern) => {
15 |     // expand ['a/{b,c}'] to ['a/b', 'a/c']
16 |     expandedPatterns = expandedPatterns.concat(braces.expand(pattern, { keepEscaping: true }))
17 |   })
18 |   expandedPatterns
19 |     .map(PatternUtils.getBaseDir)
20 |     .filter((path, index, paths) => paths.indexOf(path) === index) // filter unique values
21 |     .forEach((path, index, paths) => {
22 |       if (!paths.some((p) => path.startsWith(p + DIR_SEP))) {
23 |         watcher.add(path)
24 |         log.debug(`Watching "${path}"`)
25 |       }
26 |     })
27 | }
28 | 
29 | function checkAnyPathMatch (patterns, path) {
30 |   return patterns.some((pattern) => mm(path, pattern, { dot: true }))
31 | }
32 | 
33 | function createIgnore (patterns, excludes) {
34 |   return function (path, stat) {
35 |     if (stat && !stat.isDirectory()) {
36 |       return !checkAnyPathMatch(patterns, path) || checkAnyPathMatch(excludes, path)
37 |     } else {
38 |       return false
39 |     }
40 |   }
41 | }
42 | 
43 | function getWatchedPatterns (patterns) {
44 |   return patterns
45 |     .filter((pattern) => pattern.watched)
46 |     .map((pattern) => pattern.pattern)
47 | }
48 | 
49 | function watch (patterns, excludes, fileList, usePolling, emitter) {
50 |   const watchedPatterns = getWatchedPatterns(patterns)
51 |   // Lazy-load 'chokidar' to make the dependency optional. This is desired when
52 |   // third-party watchers are in use.
53 |   const chokidar = require('chokidar')
54 |   const watcher = new chokidar.FSWatcher({
55 |     usePolling: usePolling,
56 |     ignorePermissionErrors: true,
57 |     ignoreInitial: true,
58 |     ignored: createIgnore(watchedPatterns, excludes)
59 |   })
60 | 
61 |   watchPatterns(watchedPatterns, watcher)
62 | 
63 |   watcher
64 |     .on('add', (path) => fileList.addFile(helper.normalizeWinPath(path)))
65 |     .on('change', (path) => fileList.changeFile(helper.normalizeWinPath(path)))
66 |     .on('unlink', (path) => fileList.removeFile(helper.normalizeWinPath(path)))
67 |     .on('error', log.debug.bind(log))
68 | 
69 |   emitter.on('exit', (done) => {
70 |     watcher.close()
71 |     done()
72 |   })
73 | 
74 |   return watcher
75 | }
76 | 
77 | watch.$inject = [
78 |   'config.files',
79 |   'config.exclude',
80 |   'fileList',
81 |   'config.usePolling',
82 |   'emitter'
83 | ]
84 | 
85 | module.exports = watch
86 | 


--------------------------------------------------------------------------------
/logo/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karma-runner/karma/84f85e7016efc2266fa6b3465f494a3fa151c85c/logo/banner.png


--------------------------------------------------------------------------------
/logo/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karma-runner/karma/84f85e7016efc2266fa6b3465f494a3fa151c85c/logo/favicon.ico


--------------------------------------------------------------------------------
/logo/logo.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karma-runner/karma/84f85e7016efc2266fa6b3465f494a3fa151c85c/logo/logo.ai


--------------------------------------------------------------------------------
/logo/logo.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karma-runner/karma/84f85e7016efc2266fa6b3465f494a3fa151c85c/logo/logo.eps


--------------------------------------------------------------------------------
/logo/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karma-runner/karma/84f85e7016efc2266fa6b3465f494a3fa151c85c/logo/logo.png


--------------------------------------------------------------------------------
/release.config.js:
--------------------------------------------------------------------------------
 1 | module.exports = {
 2 |   // Add logging for releases until we are fully confident of the release solution.
 3 |   debug: true,
 4 |   branches: 'master',
 5 |   verifyConditions: [
 6 |     '@semantic-release/changelog',
 7 |     '@semantic-release/npm',
 8 |     '@semantic-release/github'
 9 |   ],
10 |   prepare: [
11 |     './tools/update-contributors',
12 |     '@semantic-release/changelog',
13 |     '@semantic-release/npm',
14 |     '@semantic-release/git'
15 |   ],
16 |   publish: [
17 |     '@semantic-release/npm',
18 |     '@semantic-release/github'
19 |   ],
20 |   success: [
21 |     '@semantic-release/github',
22 |     './tools/update-docs'
23 |   ],
24 | 
25 |   // The release rules determine what kind of release should be triggered
26 |   // based on the information included in the commit message. The default
27 |   // rules used by semantic-release are the same, but they are set explicitly
28 |   // for better visibility.
29 |   // See https://github.com/semantic-release/commit-analyzer/blob/master/lib/default-release-rules.js
30 |   releaseRules: [
31 |     { breaking: true, release: 'major' },
32 |     { revert: true, release: 'patch' },
33 |     { type: 'feat', release: 'minor' },
34 |     { type: 'fix', release: 'patch' },
35 |     { type: 'perf', release: 'patch' },
36 |     { type: 'build', release: 'patch' }
37 |   ],
38 | 
39 |   // The preset determines which commits are included in the changelog and how
40 |   // the changelog is formatted. The default value used by semantic-release is
41 |   // the same, but it is set explicitly for visibility.
42 |   // See https://semantic-release.gitbook.io/semantic-release/#commit-message-format
43 |   // See https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular
44 |   preset: 'angular'
45 | }
46 | 


--------------------------------------------------------------------------------
/requirejs.config.tpl.coffee:
--------------------------------------------------------------------------------
 1 | allTestFiles = []
 2 | TEST_REGEXP = /(spec|test)(\.coffee)?(\.js)?$/i
 3 | 
 4 | # Get a list of all the test files to include
 5 | Object.keys(window.__karma__.files).forEach (file) ->
 6 | 
 7 |   if TEST_REGEXP.test(file)
 8 |     # Normalize paths to RequireJS module names.
 9 |     # If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
10 |     # then do not normalize the paths
11 |     allTestFiles.push file.replace(/^\/base\/|\.js$/g, '')
12 |   return
13 | 
14 | require.config
15 |   # Karma serves files under /base, which is the basePath from your config file
16 |   baseUrl: "/base"
17 | 
18 |   # dynamically load all test files
19 |   deps: allTestFiles
20 | 
21 |   # we have to kickoff jasmine, as it is asynchronous
22 |   callback: window.__karma__.start
23 | 


--------------------------------------------------------------------------------
/requirejs.config.tpl.js:
--------------------------------------------------------------------------------
 1 | var allTestFiles = []
 2 | var TEST_REGEXP = /(spec|test)\.js$/i
 3 | 
 4 | // Get a list of all the test files to include
 5 | Object.keys(window.__karma__.files).forEach(function (file) {
 6 |   if (TEST_REGEXP.test(file)) {
 7 |     // Normalize paths to RequireJS module names.
 8 |     // If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
 9 |     // then do not normalize the paths
10 |     var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '')
11 |     allTestFiles.push(normalizedTestModule)
12 |   }
13 | })
14 | 
15 | require.config({
16 |   // Karma serves files under /base, which is the basePath from your config file
17 |   baseUrl: '/base',
18 | 
19 |   // dynamically load all test files
20 |   deps: allTestFiles,
21 | 
22 |   // we have to kickoff jasmine, as it is asynchronous
23 |   callback: window.__karma__.start
24 | })
25 | 


--------------------------------------------------------------------------------
/scripts/client.js:
--------------------------------------------------------------------------------
 1 | const browserify = require('browserify')
 2 | const watchify = require('watchify')
 3 | const { createWriteStream } = require('fs')
 4 | const { readFile } = require('fs').promises
 5 | 
 6 | const bundleResourceToFile = (inPath, outPath) => {
 7 |   return new Promise((resolve, reject) => {
 8 |     browserify(inPath).bundle()
 9 |       .once('error', (e) => reject(e))
10 |       .pipe(createWriteStream(outPath))
11 |       .once('finish', () => resolve())
12 |   })
13 | }
14 | 
15 | const bundleResource = (inPath) => {
16 |   return new Promise((resolve, reject) => {
17 |     browserify(inPath).bundle((err, buffer) => {
18 |       if (err != null) {
19 |         reject(err)
20 |         return
21 |       }
22 | 
23 |       resolve(buffer)
24 |     })
25 |   })
26 | }
27 | 
28 | const watchResourceToFile = (inPath, outPath) => {
29 |   const b = browserify({
30 |     entries: [inPath],
31 |     cache: {},
32 |     packageCache: {},
33 |     plugin: [watchify]
34 |   })
35 | 
36 |   const bundle = () => {
37 |     b.bundle()
38 |       .once('error', (e) => {
39 |         console.error(`Failed to bundle ${inPath} into ${outPath}.`)
40 |         console.error(e)
41 |       })
42 |       .pipe(createWriteStream(outPath))
43 |       .once('finish', () => console.log(`Bundled ${inPath} into ${outPath}.`))
44 |   }
45 | 
46 |   b.on('update', bundle)
47 |   bundle()
48 | }
49 | 
50 | const main = async () => {
51 |   if (process.argv[2] === 'build') {
52 |     await bundleResourceToFile('client/main.js', 'static/karma.js')
53 |     await bundleResourceToFile('context/main.js', 'static/context.js')
54 |   } else if (process.argv[2] === 'check') {
55 |     const expectedClient = await bundleResource('client/main.js')
56 |     const expectedContext = await bundleResource('context/main.js')
57 | 
58 |     const actualClient = await readFile('static/karma.js')
59 |     const actualContext = await readFile('static/context.js')
60 | 
61 |     if (Buffer.compare(expectedClient, actualClient) !== 0 || Buffer.compare(expectedContext, actualContext) !== 0) {
62 |       // eslint-disable-next-line no-throw-literal
63 |       throw 'Bundled client assets are outdated. Forgot to run "npm run build"?'
64 |     }
65 |   } else if (process.argv[2] === 'watch') {
66 |     watchResourceToFile('client/main.js', 'static/karma.js')
67 |     watchResourceToFile('context/main.js', 'static/context.js')
68 |   } else {
69 |     // eslint-disable-next-line no-throw-literal
70 |     throw `Unknown command: ${process.argv[2]}`
71 |   }
72 | }
73 | 
74 | main().catch((err) => {
75 |   console.error(err)
76 |   process.exit(1)
77 | })
78 | 


--------------------------------------------------------------------------------
/scripts/integration-tests.sh:
--------------------------------------------------------------------------------
1 | PKG_FILE="$PWD/$(npm pack)"
2 | git clone https://github.com/karma-runner/integration-tests.git --depth 1
3 | cd integration-tests
4 | ./run.sh $PKG_FILE
5 | 


--------------------------------------------------------------------------------
/scripts/karma-completion.sh:
--------------------------------------------------------------------------------
 1 | ###-begin-karma-completion-###
 2 | #
 3 | # karma command completion script
 4 | # This is stolen from npm. Thanks @isaac!
 5 | #
 6 | # Installation: karma completion >> ~/.bashrc  (or ~/.zshrc)
 7 | # Or, maybe: karma completion > /usr/local/etc/bash_completion.d/karma
 8 | #
 9 | 
10 | if type complete &>/dev/null; then
11 |   __karma_completion () {
12 |     local si="$IFS"
13 |     IFS=
#39;\n' COMPREPLY=($(COMP_CWORD="$COMP_CWORD" \
14 |                            COMP_LINE="$COMP_LINE" \
15 |                            COMP_POINT="$COMP_POINT" \
16 |                            karma completion -- "${COMP_WORDS[@]}" \
17 |                            2>/dev/null)) || return $?
18 |     IFS="$si"
19 |   }
20 |   complete -F __karma_completion karma
21 | elif type compdef &>/dev/null; then
22 |   __karma_completion() {
23 |     si=$IFS
24 |     compadd -- $(COMP_CWORD=$((CURRENT-1)) \
25 |                  COMP_LINE=$BUFFER \
26 |                  COMP_POINT=0 \
27 |                  karma completion -- "${words[@]}" \
28 |                  2>/dev/null)
29 |     IFS=$si
30 |   }
31 |   compdef __karma_completion karma
32 | elif type compctl &>/dev/null; then
33 |   __karma_completion () {
34 |     local cword line point words si
35 |     read -Ac words
36 |     read -cn cword
37 |     let cword-=1
38 |     read -l line
39 |     read -ln point
40 |     si="$IFS"
41 |     IFS=
#39;\n' reply=($(COMP_CWORD="$cword" \
42 |                        COMP_LINE="$line" \
43 |                        COMP_POINT="$point" \
44 |                        karma completion -- "${words[@]}" \
45 |                        2>/dev/null)) || return $?
46 |     IFS="$si"
47 |   }
48 |   compctl -K __karma_completion karma
49 | fi
50 | ###-end-karma-completion-###
51 | 


--------------------------------------------------------------------------------
/static/client.html:
--------------------------------------------------------------------------------
  1 | <!DOCTYPE html>
  2 | <!--
  3 | The entry point for client. This file is loaded just once when the client is captured.
  4 | It contains socket.io and all the communication logic.
  5 | -->
  6 | <html>
  7 | <head>
  8 |   %X_UA_COMPATIBLE%
  9 |   <title>Karma</title>
 10 |   <link href="favicon.ico" rel="icon" type="image/x-icon">
 11 |   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 12 |   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
 13 |   <style type="text/css">
 14 |     iframe {
 15 |       height: 100%;
 16 |       width: 100%;
 17 |       border: 0;
 18 |     }
 19 | 
 20 |     html, body {
 21 |       height: 100%;
 22 |       padding: 0;
 23 |       margin: 0;
 24 | 
 25 |       font-family: sans-serif;
 26 |     }
 27 | 
 28 |     .offline {
 29 |       background: #DDD;
 30 |     }
 31 | 
 32 |     .online {
 33 |       background: #6C4;
 34 |     }
 35 | 
 36 |     .idle {
 37 |     }
 38 | 
 39 |     .executing {
 40 |       background: #F99;
 41 |     }
 42 | 
 43 |     #banner {
 44 |       padding: 5px 10px;
 45 |     }
 46 | 
 47 |     h1 {
 48 |       font-size: 1.8em;
 49 |       margin: 0;
 50 |       padding: 0;
 51 |     }
 52 | 
 53 |     ul {
 54 |       margin: 0;
 55 |       padding: 0;
 56 | 
 57 |       list-style: none;
 58 |     }
 59 | 
 60 |     li {
 61 |       padding: 5px 12px;
 62 |     }
 63 | 
 64 |     .btn-debug {
 65 |       float: right;
 66 |     }
 67 | 
 68 |     .offline .btn-debug {
 69 |       display: none;
 70 |     }
 71 | 
 72 |     .btn-debug {
 73 |       -moz-box-shadow:inset 0px 1px 0px 0px #ffffff;
 74 |       -webkit-box-shadow:inset 0px 1px 0px 0px #ffffff;
 75 |       box-shadow:inset 0px 1px 0px 0px #ffffff;
 76 |       background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ffffff), color-stop(1, #f6f6f6) );
 77 |       background:-moz-linear-gradient( center top, #ffffff 5%, #f6f6f6 100% );
 78 |       filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f6f6f6');
 79 |       background-color:#ffffff;
 80 |       -moz-border-radius:6px;
 81 |       -webkit-border-radius:6px;
 82 |       border-radius:6px;
 83 |       border:1px solid #dcdcdc;
 84 |       display:inline-block;
 85 |       color:#666666;
 86 |       font-family:arial;
 87 |       font-size:15px;
 88 |       font-weight:bold;
 89 |       padding:6px 24px;
 90 |       text-decoration:none;
 91 |       text-shadow:1px 1px 0px #ffffff;
 92 |     }
 93 | 
 94 |     .btn-debug:hover {
 95 |       background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #f6f6f6), color-stop(1, #ffffff) );
 96 |       background:-moz-linear-gradient( center top, #f6f6f6 5%, #ffffff 100% );
 97 |       filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6f6', endColorstr='#ffffff');
 98 |       background-color:#f6f6f6;
 99 |     }
100 |   </style>
101 | </head>
102 | <body>
103 |   <div id="banner" class="offline">
104 |     <a href="#" onclick="window.open('debug.html%X_UA_COMPATIBLE_URL%')" class="btn-debug">DEBUG</a>
105 |     <h1 id="title">Karma - starting</h1>
106 |   </div>
107 | 
108 |   <ul id="browsers"></ul>
109 | 
110 |   <iframe id="context" src="about:blank" width="100%" height="100%"></iframe>
111 | 
112 |   <script src="socket.io/socket.io.min.js"></script>
113 |   <script src="karma.js"></script>
114 | </body>
115 | </html>
116 | 


--------------------------------------------------------------------------------
/static/context.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <!--
 3 | This is the execution context.
 4 | Loaded within the iframe.
 5 | Reloaded before every execution run.
 6 | -->
 7 | <html>
 8 | <head>
 9 |   <title></title>
10 |   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
11 |   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
12 | </head>
13 | <body>
14 |   <!-- The scripts need to be in the body DOM element, as some test running frameworks need the body
15 |        to have already been created so they can insert their magic into it. For example, if loaded
16 |        before body, Angular Scenario test framework fails to find the body and crashes and burns in
17 |        an epic manner. -->
18 |   <script src="context.js"></script>
19 |   <script type="text/javascript">
20 |     // Configure our Karma and set up bindings
21 |     %CLIENT_CONFIG%
22 |     window.__karma__.setupContext(window);
23 | 
24 |     // All served files with the latest timestamps
25 |     %MAPPINGS%
26 |   </script>
27 |   <!-- Dynamically replaced with <script> tags -->
28 |   %SCRIPTS%
29 |   <!-- Since %SCRIPTS% might include modules, the `loaded()` call needs to be in a module too.
30 |    This ensures all the tests will have been declared before karma tries to run them. -->
31 |   <script type="module">
32 |     window.__karma__.loaded();
33 |   </script>
34 |   <script nomodule>
35 |     window.__karma__.loaded();
36 |   </script>
37 | </body>
38 | </html>
39 | 


--------------------------------------------------------------------------------
/static/debug.html:
--------------------------------------------------------------------------------
 1 | <!doctype html>
 2 | <!--
 3 | This file is almost the same as context.html - loads all source files,
 4 | but its purpose is to be loaded in the main frame (not within an iframe),
 5 | just for immediate execution, without reporting to Karma server.
 6 | -->
 7 | <html>
 8 | <head>
 9 |   %X_UA_COMPATIBLE%
10 |   <title>Karma DEBUG RUNNER</title>
11 |   <link href="favicon.ico" rel="icon" type="image/x-icon" />
12 |   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
13 |   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
14 | </head>
15 | <body>
16 |   <!-- The scripts need to be at the end of body, so that some test running frameworks
17 |    (Angular Scenario, for example) need the body to be loaded so that it can insert its magic
18 |    into it. If it is before body, then it fails to find the body and crashes and burns in an epic
19 |    manner. -->
20 |   <script src="context.js"></script>
21 |   <script src="debug.js"></script>
22 |   <script type="text/javascript">
23 |     // Configure our Karma
24 |     %CLIENT_CONFIG%
25 | 
26 |     // All served files with the latest timestamps
27 |     %MAPPINGS%
28 |   </script>
29 |   <!-- Dynamically replaced with <script> tags -->
30 |   %SCRIPTS%
31 |   <!-- Since %SCRIPTS% might include modules, the `loaded()` call needs to be in a module too.
32 |    This ensures all the tests will have been declared before karma tries to run them. -->
33 |   <script type="module">
34 |     window.__karma__.loaded();
35 |   </script>
36 |   <script nomodule>
37 |     window.__karma__.loaded();
38 |   </script>
39 | </body>
40 | </html>
41 | 


--------------------------------------------------------------------------------
/static/debug.js:
--------------------------------------------------------------------------------
 1 | // Override the Karma setup for local debugging
 2 | window.__karma__.info = function (info) {
 3 |   if (info.dump && window.console) window.console.log(info.dump)
 4 | }
 5 | window.__karma__.complete = function () {
 6 |   if (window.console) window.console.log('Skipped ' + this.skipped + ' tests')
 7 | }
 8 | window.__karma__.skipped = 0
 9 | window.__karma__.result = window.console ? function (result) {
10 |   if (result.skipped) {
11 |     this.skipped++
12 |     return
13 |   }
14 |   var msg = result.success ? 'SUCCESS ' : 'FAILED '
15 |   window.console.log(msg + result.suite.join(' ') + ' ' + result.description)
16 | 
17 |   for (var i = 0; i < result.log.length; i++) {
18 |     // Printing error without losing stack trace
19 |     (function (err) {
20 |       setTimeout(function () {
21 |         window.console.error(err)
22 |       })
23 |     })(result.log[i])
24 |   }
25 | } : function () {}
26 | window.__karma__.loaded = function () {
27 |   this.start()
28 | }
29 | 


--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karma-runner/karma/84f85e7016efc2266fa6b3465f494a3fa151c85c/static/favicon.ico


--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
 1 | {
 2 |   "env": {
 3 |     "mocha": true
 4 |   },
 5 |   "globals": {
 6 |     "expect": true,
 7 |     "sinon": true
 8 |   },
 9 |   "rules": {
10 |     "no-unused-expressions": "off"
11 |   }
12 | }
13 | 


--------------------------------------------------------------------------------
/test/client/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "env": {
3 |     "jasmine": true,
4 |     "browser": true
5 |   }
6 | }


--------------------------------------------------------------------------------
/test/client/mocks.js:
--------------------------------------------------------------------------------
 1 | function Emitter () {
 2 |   var listeners = {}
 3 | 
 4 |   this.on = function (event, fn) {
 5 |     if (!listeners[event]) {
 6 |       listeners[event] = []
 7 |     }
 8 | 
 9 |     listeners[event].push(fn)
10 |   }
11 | 
12 |   this.emit = function (event) {
13 |     var eventListeners = listeners[event]
14 | 
15 |     if (!eventListeners) return
16 | 
17 |     var i = 0
18 |     while (i < eventListeners.length) {
19 |       eventListeners[i].apply(null, Array.prototype.slice.call(arguments, 1))
20 |       i++
21 |     }
22 |   }
23 | }
24 | 
25 | function MockSocket () {
26 |   Emitter.call(this)
27 | 
28 |   this.socket = { transport: { name: 'websocket' } }
29 | 
30 |   var transportName = 'websocket'
31 | 
32 |   this.io = {
33 |     engine: {
34 |       on: function (event, cb) {
35 |         if (event === 'upgrade' && transportName === 'websocket') {
36 |           cb()
37 |         }
38 |       }
39 |     }
40 |   }
41 | 
42 |   this.disconnect = function () {
43 |     this.emit('disconnect')
44 |   }
45 | 
46 |   // MOCK API
47 |   this._setTransportNameTo = function (name) {
48 |     transportName = name
49 |   }
50 | }
51 | 
52 | exports.Socket = MockSocket
53 | 


--------------------------------------------------------------------------------
/test/client/util.spec.js:
--------------------------------------------------------------------------------
 1 | var assert = require('assert')
 2 | 
 3 | var util = require('../../common/util')
 4 | 
 5 | describe('util', function () {
 6 |   it('parseQueryParams', function () {
 7 |     var params = util.parseQueryParams('?id=123&return_url=http://whatever.com')
 8 | 
 9 |     assert.deepStrictEqual(params, { id: '123', return_url: 'http://whatever.com' })
10 |   })
11 | })
12 | 


--------------------------------------------------------------------------------
/test/e2e/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "env": {
3 |     "browser": true
4 |   }
5 | }
6 | 


--------------------------------------------------------------------------------
/test/e2e/basic.feature:
--------------------------------------------------------------------------------
 1 | Feature: Basic Testrunner
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to be able to run tests from the command line.
 5 | 
 6 |   Scenario: Execute a test in ChromeHeadless
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['basic/plus.js', 'basic/test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       """
16 |     When I start Karma
17 |     Then it passes with:
18 |       """
19 |       ..
20 |       Chrome Headless
21 |       """
22 | 
23 |   Scenario: Execute a test in Firefox
24 |     Given a configuration with:
25 |       """
26 |       files = ['basic/plus.js', 'basic/test.js']
27 |       browsers = ['FirefoxHeadless']
28 |       plugins = [
29 |         'karma-jasmine',
30 |         'karma-firefox-launcher'
31 |       ]
32 |       """
33 |     When I start Karma
34 |     Then it passes with:
35 |       """
36 |       ..
37 |       Firefox
38 |       """
39 | 


--------------------------------------------------------------------------------
/test/e2e/custom-context.feature:
--------------------------------------------------------------------------------
 1 | Feature: Custom Context File
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want Karma to use a custom context file
 5 | 
 6 |   Scenario: Custom context.html file
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['context/*.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       customContextFile = 'context/context2.html'
16 |       """
17 |     When I start Karma
18 |     Then it passes with:
19 |       """
20 |       .
21 |       Chrome Headless
22 |       """
23 | 


--------------------------------------------------------------------------------
/test/e2e/displayname.feature:
--------------------------------------------------------------------------------
 1 | Feature: Custom Display-name
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to Karma to send custom display-name.
 5 | 
 6 |   Scenario: Execute a test in ChromeHeadless
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['basic/plus.js', 'basic/test.js'];
10 |       browsers = ['customChrome'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       customLaunchers = {
16 |         customChrome: {
17 |           base: 'ChromeHeadlessNoSandbox',
18 |           displayName: '42'
19 |         }
20 |       };
21 |       """
22 |     When I start Karma
23 |     Then it passes with:
24 |       """
25 |       ..
26 |       42
27 |       """
28 | 


--------------------------------------------------------------------------------
/test/e2e/error.feature:
--------------------------------------------------------------------------------
 1 | Feature: Error Display
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want Karma to log errors
 5 | 
 6 |   Scenario: Syntax Error in a test file
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['error/test.js', 'error/under-test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       """
16 |     When I start Karma
17 |     Then it fails with:
18 |       """
19 |       SyntaxError: Unexpected token '}'
20 |       """
21 | 
22 |   Scenario: Not single-run Syntax Error in a test file
23 |     Given a configuration with:
24 |       """
25 |       files = ['error/test.js', 'error/under-test.js'];
26 |       browsers = ['ChromeHeadlessNoSandbox'];
27 |       plugins = [
28 |         'karma-jasmine',
29 |         'karma-chrome-launcher'
30 |       ];
31 |       singleRun = false;
32 |       """
33 |     When I start a server in background
34 |     And I wait until server output contains:
35 |       """
36 |       Executed 2 of 2 (1 FAILED)
37 |       """
38 |     And I run Karma
39 |     Then it fails with like:
40 |       """
41 |       SyntaxError: Unexpected token '}'
42 |       """
43 | 
44 | Scenario: Missing module Error in a test file
45 |     Given a configuration with:
46 |       """
47 |       files = [{pattern: 'error/import-something-from-somewhere.js', type: 'module'}];
48 |       browsers = ['ChromeHeadlessNoSandbox'];
49 |       plugins = [
50 |         'karma-jasmine',
51 |         'karma-chrome-launcher'
52 |       ];
53 |       """
54 |     When I start Karma
55 |     Then it fails with:
56 |       """
57 |       Uncaught Error loading error/import-something-from-somewhere.js
58 |       """
59 | 


--------------------------------------------------------------------------------
/test/e2e/headers.feature:
--------------------------------------------------------------------------------
 1 | Feature: Custom Headers
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to Karma to send custom headers on files sent.
 5 | 
 6 |   Scenario: Simple file with headers
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['headers/*.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       customHeaders = [{
16 |         match: 'foo.js',
17 |         name: 'Custom-Header-Awesomeness',
18 |         value: 'there.is.no.dana.only.zuul'
19 |       }];
20 |       """
21 |     When I start Karma
22 |     Then it passes with:
23 |       """
24 |       .
25 |       Chrome Headless
26 |       """
27 | 


--------------------------------------------------------------------------------
/test/e2e/helpful-logs.feature:
--------------------------------------------------------------------------------
 1 | Feature: Helpful warning and errors
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to get messages which help me to fix problems
 5 | 
 6 |   Scenario: Karma fails to determine a file type from the file extension
 7 |     Given a configuration with:
 8 |       """
 9 |       files = [ 'modules/**/*.mjs' ];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       frameworks = ['mocha', 'chai'];
12 |       plugins = [
13 |         'karma-mocha',
14 |         'karma-chai',
15 |         'karma-chrome-launcher'
16 |       ];
17 |       """
18 |     When I start Karma
19 |     Then the stdout matches RegExp:
20 |       """
21 |       WARN \[middleware:karma\]: Unable to determine file type from the file extension, defaulting to js.
22 |         To silence the warning specify a valid type for .+modules/minus.mjs in the configuration file.
23 |         See https://karma-runner.github.io/latest/config/files.html
24 |       """
25 | 


--------------------------------------------------------------------------------
/test/e2e/launcher-error.feature:
--------------------------------------------------------------------------------
 1 | Feature: Launcher error
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want Karma to output stderr if a browser fails to connect.
 5 | 
 6 |   Scenario: Broken Browser
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['launcher-error/specs.js'];
10 |       browsers = [_resolve('launcher-error/fake-browser.sh')];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-script-launcher'
14 |       ];
15 |       """
16 |     When I start Karma
17 |     Then it fails with like:
18 |       """
19 |       Missing fake dependency
20 |       """
21 | 


--------------------------------------------------------------------------------
/test/e2e/load.feature:
--------------------------------------------------------------------------------
  1 | Feature: Basic Testrunner
  2 |   In order to use Karma
  3 |   As a person who wants to write great tests
  4 |   I want Karma to terminate upon misconfiguration
  5 | 
  6 |   Scenario: Execute with missing browser
  7 |     Given a configuration with:
  8 |       """
  9 |       files = ['basic/plus.js', 'basic/test.js'];
 10 |       browsers = ['NonExistingBrowser', 'ChromeHeadless'];
 11 |       plugins = [
 12 |         'karma-jasmine',
 13 |         'karma-chrome-launcher'
 14 |       ];
 15 |       singleRun = false
 16 |       """
 17 |     When I start Karma
 18 |     Then it fails with like:
 19 |       """
 20 |       Cannot load browser "NonExistingBrowser": it is not registered! Perhaps you are missing some plugin\?
 21 |       """
 22 |     And it fails with like:
 23 |       """
 24 |       Found 1 load error
 25 |       """
 26 | 
 27 |   Scenario: Execute with missing plugin
 28 |     Given a configuration with:
 29 |       """
 30 |       files = ['basic/plus.js', 'basic/test.js'];
 31 |       browsers = ['ChromeHeadlessNoSandbox'];
 32 |       plugins = [
 33 |         'karma-totally-non-existing-plugin',
 34 |         'karma-jasmine',
 35 |         'karma-chrome-launcher'
 36 |       ];
 37 |       singleRun = false
 38 |       """
 39 |     When I start Karma
 40 |     Then it fails with like:
 41 |       """
 42 |       Cannot find plugin "karma-totally-non-existing-plugin".
 43 |       [\s]+Did you forget to install it\?
 44 |       [\s]+npm install karma-totally-non-existing-plugin --save-dev
 45 |       """
 46 |     And it fails with like:
 47 |       """
 48 |       Found 1 load error
 49 |       """
 50 | 
 51 |   Scenario: Execute with missing reporter
 52 |     Given a configuration with:
 53 |       """
 54 |       files = ['basic/plus.js', 'basic/test.js'];
 55 |       browsers = ['ChromeHeadlessNoSandbox'];
 56 |       reporters = ['unreal-reporter']
 57 |       plugins = [
 58 |         'karma-jasmine',
 59 |         'karma-chrome-launcher'
 60 |       ];
 61 |       singleRun = false
 62 |       """
 63 |     When I start Karma
 64 |     Then it fails with like:
 65 |       """
 66 |       Can not load reporter "unreal-reporter", it is not registered!
 67 |       [\s]+Perhaps you are missing some plugin\?
 68 |       """
 69 |     And it fails with like:
 70 |       """
 71 |       Found 1 load error
 72 |       """
 73 | 
 74 |   Scenario: Execute with missing reporter, plugin and browser
 75 |     Given a configuration with:
 76 |       """
 77 |       files = ['basic/plus.js', 'basic/test.js'];
 78 |       browsers = ['NonExistingBrowser', 'ChromeHeadless'];
 79 |       reporters = ['unreal-reporter']
 80 |       plugins = [
 81 |         'karma-totally-non-existing-plugin',
 82 |         'karma-jasmine',
 83 |         'karma-chrome-launcher'
 84 |       ];
 85 |       singleRun = false
 86 |       """
 87 |     When I start Karma
 88 |     Then it fails with like:
 89 |       """
 90 |       Can not load reporter "unreal-reporter", it is not registered!
 91 |       [\s]+Perhaps you are missing some plugin\?
 92 |       """
 93 |     And it fails with like:
 94 |       """
 95 |       Cannot find plugin "karma-totally-non-existing-plugin".
 96 |       [\s]+Did you forget to install it\?
 97 |       [\s]+npm install karma-totally-non-existing-plugin --save-dev
 98 |       """
 99 |     And it fails with like:
100 |       """
101 |       Found 2 load errors
102 |       """
103 | 


--------------------------------------------------------------------------------
/test/e2e/middleware.feature:
--------------------------------------------------------------------------------
 1 | Feature: Middleware
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to use custom middleware with Karma.
 5 | 
 6 |   Scenario: Simple middleware
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['middleware/test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher',
14 |         _resolve('middleware/middleware')
15 |       ];
16 |       middleware = [
17 |         'foo'
18 |       ]
19 |       """
20 |     When I start Karma
21 |     Then it passes with:
22 |       """
23 |       .
24 |       Chrome Headless
25 |       """
26 | 
27 |   Scenario: Frameworks can add middleware
28 |     Given a configuration with:
29 |       """
30 |       files = ['middleware/test.js'];
31 |       browsers = ['ChromeHeadlessNoSandbox'];
32 |       plugins = [
33 |         'karma-jasmine',
34 |         'karma-chrome-launcher',
35 |         _resolve('middleware/middleware')
36 |       ];
37 |       frameworks = ['jasmine', 'foo']
38 |       """
39 |     When I start Karma
40 |     Then it passes with:
41 |       """
42 |       .
43 |       Chrome Headless
44 |       """
45 | 


--------------------------------------------------------------------------------
/test/e2e/mocharepoter.feature:
--------------------------------------------------------------------------------
 1 | Feature: Mocha reporter
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to be able to use the mocha reporter.
 5 | 
 6 |   Scenario: Execute a test in ChromeHeadless with colors
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['mocha/plus.js', 'mocha/test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       frameworks = ['mocha', 'chai']
12 |       colors = true
13 |       plugins = [
14 |         'karma-jasmine',
15 |         'karma-chrome-launcher',
16 |         'karma-mocha-reporter',
17 |         'karma-mocha',
18 |         'karma-chai'
19 |       ];
20 |       reporters = ['mocha'];
21 |       """
22 |     When I start Karma
23 |     Then it passes with like:
24 |       """
25 |       2 tests completed
26 |       """
27 | 
28 |   Scenario: Execute a test in ChromeHeadless with no-colors
29 |     Given a configuration with:
30 |       """
31 |       files = ['mocha/plus.js', 'mocha/test.js'];
32 |       browsers = ['ChromeHeadlessNoSandbox'];
33 |       frameworks = ['mocha', 'chai']
34 |       colors = false
35 |       plugins = [
36 |         'karma-jasmine',
37 |         'karma-chrome-launcher',
38 |         'karma-mocha-reporter',
39 |         'karma-mocha',
40 |         'karma-chai'
41 |       ];
42 |       reporters = ['mocha'];
43 |       """
44 |     When I start Karma
45 |     Then it passes with like:
46 |       """
47 |       ✔ 2 tests completed
48 |       """
49 | 


--------------------------------------------------------------------------------
/test/e2e/module-types.feature:
--------------------------------------------------------------------------------
 1 | Feature: ES Modules
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to use different script types with Karma.
 5 | 
 6 |   Scenario: Globbing modules, with both .js and .mjs extensions
 7 |     Given a configuration with:
 8 |       """
 9 |       files = [
10 |         { pattern: 'modules/**/*.js', type: 'module' },
11 |         { pattern: 'modules/**/*.mjs', type: 'module' },
12 |       ];
13 |       // Chrome fails on Travis, so we must use Firefox (which means we must
14 |       // manually enable modules).
15 |       customLaunchers = {
16 |         FirefoxWithModules: {
17 |           base: 'FirefoxHeadless',
18 |           prefs: {
19 |             'dom.moduleScripts.enabled': true
20 |           }
21 |         }
22 |       };
23 |       browsers = ['FirefoxWithModules'];
24 |       frameworks = ['mocha', 'chai'];
25 |       plugins = [
26 |         'karma-mocha',
27 |         'karma-chai',
28 |         'karma-firefox-launcher'
29 |       ];
30 |       """
31 |     When I start Karma
32 |     Then it passes with like:
33 |       """
34 |       Executed 4 of 4 SUCCESS
35 |       """
36 | 


--------------------------------------------------------------------------------
/test/e2e/pass-opts.feature:
--------------------------------------------------------------------------------
 1 | Feature: Passing Options
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to be able to pass arguments from the config file to the browser.
 5 | 
 6 |   Scenario: Passing Options to run on the Command Line
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['pass-opts/test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       singleRun = false;
16 |       """
17 |     When I start a server in background
18 |     And I wait until server output contains:
19 |       """
20 |       Executed 1 of 1 (1 FAILED)
21 |       """
22 |     And I run Karma with additional arguments: "-- arg1 arg2"
23 |     Then it passes with:
24 |       """
25 |       .
26 |       """
27 | 


--------------------------------------------------------------------------------
/test/e2e/proxy.feature:
--------------------------------------------------------------------------------
 1 | Feature: Proxying
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to Karma to proxy requests.
 5 | 
 6 |   Scenario: Simple file proxy
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['proxy/test.js', 'proxy/foo.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       proxies = {
16 |         '/foo.js': '/base/proxy/foo.js'
17 |       }
18 |       """
19 |     When I start Karma
20 |     Then it passes with:
21 |       """
22 |       .
23 |       Chrome Headless
24 |       """
25 | 
26 |   Scenario: Added by a framework
27 |     Given a configuration with:
28 |       """
29 |       files = ['proxy/test.js', 'proxy/foo.js'];
30 |       browsers = ['ChromeHeadlessNoSandbox'];
31 |       plugins = [
32 |         'karma-jasmine',
33 |         'karma-chrome-launcher',
34 |         _resolve('proxy/plugin')
35 |       ];
36 |       frameworks = ['jasmine', 'foo']
37 |       """
38 |     When I start Karma
39 |     Then it passes with:
40 |       """
41 |       .
42 |       Chrome Headless
43 |       """
44 | 
45 |   Scenario: URLRoot
46 |     Given a configuration with:
47 |       """
48 |       files = ['proxy/test.js', 'proxy/foo.js'];
49 |       browsers = ['ChromeHeadlessNoSandbox'];
50 |       plugins = [
51 |         'karma-jasmine',
52 |         'karma-chrome-launcher'
53 |       ];
54 |       urlRoot = '/__karma__/';
55 |       proxies = {
56 |         '/foo.js': '/base/proxy/foo.js'
57 |       }
58 |       """
59 |     When I start Karma
60 |     Then it passes with:
61 |       """
62 |       .
63 |       Chrome Headless
64 |       """
65 | 


--------------------------------------------------------------------------------
/test/e2e/reconnecting.feature:
--------------------------------------------------------------------------------
 1 | Feature: Passing Options
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to the browser to reconnect to Karma when it gets disconnected.
 5 | 
 6 |   Scenario: Manual disconnect from the browser
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['reconnecting/test.js', 'reconnecting/plus.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       client = {
16 |         jasmine: {
17 |           random: false
18 |         }
19 |       };
20 |       """
21 |     When I start Karma
22 |     Then it passes with:
23 |       """
24 |       LOG: '============== START TEST =============='
25 |       .....
26 |       Chrome Headless
27 |       """
28 | 


--------------------------------------------------------------------------------
/test/e2e/reporting.feature:
--------------------------------------------------------------------------------
 1 | Feature: Results reporting
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to Karma to report test results in the same order as they are executed.
 5 | 
 6 |   Scenario: Results appear as tests are executed
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['reporting/test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-mocha',
13 |         'karma-mocha-reporter',
14 |         'karma-chrome-launcher'
15 |       ];
16 |       frameworks = ['mocha']
17 |       reporters = ['mocha']
18 |       """
19 |     When I start Karma
20 |     Then it passes with like:
21 |     """
22 |     START:
23 |       Reporting order
24 |         ✔ sync test
25 |         ✔ async test
26 |       """
27 | 


--------------------------------------------------------------------------------
/test/e2e/restart-on-change.feature:
--------------------------------------------------------------------------------
 1 | Feature: Restart on file change
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want Karma to re-run tests whenever file changes.
 5 | 
 6 |   Scenario: Re-run tests when file changes
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['basic/plus.js', 'basic/test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       restartOnFileChange = true;
16 |       singleRun = false;
17 |       """
18 |     When I start a server in background
19 |     And I wait until server output contains:
20 |       """
21 |       ..
22 |       Chrome Headless
23 |       """
24 |     When I touch file: "basic/test.js"
25 |     Then the background stdout matches RegExp:
26 |       """
27 |       Executed 2 of 2 SUCCESS[\s\S]+Executed 2 of 2 SUCCESS
28 |       """
29 | 


--------------------------------------------------------------------------------
/test/e2e/runInParent.feature:
--------------------------------------------------------------------------------
 1 | Feature: runInParent option
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want Karma to run without iframe or opening new window
 5 | 
 6 |   Scenario: Execute a test in ChromeHeadless
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['basic/plus.js', 'basic/test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       client = {
16 |         useIframe: false,
17 |         runInParent: true
18 |       };
19 |       """
20 |     When I start Karma
21 |     Then it passes with:
22 |       """
23 |       ..
24 |       Chrome Headless
25 |       """
26 | 
27 |   Scenario: Execute a test in Firefox
28 |     Given a configuration with:
29 |       """
30 |       files = ['basic/plus.js', 'basic/test.js']
31 |       browsers = ['FirefoxHeadless']
32 |       plugins = [
33 |         'karma-jasmine',
34 |         'karma-firefox-launcher'
35 |       ]
36 |       client = {
37 |         useIframe: false,
38 |         runInParent: true
39 |       }
40 |       """
41 |     When I start Karma
42 |     Then it passes with:
43 |       """
44 |       ..
45 |       Firefox
46 |       """
47 | 


--------------------------------------------------------------------------------
/test/e2e/step_definitions/hooks.js:
--------------------------------------------------------------------------------
 1 | const { After, Before } = require('cucumber')
 2 | 
 3 | Before(function () {
 4 |   this.ensureSandbox()
 5 | })
 6 | 
 7 | After(async function () {
 8 |   await this.proxy.stopIfRunning()
 9 |   await this.stopBackgroundProcessIfRunning()
10 | })
11 | 


--------------------------------------------------------------------------------
/test/e2e/step_definitions/utils.js:
--------------------------------------------------------------------------------
 1 | const { promisify } = require('util')
 2 | 
 3 | const sleep = promisify(setTimeout)
 4 | 
 5 | module.exports.waitForCondition = async (evaluateCondition, timeout = 1000, customError = null) => {
 6 |   let remainingTime = timeout
 7 |   while (!evaluateCondition()) {
 8 |     if (remainingTime > 0) {
 9 |       await sleep(50)
10 |       remainingTime -= 50
11 |     } else {
12 |       if (customError != null) {
13 |         throw customError()
14 |       } else {
15 |         throw new Error(`Condition not fulfilled, waited ${timeout}ms`)
16 |       }
17 |     }
18 |   }
19 | }
20 | 


--------------------------------------------------------------------------------
/test/e2e/stop.feature:
--------------------------------------------------------------------------------
 1 | Feature: Stop karma
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to be able to stop Karma.
 5 | 
 6 |   Scenario: A server can't be stopped if it isn't running
 7 |     Given a default configuration
 8 |     When I stop Karma
 9 |     Then it fails with like:
10 |       """
11 |       ERROR \[stopper\]: There is no server listening on port [0-9]+
12 |       """
13 | 
14 |   Scenario: A server can be stopped
15 |     Given a configuration with:
16 |       """
17 |       files = ['basic/plus.js', 'basic/test.js'];
18 |       browsers = ['ChromeHeadlessNoSandbox'];
19 |       plugins = [
20 |         'karma-jasmine',
21 |         'karma-chrome-launcher'
22 |       ];
23 |       singleRun = false;
24 |       """
25 |     When I start a server in background
26 |     And I stop Karma
27 |     Then The server is dead with exit code 0
28 | 
29 |   Scenario: A server can be stopped and give informative output
30 |     Given a configuration with:
31 |       """
32 |       files = ['basic/plus.js', 'basic/test.js'];
33 |       browsers = ['ChromeHeadlessNoSandbox'];
34 |       plugins = [
35 |         'karma-jasmine',
36 |         'karma-chrome-launcher'
37 |       ];
38 |       singleRun = false;
39 |       """
40 |     When I start a server in background
41 |     And I stop Karma with additional arguments: "--log-level info"
42 |     Then it passes with like:
43 |     """
44 |     Server stopped.
45 |     """
46 | 
47 | 
48 |   Scenario: A server can be stopped programmatically
49 |     Given a configuration with:
50 |       """
51 |       files = ['basic/plus.js', 'basic/test.js'];
52 |       browsers = ['ChromeHeadlessNoSandbox'];
53 |       plugins = [
54 |         'karma-jasmine',
55 |         'karma-chrome-launcher'
56 |       ];
57 |       singleRun = false;
58 |       logLevel = 'error';
59 |       """
60 |     When I start a server in background
61 |     And I stop a server programmatically
62 |     Then The server is dead with exit code 0
63 |     And The stopper is dead with exit code 0
64 | 


--------------------------------------------------------------------------------
/test/e2e/support/basic/plus.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | // Some code under test
3 | function plus (a, b) {
4 |   return a + b
5 | }
6 | 


--------------------------------------------------------------------------------
/test/e2e/support/basic/test.js:
--------------------------------------------------------------------------------
 1 | /* globals plus */
 2 | describe('plus', function () {
 3 |   it('should pass', function () {
 4 |     expect(true).toBe(true)
 5 |   })
 6 | 
 7 |   it('should work', function () {
 8 |     expect(plus(1, 2)).toBe(3)
 9 |   })
10 | })
11 | 


--------------------------------------------------------------------------------
/test/e2e/support/behind-proxy/plus.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | // Some code under test
3 | function plus (a, b) {
4 |   return a + b
5 | }
6 | 


--------------------------------------------------------------------------------
/test/e2e/support/behind-proxy/test.js:
--------------------------------------------------------------------------------
 1 | /* globals plus */
 2 | describe('plus', function () {
 3 |   it('should pass', function () {
 4 |     expect(true).toBe(true)
 5 |   })
 6 | 
 7 |   it('should work', function () {
 8 |     expect(plus(1, 2)).toBe(3)
 9 |   })
10 | })
11 | 


--------------------------------------------------------------------------------
/test/e2e/support/browser-console/log.js:
--------------------------------------------------------------------------------
1 | 
2 | console.log('foo')
3 | console.debug('bar')
4 | console.info('baz')
5 | console.warn('foobar')
6 | console.error('barbaz')
7 | 


--------------------------------------------------------------------------------
/test/e2e/support/browser-console/test.js:
--------------------------------------------------------------------------------
1 | describe('Truism', function () {
2 |   it('should pass', function () {
3 |     expect(true).toBe(true)
4 |   })
5 | })
6 | 


--------------------------------------------------------------------------------
/test/e2e/support/context/context2.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <!--
 3 | This is the execution context.
 4 | Loaded within the iframe.
 5 | Reloaded before every execution run.
 6 | -->
 7 | <html>
 8 | <head>
 9 |   <title></title>
10 |   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
11 |   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
12 | </head>
13 | <body>
14 |   <!-- The scripts need to be in the body DOM element, as some test running frameworks need the body
15 |        to have already been created so they can insert their magic into it. For example, if loaded
16 |        before body, Angular Scenario test framework fails to find the body and crashes and burns in
17 |        an epic manner. -->
18 |   <div id="custom-context"></div>
19 |   <script src="context.js"></script>
20 |   <script type="text/javascript">
21 |     // Configure our Karma and set up bindings
22 |     %CLIENT_CONFIG%
23 |     window.__karma__.setupContext(window);
24 | 
25 |     // All served files with the latest timestamps
26 |     %MAPPINGS%
27 |   </script>
28 |   <!-- Dynamically replaced with <script> tags -->
29 |   %SCRIPTS%
30 |   <script type="text/javascript">
31 |     window.__karma__.loaded();
32 |   </script>
33 | </body>
34 | </html>
35 | 


--------------------------------------------------------------------------------
/test/e2e/support/context/test.js:
--------------------------------------------------------------------------------
1 | describe('custom context file', function () {
2 |   it('should be able to find custom DOM elements', function () {
3 |     expect(document.querySelector('#custom-context') == null).toBe(false)
4 |   })
5 | })
6 | 


--------------------------------------------------------------------------------
/test/e2e/support/env.js:
--------------------------------------------------------------------------------
1 | const { setDefaultTimeout } = require('cucumber')
2 | 
3 | setDefaultTimeout(60 * 1000)
4 | 


--------------------------------------------------------------------------------
/test/e2e/support/error/import-something-from-somewhere.js:
--------------------------------------------------------------------------------
1 | import { something } from './somewhere.js'
2 | console.log(something)
3 | 


--------------------------------------------------------------------------------
/test/e2e/support/error/test.js:
--------------------------------------------------------------------------------
 1 | /* global plus */
 2 | describe('plus', function () {
 3 |   it('should pass', function () {
 4 |     expect(true).toBe(true)
 5 |   })
 6 | 
 7 |   it('should work', function () {
 8 |     expect(plus(1, 2)).toBe(3)
 9 |   })
10 | })
11 | 


--------------------------------------------------------------------------------
/test/e2e/support/error/under-test.js:
--------------------------------------------------------------------------------
1 | // Some code under test, with syntax error
2 | }}}}
3 | 
4 | function plus(a, b) {
5 |   return a + b;
6 | }
7 | 


--------------------------------------------------------------------------------
/test/e2e/support/files/log_foo.js:
--------------------------------------------------------------------------------
1 | 
2 | console.log('foo')
3 | 


--------------------------------------------------------------------------------
/test/e2e/support/files/test.js:
--------------------------------------------------------------------------------
1 | describe('plus', function () {
2 |   it('should pass', function () {
3 |     expect(true).toBe(true)
4 |   })
5 | })
6 | 


--------------------------------------------------------------------------------
/test/e2e/support/headers/foo.js:
--------------------------------------------------------------------------------
1 | '/base/headers/foo.js source'
2 | 


--------------------------------------------------------------------------------
/test/e2e/support/headers/test.js:
--------------------------------------------------------------------------------
 1 | function httpGet (url) {
 2 |   const xmlHttp = new XMLHttpRequest()
 3 | 
 4 |   xmlHttp.open('GET', url, false)
 5 |   xmlHttp.send(null)
 6 | 
 7 |   return xmlHttp
 8 | }
 9 | 
10 | describe('setting custom headers', function () {
11 |   it('should get custom headers', function () {
12 |     expect(httpGet('/base/headers/foo.js').getResponseHeader('Custom-Header-Awesomeness')).toBe('there.is.no.dana.only.zuul')
13 |   })
14 | })
15 | 


--------------------------------------------------------------------------------
/test/e2e/support/launcher-error/fake-browser.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | echo "Missing fake dependency" 1>&2
4 | exit 1
5 | 


--------------------------------------------------------------------------------
/test/e2e/support/launcher-error/specs.js:
--------------------------------------------------------------------------------
1 | describe('something', function () {
2 |   it('should never happen anyway', function () {
3 |     expect(true).toBe(true)
4 |   })
5 | })
6 | 


--------------------------------------------------------------------------------
/test/e2e/support/middleware/middleware.js:
--------------------------------------------------------------------------------
 1 | function middleware (request, response, next) {
 2 |   if (/\/foo\.js/.test(request.normalizedUrl)) {
 3 |     response.setHeader('Content-Type', 'text/plain')
 4 |     response.writeHead(200)
 5 |     response.end('this is the middleware response')
 6 |     return
 7 |   }
 8 |   next()
 9 | }
10 | 
11 | function framework (config) {
12 |   config.middleware = config.middleware || []
13 |   config.middleware.push('foo')
14 | }
15 | 
16 | framework.$inject = ['config']
17 | 
18 | module.exports = {
19 |   'framework:foo': ['factory', framework],
20 |   'middleware:foo': ['value', middleware]
21 | }
22 | 


--------------------------------------------------------------------------------
/test/e2e/support/middleware/test.js:
--------------------------------------------------------------------------------
 1 | function httpGet (url) {
 2 |   const xmlHttp = new XMLHttpRequest()
 3 | 
 4 |   xmlHttp.open('GET', url, false)
 5 |   xmlHttp.send(null)
 6 | 
 7 |   return xmlHttp.responseText
 8 | }
 9 | 
10 | describe('foo', function () {
11 |   it('should should serve /foo.js', function () {
12 |     expect(httpGet('/foo.js')).toBe('this is the middleware response')
13 |   })
14 | })
15 | 


--------------------------------------------------------------------------------
/test/e2e/support/mocha/plus.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | // Some code under test
3 | function plus (a, b) {
4 |   return a + b
5 | }
6 | 


--------------------------------------------------------------------------------
/test/e2e/support/mocha/test.js:
--------------------------------------------------------------------------------
 1 | /* globals plus */
 2 | describe('plus', function () {
 3 |   it('should pass', function () {
 4 |     expect(true).to.be.true
 5 |   })
 6 | 
 7 |   it('should work', function () {
 8 |     expect(plus(1, 2)).to.equal(3)
 9 |   })
10 | })
11 | 


--------------------------------------------------------------------------------
/test/e2e/support/modules/__tests__/minus.test.mjs:
--------------------------------------------------------------------------------
 1 | import { minus } from '../minus.mjs'
 2 | 
 3 | describe('minus', function () {
 4 |   it('should pass', function () {
 5 |     expect(true).to.be.true
 6 |   })
 7 | 
 8 |   it('should work', function () {
 9 |     expect(minus(3, 2)).to.equal(1)
10 |   })
11 | })
12 | 


--------------------------------------------------------------------------------
/test/e2e/support/modules/__tests__/plus.test.js:
--------------------------------------------------------------------------------
 1 | import { plus } from '../plus.js'
 2 | 
 3 | describe('plus', function () {
 4 |   it('should pass', function () {
 5 |     expect(true).to.be.true
 6 |   })
 7 | 
 8 |   it('should work', function () {
 9 |     expect(plus(1, 2)).to.equal(3)
10 |   })
11 | })
12 | 


--------------------------------------------------------------------------------
/test/e2e/support/modules/minus.mjs:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | export function minus (a, b) {
3 |   return a - b
4 | }
5 | 


--------------------------------------------------------------------------------
/test/e2e/support/modules/plus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | export function plus (a, b) {
3 |   return a + b
4 | }
5 | 


--------------------------------------------------------------------------------
/test/e2e/support/pass-opts/test.js:
--------------------------------------------------------------------------------
1 | describe('config', function () {
2 |   it('should be passed through to the browser', function () {
3 |     expect(window.__karma__.config).toBeDefined()
4 |     expect(window.__karma__.config.args).toEqual(['arg1', 'arg2'])
5 |   })
6 | })
7 | 


--------------------------------------------------------------------------------
/test/e2e/support/proxy.js:
--------------------------------------------------------------------------------
 1 | const http = require('http')
 2 | const httpProxy = require('http-proxy')
 3 | const { promisify } = require('util')
 4 | 
 5 | module.exports = class Proxy {
 6 |   constructor () {
 7 |     this.running = false
 8 |     this.proxyPathRegExp = null
 9 | 
10 |     this.proxy = httpProxy.createProxyServer({
11 |       target: 'http://127.0.0.1:9876'
12 |     })
13 | 
14 |     this.proxy.on('error', (err) => {
15 |       console.log('support/proxy onerror', err)
16 |     })
17 | 
18 |     this.server = http.createServer((req, res) => {
19 |       const url = req.url
20 |       const match = url.match(this.proxyPathRegExp)
21 |       if (match) {
22 |         req.url = '/' + match[1]
23 |         this.proxy.web(req, res)
24 |       } else {
25 |         res.statusCode = 404
26 |         res.statusMessage = 'Not found'
27 |         res.end()
28 |       }
29 |     })
30 | 
31 |     this.server.on('clientError', (err) => {
32 |       console.log('support/proxy clientError', err)
33 |     })
34 |   }
35 | 
36 |   async start (port, proxyPath) {
37 |     this.proxyPathRegExp = new RegExp('^' + proxyPath + '(.*)')
38 |     await promisify(this.server.listen.bind(this.server))(port)
39 |     this.running = true
40 |   }
41 | 
42 |   async stopIfRunning () {
43 |     if (this.running) {
44 |       this.running = false
45 |       await promisify(this.server.close.bind(this.server))()
46 |     }
47 |   }
48 | }
49 | 


--------------------------------------------------------------------------------
/test/e2e/support/proxy/.tern-port:
--------------------------------------------------------------------------------
1 | 63683


--------------------------------------------------------------------------------
/test/e2e/support/proxy/foo.js:
--------------------------------------------------------------------------------
1 | '/base/proxy/foo.js source'
2 | 


--------------------------------------------------------------------------------
/test/e2e/support/proxy/plugin.js:
--------------------------------------------------------------------------------
 1 | function framework (config) {
 2 |   config.proxies = {
 3 |     '/foo.js': '/base/proxy/foo.js'
 4 |   }
 5 | }
 6 | 
 7 | framework.$inject = ['config']
 8 | 
 9 | module.exports = {
10 |   'framework:foo': ['factory', framework]
11 | }
12 | 


--------------------------------------------------------------------------------
/test/e2e/support/proxy/test.js:
--------------------------------------------------------------------------------
 1 | function httpGet (url) {
 2 |   const xmlHttp = new XMLHttpRequest()
 3 | 
 4 |   xmlHttp.open('GET', url, false)
 5 |   xmlHttp.send(null)
 6 | 
 7 |   return xmlHttp.responseText
 8 | }
 9 | 
10 | describe('foo', function () {
11 |   it('should should serve /foo.js', function () {
12 |     expect(httpGet('/foo.js')).toBe("'/base/proxy/foo.js source'\n")
13 |   })
14 | })
15 | 


--------------------------------------------------------------------------------
/test/e2e/support/reconnecting/plus.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | // Some code under test
3 | function plus (a, b) {
4 |   return a + b
5 | }
6 | 


--------------------------------------------------------------------------------
/test/e2e/support/reconnecting/test.js:
--------------------------------------------------------------------------------
 1 | /* globals plus */
 2 | describe('plus', function () {
 3 |   // super hacky way to get the actual socket to manipulate it...
 4 |   function socket () {
 5 |     return window.parent.karma.socket
 6 |   }
 7 | 
 8 |   it('should pass', function () {
 9 |     // In flaky fails we probably get two starts.
10 |     console.log('============== START TEST ==============')
11 |     expect(1).toBe(1)
12 |   })
13 | 
14 |   it('should disconnect', function (done) {
15 |     expect(2).toBe(2)
16 |     setTimeout(() => {
17 |       socket().disconnect()
18 |       done()
19 |     }, 500)
20 |   })
21 | 
22 |   it('should work', function () {
23 |     expect(plus(1, 2)).toBe(3)
24 |   })
25 | 
26 |   it('should re-connect', function () {
27 |     expect(4).toBe(4)
28 |     socket().connect()
29 |   })
30 | 
31 |   it('should work', function () {
32 |     expect(plus(3, 2)).toBe(5)
33 |   })
34 | })
35 | 


--------------------------------------------------------------------------------
/test/e2e/support/reporting/test.js:
--------------------------------------------------------------------------------
 1 | describe('Reporting order', () => {
 2 |   it('sync test', () => {
 3 |     // pass
 4 |   })
 5 | 
 6 |   it('async test', (done) => {
 7 |     setTimeout(done, 200)
 8 |   })
 9 | })
10 | 


--------------------------------------------------------------------------------
/test/e2e/support/tag/tag.js:
--------------------------------------------------------------------------------
 1 | /* eslint-disable no-unused-vars */
 2 | function isFirefoxBefore59 () {
 3 |   return typeof InstallTrigger !== 'undefined' && parseFloat(navigator.userAgent.match(/\d+\.\d+$/)) < 59
 4 | }
 5 | 
 6 | function containsJsTag () {
 7 |   const scripts = document.getElementsByTagName('script')
 8 |   for (let i = 0; i < scripts.length; i++) {
 9 |     if (scripts[i].type.indexOf(';version=') > -1) {
10 |       return true
11 |     }
12 |   }
13 |   return false
14 | }
15 | 


--------------------------------------------------------------------------------
/test/e2e/support/tag/test-with-version.js:
--------------------------------------------------------------------------------
1 | /* globals containsJsTag, isFirefoxBefore59 */
2 | describe('JavaScript version tag', function () {
3 |   it('should add the version tag, if Firefox is used', function () {
4 |     expect(containsJsTag()).toBe(isFirefoxBefore59())
5 |   })
6 | })
7 | 


--------------------------------------------------------------------------------
/test/e2e/support/tag/test-without-version.js:
--------------------------------------------------------------------------------
1 | /* globals containsJsTag */
2 | describe('JavaScript version tag', function () {
3 |   it('should not add the version tag for every browser', function () {
4 |     expect(containsJsTag()).toBe(false)
5 |   })
6 | })
7 | 


--------------------------------------------------------------------------------
/test/e2e/support/timeout/fake-browser.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | read
4 | 


--------------------------------------------------------------------------------
/test/e2e/support/timeout/specs.js:
--------------------------------------------------------------------------------
1 | describe('something', function () {
2 |   it('should never happen anyway', function () {
3 |     expect(true).toBe(true)
4 |   })
5 | })
6 | 


--------------------------------------------------------------------------------
/test/e2e/tag.feature:
--------------------------------------------------------------------------------
 1 | Feature: JavaScript Tag
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want to add a JavaScript version tag in Firefox only.
 5 | 
 6 |   Scenario: Execute a test in Firefox with version, with JavaScript tag
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['tag/tag.js', 'tag/test-with-version.js'];
10 |       browsers = ['FirefoxHeadless']
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-firefox-launcher'
14 |       ]
15 |       """
16 |     When I start Karma
17 |     Then it passes with like:
18 |       """
19 |       .
20 |       Firefox
21 |       """
22 | 
23 |   Scenario: Execute a test in ChromeHeadless with version, without JavaScript tag
24 |     Given a configuration with:
25 |       """
26 |       files = ['tag/tag.js', 'tag/test-with-version.js'];
27 |       browsers = ['ChromeHeadlessNoSandbox'];
28 |       plugins = [
29 |         'karma-jasmine',
30 |         'karma-chrome-launcher'
31 |       ];
32 |       """
33 |     When I start Karma
34 |     Then it passes with:
35 |       """
36 |       .
37 |       Chrome
38 |       """
39 | 
40 |   Scenario: Execute a test in Firefox without version, without JavaScript tag
41 |     Given a configuration with:
42 |       """
43 |       files = ['tag/tag.js', 'tag/test-without-version.js'];
44 |       browsers = ['FirefoxHeadless']
45 |       plugins = [
46 |         'karma-jasmine',
47 |         'karma-firefox-launcher'
48 |       ]
49 |       """
50 |     When I start Karma
51 |     Then it passes with:
52 |       """
53 |       .
54 |       Firefox
55 |       """
56 | 
57 |   Scenario: Execute a test in ChromeHeadless without version, without JavaScript tag
58 |     Given a configuration with:
59 |       """
60 |       files = ['tag/tag.js', 'tag/test-without-version.js'];
61 |       browsers = ['ChromeHeadlessNoSandbox'];
62 |       plugins = [
63 |         'karma-jasmine',
64 |         'karma-chrome-launcher'
65 |       ];
66 |       """
67 |     When I start Karma
68 |     Then it passes with:
69 |       """
70 |       .
71 |       Chrome
72 |       """
73 | 


--------------------------------------------------------------------------------
/test/e2e/timeout.feature:
--------------------------------------------------------------------------------
 1 | Feature: Timeout
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want Karma to timeout if a browser fails to connect.
 5 | 
 6 |   Scenario: Broken Browser
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['timeout/specs.js'];
10 |       browsers = [_resolve('timeout/fake-browser.sh')];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-script-launcher'
14 |       ];
15 |       captureTimeout = 100
16 |       """
17 |     When I start Karma
18 |     Then it fails with like:
19 |       """
20 |       has not captured in 100 ms
21 |       """
22 | 


--------------------------------------------------------------------------------
/test/e2e/upstream-proxy.feature:
--------------------------------------------------------------------------------
 1 | Feature: UpstreamProxy
 2 |   In order to use Karma
 3 |   As a person who wants to write great tests
 4 |   I want Karma to work when it is behind a proxy that prepends to the base path.
 5 | 
 6 |   Scenario: UpstreamProxy
 7 |     Given a configuration with:
 8 |       """
 9 |       files = ['behind-proxy/plus.js', 'behind-proxy/test.js'];
10 |       browsers = ['ChromeHeadlessNoSandbox'];
11 |       plugins = [
12 |         'karma-jasmine',
13 |         'karma-chrome-launcher'
14 |       ];
15 |       urlRoot = '/__karma__/';
16 |       upstreamProxy = {
17 |         path: '/__proxy__/'
18 |       };
19 |       """
20 |     And a proxy on port 9875 that prepends '/__proxy__/' to the base path
21 |     When I start Karma with additional arguments: "--log-level debug"
22 |     Then it passes with regexp:
23 |       """
24 |       Chrome Headless.*Executed.*SUCCESS
25 |       """
26 | 


--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --reporter dot
2 | --ui bdd
3 | test/unit/mocha-globals.js
4 | 


--------------------------------------------------------------------------------
/test/unit/browser_result.spec.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | describe('BrowserResult', () => {
 4 |   const BrowserResult = require('../../lib/browser_result')
 5 |   let result = null
 6 | 
 7 |   const successResultFromBrowser = {
 8 |     success: true,
 9 |     skipped: false,
10 |     time: 100
11 |   }
12 | 
13 |   const failedResultFromBrowser = {
14 |     success: false,
15 |     skipped: false,
16 |     time: 200
17 |   }
18 | 
19 |   const skippedResultFromBrowser = {
20 |     success: false,
21 |     skipped: true,
22 |     time: 0
23 |   }
24 | 
25 |   beforeEach(() => {
26 |     sinon.stub(Date, 'now')
27 |     Date.now.returns(123)
28 |     result = new BrowserResult()
29 |   })
30 | 
31 |   afterEach(() => {
32 |     Date.now.restore()
33 |   })
34 | 
35 |   it('should compute totalTime', () => {
36 |     Date.now.returns(223)
37 |     result.totalTimeEnd()
38 |     expect(result.totalTime).to.equal(223 - 123)
39 |   })
40 | 
41 |   it('should sum success/failed/skipped', () => {
42 |     result.add(successResultFromBrowser)
43 |     expect(result.success).to.equal(1)
44 |     expect(result.failed).to.equal(0)
45 |     expect(result.skipped).to.equal(0)
46 | 
47 |     result.add(failedResultFromBrowser)
48 |     expect(result.success).to.equal(1)
49 |     expect(result.failed).to.equal(1)
50 |     expect(result.skipped).to.equal(0)
51 | 
52 |     result.add(successResultFromBrowser)
53 |     expect(result.success).to.equal(2)
54 |     expect(result.failed).to.equal(1)
55 |     expect(result.skipped).to.equal(0)
56 | 
57 |     result.add(skippedResultFromBrowser)
58 |     expect(result.success).to.equal(2)
59 |     expect(result.failed).to.equal(1)
60 |     expect(result.skipped).to.equal(1)
61 |   })
62 | 
63 |   it('should sum net time of all results', () => {
64 |     result.add(successResultFromBrowser)
65 |     result.add(failedResultFromBrowser)
66 |     expect(result.netTime).to.equal(300)
67 | 
68 |     result.add(successResultFromBrowser)
69 |     expect(result.netTime).to.equal(400)
70 |   })
71 | })
72 | 


--------------------------------------------------------------------------------
/test/unit/certificates/server.crt:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE-----
 2 | MIICAzCCAWwCCQDlm49KXF45gzANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJB
 3 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTEQMA4GA1UEChMHR3J1bnRKUzEQMA4GA1UE
 4 | AxMHMC4wLjAuMDAeFw0xNDAyMTkyMzE1NDRaFw0xNTAyMTkyMzE1NDRaMEYxCzAJ
 5 | BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRAwDgYDVQQKEwdHcnVudEpT
 6 | MRAwDgYDVQQDEwcwLjAuMC4wMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCm
 7 | ipCqKyQ6aJJiVMvXZVoTw9sEC5dKFA35n15r9fG565/Zj8LVg/kgt79am1bnF+/H
 8 | F880f8kfDsgEaAC1qzo8XU8yqt+UoFOB2Ncw76g6B6ZiuC2R1uHyD/46sYtMejy3
 9 | n8EcTk9jNmNlglF6Ig6/hWcz+0XH6QjJT0lAM06tswIDAQABMA0GCSqGSIb3DQEB
10 | BQUAA4GBADnTBlN7+Aa8zj2zsUBSUv9w7iYut3ZDvrEY+IJt8EurwA6+Q7rQqVsY
11 | an5ztiEESriWvqNIfvWb+Yekhv9sISJFMfApVbimmT6QseQcFEIlRNW5cfukHQVH
12 | 9dBI7upQO2vN7N9ABo4a3aBANMBxIvCnE+adiqNOTJF/8qkiAFY9
13 | -----END CERTIFICATE-----


--------------------------------------------------------------------------------
/test/unit/certificates/server.key:
--------------------------------------------------------------------------------
 1 | -----BEGIN RSA PRIVATE KEY-----
 2 | MIICWwIBAAKBgQCmipCqKyQ6aJJiVMvXZVoTw9sEC5dKFA35n15r9fG565/Zj8LV
 3 | g/kgt79am1bnF+/HF880f8kfDsgEaAC1qzo8XU8yqt+UoFOB2Ncw76g6B6ZiuC2R
 4 | 1uHyD/46sYtMejy3n8EcTk9jNmNlglF6Ig6/hWcz+0XH6QjJT0lAM06tswIDAQAB
 5 | AoGATqG34hCSf11mWDUPNXjuCcz8eLF8Ugab/pMngrPR2OWOSKue4y73jmITYBVd
 6 | 96iOlqMAOxpmfFp/R81PIHdi++Bax1NfSBT8tK0U7HHzkbHEXyvHiBSug78Y14h8
 7 | Y/NMZXEvVapY7lapr5ZgOSf2rcKOlceMRsoohl6bGc+55BECQQDPZTw5WxDDe7/W
 8 | oYzHy7abLw+A92cP8A6qlwXBik9ko6jtYXvoI454OIr6RsHoFPU9bUkx5G1fvOUZ
 9 | J3sxfxMZAkEAzZJEwcvmxHizX/2NZZ8LvVyWGpao07bBcAEvDXDZFOZqKUujukOe
10 | iilQD6JZDJTmW9RJmOgdQKeL9ZaTlX3MqwJASMJrbnPUXcB8fQAQM8f0OF06QzSI
11 | o77EZnS1QEEVuWjxStZ4ceiHgwXTPBq2zIUNxI8irq5E8OGEPl7riWHbgQJARzqL
12 | QGsaRrFb1cLRH4kAVFikWgoh7VnBpMGEQC/9x9QerLhcvsl3QYAXEZO7LzTYrLDd
13 | 33Ft0V08jZfjA0VXiQJAOwX6glfDKf79AK1sifFQc/v0Yu87LIOAwp0zLlsnO0Q9
14 | xQV3TdjlNQebfTG+Uw1tmbcCb2wcGFfD199IHpAzIA==
15 | -----END RSA PRIVATE KEY-----


--------------------------------------------------------------------------------
/test/unit/completion.spec.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const c = require('../../lib/completion')
 4 | 
 5 | describe('completion', () => {
 6 |   let completion
 7 | 
 8 |   function mockEnv (line) {
 9 |     const words = line.split(' ')
10 | 
11 |     return {
12 |       words: words,
13 |       count: words.length,
14 |       last: words[words.length - 1],
15 |       prev: words[words.length - 2]
16 |     }
17 |   }
18 | 
19 |   beforeEach(() => {
20 |     sinon.stub(console, 'log').callsFake((msg) => completion.push(msg))
21 |     completion = []
22 |   })
23 | 
24 |   describe('opositeWord', () => {
25 |     it('should handle --no-x args', () => {
26 |       expect(c.opositeWord('--no-single-run')).to.equal('--single-run')
27 |     })
28 | 
29 |     it('should handle --x args', () => {
30 |       expect(c.opositeWord('--browsers')).to.equal('--no-browsers')
31 |     })
32 | 
33 |     it('should ignore args without --', () => {
34 |       expect(c.opositeWord('start')).to.equal(null)
35 |     })
36 |   })
37 | 
38 |   describe('sendCompletion', () => {
39 |     it('should filter only words matching last typed partial', () => {
40 |       c.sendCompletion(['start', 'init', 'run'], mockEnv('in'))
41 |       expect(completion).to.deep.equal(['init'])
42 |     })
43 | 
44 |     it('should filter out already used words/args', () => {
45 |       c.sendCompletion(['--single-run', '--port', '--xxx'], mockEnv('start --single-run '))
46 |       expect(completion).to.deep.equal(['--port', '--xxx'])
47 |     })
48 | 
49 |     it('should filter out already used oposite words', () => {
50 |       c.sendCompletion(['--auto-watch', '--port'], mockEnv('start --no-auto-watch '))
51 |       expect(completion).to.deep.equal(['--port'])
52 |     })
53 |   })
54 | 
55 |   describe('complete', () => {
56 |     it('should complete the basic commands', () => {
57 |       c.complete(mockEnv(''))
58 |       expect(completion).to.deep.equal(['start', 'init', 'run'])
59 | 
60 |       completion.length = 0 // reset
61 |       c.complete(mockEnv('s'))
62 |       expect(completion).to.deep.equal(['start'])
63 |     })
64 |   })
65 | })
66 | 


--------------------------------------------------------------------------------
/test/unit/emitter_wrapper.spec.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const EventEmitter = require('events').EventEmitter
 4 | const EmitterWrapper = require('../../lib/emitter_wrapper')
 5 | 
 6 | describe('emitter_wrapper', () => {
 7 |   let emitter
 8 |   let wrapped
 9 | 
10 |   beforeEach(() => {
11 |     emitter = new EventEmitter()
12 |     emitter.aMethod = (e) => true
13 |     emitter.on('anEvent', emitter.aMethod)
14 |     wrapped = new EmitterWrapper(emitter)
15 |   })
16 | 
17 |   describe('addListener', () => {
18 |     const aListener = (e) => true
19 | 
20 |     it('should add a listener to the wrapped emitter', () => {
21 |       wrapped.addListener('anEvent', aListener)
22 |       expect(emitter.listeners('anEvent')).to.contain(aListener)
23 |     })
24 | 
25 |     it('returns the wrapped emitter', () => {
26 |       expect(wrapped.addListener('anEvent', aListener)).to.equal(wrapped)
27 |     })
28 |   })
29 | 
30 |   describe('removeAllListeners', () => {
31 |     const aListener = (e) => true
32 | 
33 |     beforeEach(() => {
34 |       wrapped.addListener('anEvent', aListener)
35 |     })
36 | 
37 |     it('should remove listeners that were attached via the wrapper', () => {
38 |       wrapped.removeAllListeners()
39 |       expect(emitter.listeners('anEvent')).not.to.contain(aListener)
40 |     })
41 | 
42 |     it('should not remove listeners that were attached to the original emitter', () => {
43 |       wrapped.removeAllListeners()
44 |       expect(emitter.listeners('anEvent')).to.contain(emitter.aMethod)
45 |     })
46 | 
47 |     it('should remove only matching listeners when called with an event name', () => {
48 |       const anotherListener = (e) => true
49 |       wrapped.addListener('anotherEvent', anotherListener)
50 |       wrapped.removeAllListeners('anEvent')
51 |       expect(emitter.listeners('anEvent')).not.to.contain(aListener)
52 |       expect(emitter.listeners('anotherEvent')).to.contain(anotherListener)
53 |     })
54 | 
55 |     it('returns the wrapped emitter', () => {
56 |       expect(wrapped.addListener('anEvent', aListener)).to.equal(wrapped)
57 |     })
58 |   })
59 | })
60 | 


--------------------------------------------------------------------------------
/test/unit/executor.spec.js:
--------------------------------------------------------------------------------
  1 | 'use strict'
  2 | 
  3 | const Browser = require('../../lib/browser')
  4 | const BrowserCollection = require('../../lib/browser_collection')
  5 | const EventEmitter = require('../../lib/events').EventEmitter
  6 | const Executor = require('../../lib/executor')
  7 | 
  8 | const log = require('../../lib/logger').create()
  9 | 
 10 | describe('executor', () => {
 11 |   let emitter
 12 |   let capturedBrowsers
 13 |   let config
 14 |   let spy
 15 |   let executor
 16 | 
 17 |   beforeEach(() => {
 18 |     config = { client: {} }
 19 |     emitter = new EventEmitter()
 20 |     capturedBrowsers = new BrowserCollection(emitter)
 21 |     capturedBrowsers.add(new Browser())
 22 |     executor = new Executor(capturedBrowsers, config, emitter)
 23 |     executor.socketIoSockets = new EventEmitter()
 24 | 
 25 |     spy = {
 26 |       onRunStart: sinon.stub(),
 27 |       onSocketsExecute: sinon.stub(),
 28 |       onRunComplete: sinon.stub()
 29 |     }
 30 |     sinon.stub(log, 'warn')
 31 | 
 32 |     emitter.on('run_start', spy.onRunStart)
 33 |     emitter.on('run_complete', spy.onRunComplete)
 34 |     executor.socketIoSockets.on('execute', spy.onSocketsExecute)
 35 |   })
 36 | 
 37 |   describe('schedule', () => {
 38 |     it('should start the run and pass client config', () => {
 39 |       capturedBrowsers.areAllReady = () => true
 40 | 
 41 |       executor.schedule()
 42 |       expect(spy.onRunStart).to.have.been.called
 43 |       expect(spy.onSocketsExecute).to.have.been.calledWith(config.client)
 44 |     })
 45 | 
 46 |     it('should wait for all browsers to finish', () => {
 47 |       capturedBrowsers.areAllReady = () => false
 48 | 
 49 |       // they are not ready yet
 50 |       executor.schedule()
 51 |       expect(spy.onRunStart).not.to.have.been.called
 52 |       expect(spy.onSocketsExecute).not.to.have.been.called
 53 | 
 54 |       capturedBrowsers.areAllReady = () => true
 55 |       emitter.emit('run_complete')
 56 |       expect(spy.onRunStart).to.have.been.called
 57 |       expect(spy.onSocketsExecute).to.have.been.called
 58 |     })
 59 |   })
 60 | 
 61 |   describe('scheduleError', () => {
 62 |     it('should return `true` if scheduled synchronously', () => {
 63 |       const result = executor.scheduleError('expected error')
 64 |       expect(result).to.be.true
 65 |     })
 66 | 
 67 |     it('should emit both "run_start" and "run_complete"', () => {
 68 |       executor.scheduleError('expected error')
 69 |       expect(spy.onRunStart).to.have.been.called
 70 |       expect(spy.onRunComplete).to.have.been.called
 71 |       expect(spy.onRunStart).to.have.been.calledBefore(spy.onRunComplete)
 72 |     })
 73 | 
 74 |     it('should report the error', () => {
 75 |       const expectedError = 'expected error'
 76 |       executor.scheduleError(expectedError)
 77 |       expect(spy.onRunComplete).to.have.been.calledWith([], {
 78 |         success: 0,
 79 |         failed: 0,
 80 |         skipped: 0,
 81 |         error: expectedError,
 82 |         exitCode: 1
 83 |       })
 84 |     })
 85 | 
 86 |     it('should wait for scheduled runs to end before reporting the error', () => {
 87 |       // Arrange
 88 |       let browsersAreReady = true
 89 |       const expectedError = 'expected error'
 90 |       capturedBrowsers.areAllReady = () => browsersAreReady
 91 |       executor.schedule()
 92 |       browsersAreReady = false
 93 | 
 94 |       // Act
 95 |       const result = executor.scheduleError(expectedError)
 96 |       browsersAreReady = true
 97 | 
 98 |       // Assert
 99 |       expect(result).to.be.false
100 |       expect(spy.onRunComplete).to.not.have.been.called
101 |       emitter.emit('run_complete')
102 |       expect(spy.onRunComplete).to.have.been.calledWith([], sinon.match({
103 |         error: expectedError
104 |       }))
105 |     })
106 |   })
107 | })
108 | 


--------------------------------------------------------------------------------
/test/unit/file.spec.js:
--------------------------------------------------------------------------------
 1 | const File = require('../../lib/file')
 2 | 
 3 | describe('File', () => {
 4 |   describe('detectType', () => {
 5 |     it('should detect type from the file extension', () => {
 6 |       const file = new File('/path/to/file.js')
 7 |       expect(file.detectType()).to.equal('js')
 8 |     })
 9 | 
10 |     it('should return empty string if file does not have an extension', () => {
11 |       const file = new File('/path/to/file-without-extension')
12 |       expect(file.detectType()).to.equal('')
13 |     })
14 |   })
15 | })
16 | 


--------------------------------------------------------------------------------
/test/unit/fixtures/format-error-property.js:
--------------------------------------------------------------------------------
1 | exports.formatError = function formatErrorProperty (msg) {
2 |   return msg
3 | }
4 | 


--------------------------------------------------------------------------------
/test/unit/fixtures/format-error-root.js:
--------------------------------------------------------------------------------
1 | // a valid --format-error file
2 | module.exports = function formatErrorRoot (msg) {
3 |   return msg
4 | }
5 | 


--------------------------------------------------------------------------------
/test/unit/index.spec.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const cfg = require('../../lib/config')
 4 | 
 5 | describe('index', () => {
 6 |   const index = require('../../lib/index')
 7 | 
 8 |   it('should expose the `config` object', () => {
 9 |     expect(index.config.parseConfig).to.be.eq(cfg.parseConfig)
10 |   })
11 | })
12 | 


--------------------------------------------------------------------------------
/test/unit/init/formatters.spec.js:
--------------------------------------------------------------------------------
 1 | const formatters = require('../../../lib/init/formatters')
 2 | 
 3 | describe('init/formatters', () => {
 4 |   let formatter
 5 | 
 6 |   describe('JavaScript', () => {
 7 |     beforeEach(() => {
 8 |       formatter = new formatters.JavaScript()
 9 |     })
10 | 
11 |     describe('formatAnswers', () => {
12 |       function createAnswers (ans) {
13 |         ans = ans || {}
14 |         ans.frameworks = ans.frameworks || []
15 |         ans.files = ans.files || []
16 |         ans.onlyServedFiles = ans.onlyServedFiles || []
17 |         ans.exclude = ans.exclude || []
18 |         ans.browsers = ans.browsers || []
19 |         ans.preprocessors = ans.preprocessors || {}
20 |         return ans
21 |       }
22 | 
23 |       it('should format FRAMEWORKS', () => {
24 |         const replacements = formatter.formatAnswers(createAnswers({ frameworks: ['jasmine', 'requirejs'] }))
25 |         expect(replacements.FRAMEWORKS).to.equal("'jasmine', 'requirejs'")
26 |       })
27 | 
28 |       it('should format FILES', () => {
29 |         let replacements = formatter.formatAnswers(createAnswers())
30 |         expect(replacements.FILES).to.equal('')
31 | 
32 |         replacements = formatter.formatAnswers(createAnswers({ files: ['*.js', 'other/file.js'] }))
33 |         expect(replacements.FILES).to.equal(
34 |           "\n      '*.js',\n      'other/file.js'"
35 |         )
36 |       })
37 | 
38 |       it('should format BROWSERS', () => {
39 |         const replacements = formatter.formatAnswers(createAnswers({ browsers: ['Chrome', 'Firefox'] }))
40 |         expect(replacements.BROWSERS).to.equal("'Chrome', 'Firefox'")
41 |       })
42 | 
43 |       it('should format AUTO_WATCH', () => {
44 |         let replacements = formatter.formatAnswers(createAnswers({ autoWatch: true }))
45 |         expect(replacements.AUTO_WATCH).to.equal('true')
46 | 
47 |         replacements = formatter.formatAnswers(createAnswers({ autoWatch: false }))
48 |         expect(replacements.AUTO_WATCH).to.equal('false')
49 |       })
50 | 
51 |       it('should format onlyServedFiles', () => {
52 |         const replacements = formatter.formatAnswers(createAnswers({
53 |           files: ['test-main.js'],
54 |           onlyServedFiles: ['src/*.js']
55 |         }))
56 | 
57 |         expect(replacements.FILES).to.equal(
58 |           "\n      'test-main.js',\n      { pattern: 'src/*.js', included: false }"
59 |         )
60 |       })
61 | 
62 |       it('should format PREPROCESSORS', () => {
63 |         const replacements = formatter.formatAnswers(createAnswers({ preprocessors: { '*.coffee': ['coffee'] } }))
64 | 
65 |         expect(replacements.PREPROCESSORS).to.equal(
66 |           "\n      '*.coffee': ['coffee']"
67 |         )
68 |       })
69 |     })
70 |   })
71 | })
72 | 


--------------------------------------------------------------------------------
/test/unit/launchers/capture_timeout.spec.js:
--------------------------------------------------------------------------------
 1 | const BaseLauncher = require('../../../lib/launchers/base')
 2 | const CaptureTimeoutLauncher = require('../../../lib/launchers/capture_timeout')
 3 | const createMockTimer = require('../mocks/timer')
 4 | 
 5 | describe('launchers/capture_timeout.js', () => {
 6 |   let timer
 7 |   let launcher
 8 | 
 9 |   beforeEach(() => {
10 |     timer = createMockTimer()
11 |     launcher = new BaseLauncher('fake-id')
12 | 
13 |     sinon.spy(launcher, 'kill')
14 |   })
15 | 
16 |   it('should kill if not captured in captureTimeout', () => {
17 |     CaptureTimeoutLauncher.call(launcher, timer, 10)
18 | 
19 |     launcher.start()
20 |     timer.wind(20)
21 |     expect(launcher.kill).to.have.been.called
22 |   })
23 | 
24 |   it('should not kill if browser got captured', () => {
25 |     CaptureTimeoutLauncher.call(launcher, timer, 10)
26 | 
27 |     launcher.start()
28 |     launcher.markCaptured()
29 |     timer.wind(20)
30 |     expect(launcher.kill).not.to.have.been.called
31 |   })
32 | 
33 |   it('should not do anything if captureTimeout = 0', () => {
34 |     CaptureTimeoutLauncher.call(launcher, timer, 0)
35 | 
36 |     launcher.start()
37 |     timer.wind(20)
38 |     expect(launcher.kill).not.to.have.been.called
39 |   })
40 | 
41 |   it('should clear timeout between restarts', async () => {
42 |     CaptureTimeoutLauncher.call(launcher, timer, 10)
43 | 
44 |     // simulate process finished
45 |     launcher.on('kill', (onKillDone) => {
46 |       launcher._done()
47 |       onKillDone()
48 |     })
49 | 
50 |     launcher.start()
51 |     timer.wind(8)
52 |     await launcher.kill()
53 |     launcher.kill.resetHistory()
54 |     launcher.start()
55 |     timer.wind(8)
56 |     expect(launcher.kill).not.to.have.been.called
57 |   })
58 | })
59 | 


--------------------------------------------------------------------------------
/test/unit/launchers/retry.spec.js:
--------------------------------------------------------------------------------
 1 | const _ = require('lodash')
 2 | 
 3 | const BaseLauncher = require('../../../lib/launchers/base')
 4 | const RetryLauncher = require('../../../lib/launchers/retry')
 5 | const EventEmitter = require('../../../lib/events').EventEmitter
 6 | 
 7 | describe('launchers/retry.js', () => {
 8 |   let emitter
 9 |   let launcher
10 | 
11 |   beforeEach(() => {
12 |     emitter = new EventEmitter()
13 |     launcher = new BaseLauncher('fake-id', emitter)
14 |   })
15 | 
16 |   it('should restart if browser crashed', (done) => {
17 |     RetryLauncher.call(launcher, 2)
18 | 
19 |     launcher.start('http://localhost:9876')
20 | 
21 |     sinon.spy(launcher, 'start')
22 |     const spyOnBrowserProcessFailure = sinon.spy()
23 |     emitter.on('browser_process_failure', spyOnBrowserProcessFailure)
24 | 
25 |     // simulate crash
26 |     launcher._done('crash')
27 | 
28 |     _.defer(() => {
29 |       expect(launcher.start).to.have.been.called
30 |       expect(spyOnBrowserProcessFailure).not.to.have.been.called
31 |       done()
32 |     })
33 |   })
34 | 
35 |   it('should eventually fail with "browser_process_failure"', (done) => {
36 |     RetryLauncher.call(launcher, 2)
37 | 
38 |     launcher.start('http://localhost:9876')
39 | 
40 |     sinon.spy(launcher, 'start')
41 |     const spyOnBrowserProcessFailure = sinon.spy()
42 |     emitter.on('browser_process_failure', spyOnBrowserProcessFailure)
43 | 
44 |     // simulate first crash
45 |     launcher._done('crash')
46 | 
47 |     _.defer(() => {
48 |       expect(launcher.start).to.have.been.called
49 |       expect(spyOnBrowserProcessFailure).not.to.have.been.called
50 |       launcher.start.resetHistory()
51 | 
52 |       // simulate second crash
53 |       launcher._done('crash')
54 | 
55 |       _.defer(() => {
56 |         expect(launcher.start).to.have.been.called
57 |         expect(spyOnBrowserProcessFailure).not.to.have.been.called
58 |         launcher.start.resetHistory()
59 | 
60 |         // simulate third crash
61 |         launcher._done('crash')
62 | 
63 |         _.defer(() => {
64 |           expect(launcher.start).not.to.have.been.called
65 |           expect(spyOnBrowserProcessFailure).to.have.been.called
66 |           done()
67 |         })
68 |       })
69 |     })
70 |   })
71 | 
72 |   it('should not restart if killed normally', (done) => {
73 |     RetryLauncher.call(launcher, 2)
74 | 
75 |     launcher.start('http://localhost:9876')
76 | 
77 |     sinon.spy(launcher, 'start')
78 |     const spyOnBrowserProcessFailure = sinon.spy()
79 |     emitter.on('browser_process_failure', spyOnBrowserProcessFailure)
80 | 
81 |     // process just exited normally
82 |     launcher._done()
83 | 
84 |     _.defer(() => {
85 |       expect(launcher.start).not.to.have.been.called
86 |       expect(spyOnBrowserProcessFailure).not.to.have.been.called
87 |       expect(launcher.state).to.equal(launcher.STATE_FINISHED)
88 |       done()
89 |     })
90 |   })
91 | })
92 | 


--------------------------------------------------------------------------------
/test/unit/logger.spec.js:
--------------------------------------------------------------------------------
 1 | const loadFile = require('mocks').loadFile
 2 | const path = require('path')
 3 | 
 4 | describe('logger', () => {
 5 |   let m
 6 |   let configuration
 7 | 
 8 |   beforeEach(() => {
 9 |     const mockLog4Js = {
10 |       configure: function (config) {
11 |         configuration = config
12 |       }
13 |     }
14 |     m = loadFile(path.join(__dirname, '/../../lib/logger.js'), { log4js: mockLog4Js })
15 |   })
16 | 
17 |   describe('setup', () => {
18 |     it('should allow for configuration via setup() using an array for back-compat', () => {
19 |       m.setup('INFO', true, [{
20 |         type: 'file',
21 |         filename: 'test/unit/test.log'
22 |       }])
23 |       expect(configuration).to.have.keys(['appenders', 'categories'])
24 |       expect(configuration.appenders).to.have.keys(['0'])
25 |       expect(configuration.appenders['0'].type).to.equal('file')
26 |       expect(configuration.categories).to.have.keys(['default'])
27 |       expect(configuration.categories.default.appenders).to.have.keys(['0'])
28 |       expect(configuration.categories.default.level).to.equal('INFO')
29 |     })
30 |     it('should allow setup() using log4js v2 object', () => {
31 |       m.setup('WARN', true, {
32 |         fileAppender: {
33 |           type: 'file',
34 |           filename: 'test/unit/test.log'
35 |         }
36 |       })
37 |       expect(configuration).to.have.keys(['appenders', 'categories'])
38 |       expect(configuration.appenders).to.have.keys(['fileAppender'])
39 |       expect(configuration.appenders.fileAppender.type).to.equal('file')
40 |       expect(configuration.categories).to.have.keys(['default'])
41 |       expect(configuration.categories.default.appenders[0]).to.equal('fileAppender')
42 |       expect(configuration.categories.default.level).to.equal('WARN')
43 |     })
44 |   })
45 | })
46 | 


--------------------------------------------------------------------------------
/test/unit/middleware/strip_host.spec.js:
--------------------------------------------------------------------------------
 1 | describe('middleware.strip_host', function () {
 2 |   const stripHost = require('../../../lib/middleware/strip_host').stripHost
 3 | 
 4 |   it('should strip request with IP number', function () {
 5 |     const normalizedUrl = stripHost('http://192.12.31.100/base/a.js?123345')
 6 |     expect(normalizedUrl).to.equal('/base/a.js?123345')
 7 |   })
 8 | 
 9 |   it('should strip request with absoluteURI', function () {
10 |     const normalizedUrl = stripHost('http://localhost/base/a.js?123345')
11 |     expect(normalizedUrl).to.equal('/base/a.js?123345')
12 |   })
13 | 
14 |   it('should strip request with absoluteURI and port', function () {
15 |     const normalizedUrl = stripHost('http://localhost:9876/base/a.js?123345')
16 |     expect(normalizedUrl).to.equal('/base/a.js?123345')
17 |   })
18 | 
19 |   it('should strip request with absoluteURI over HTTPS', function () {
20 |     const normalizedUrl = stripHost('https://karma-runner.github.io/base/a.js?123345')
21 |     expect(normalizedUrl).to.equal('/base/a.js?123345')
22 |   })
23 | 
24 |   it('should return same url as passed one', function () {
25 |     const normalizedUrl = stripHost('/base/b.js?123345')
26 |     expect(normalizedUrl).to.equal('/base/b.js?123345')
27 |   })
28 | })
29 | 


--------------------------------------------------------------------------------
/test/unit/mocha-globals.js:
--------------------------------------------------------------------------------
 1 | const sinon = require('sinon')
 2 | const chai = require('chai')
 3 | const logger = require('../../lib/logger')
 4 | const recording = require('log4js/lib/appenders/recording')
 5 | 
 6 | // publish globals that all specs can use
 7 | global.expect = chai.expect
 8 | global.should = chai.should()
 9 | global.sinon = sinon
10 | 
11 | // chai plugins
12 | chai.use(require('chai-as-promised'))
13 | chai.use(require('sinon-chai'))
14 | chai.use(require('chai-subset'))
15 | 
16 | beforeEach(() => {
17 |   global.sinon = sinon.createSandbox()
18 | 
19 |   // Use https://log4js-node.github.io/log4js-node/recording.html to verify logs
20 |   const vcr = { vcr: { type: 'recording' } }
21 |   logger.setup('INFO', false, vcr)
22 | })
23 | 
24 | afterEach(() => {
25 |   global.sinon.restore()
26 |   recording.erase()
27 | })
28 | 
29 | // TODO(vojta): move to helpers or something
30 | chai.use((chai, utils) => {
31 |   chai.Assertion.addMethod('beServedAs', function (expectedStatus, expectedBody) {
32 |     const response = utils.flag(this, 'object')
33 | 
34 |     this.assert(response._status === expectedStatus,
35 |       `expected response status '${response._status}' to be '${expectedStatus}'`)
36 |     this.assert(response._body === expectedBody,
37 |       `expected response body '${response._body}' to be '${expectedBody}'`)
38 |   })
39 | 
40 |   chai.Assertion.addMethod('beNotServed', function () {
41 |     const response = utils.flag(this, 'object')
42 | 
43 |     this.assert(response._status === null,
44 |       `expected response status to not be set, it was '${response._status}'`)
45 |     this.assert(response._body === null,
46 |       `expected response body to not be set, it was '${response._body}'`)
47 |   })
48 | })
49 | 


--------------------------------------------------------------------------------
/test/unit/mocks/timer.js:
--------------------------------------------------------------------------------
 1 | const Timer = require('timer-shim').Timer
 2 | 
 3 | module.exports = function () {
 4 |   const timer = new Timer()
 5 |   timer.pause()
 6 | 
 7 |   return {
 8 |     setTimeout: timer.setTimeout,
 9 |     clearTimeout: timer.clearTimeout,
10 |     setInterval: timer.setInterval,
11 |     clearInterval: timer.clearInterval,
12 |     wind: timer.wind
13 |   }
14 | }
15 | 


--------------------------------------------------------------------------------
/test/unit/reporters/progress.spec.js:
--------------------------------------------------------------------------------
 1 | describe('reporter', function () {
 2 |   const ProgressReporter = require('../../../lib/reporters/progress')
 3 | 
 4 |   describe('Progress', function () {
 5 |     let reporter
 6 |     let formatError
 7 | 
 8 |     beforeEach(function () {
 9 |       formatError = sinon.spy()
10 |       reporter = new ProgressReporter(formatError, null, false, { terminal: true })
11 |     })
12 | 
13 |     it('should turn off colors', function () {
14 |       expect(reporter.EXCLUSIVELY_USE_COLORS).to.equal(false)
15 |     })
16 | 
17 |     it('should prepare state on run tests', function () {
18 |       sinon.stub(reporter, 'write')
19 |       sinon.stub(reporter, 'renderBrowser')
20 | 
21 |       reporter.onRunStart()
22 |       reporter.onBrowserStart(createBrowserMock())
23 | 
24 |       reporter.onRunStart()
25 | 
26 |       expect(reporter._browsers.length).to.equal(0)
27 |       expect(reporter._isRendered).to.equal(false)
28 |     })
29 | 
30 |     it('should not throw exception if browser exit with error without run tests', function () {
31 |       sinon.stub(reporter, 'write')
32 |       sinon.stub(reporter, 'renderBrowser')
33 | 
34 |       expect(function () {
35 |         reporter.onBrowserError(createBrowserMock())
36 |       }).to.not.throw()
37 |     })
38 | 
39 |     function createBrowserMock () {
40 |       return {}
41 |     }
42 |   })
43 | })
44 | 


--------------------------------------------------------------------------------
/test/unit/runner.spec.js:
--------------------------------------------------------------------------------
 1 | const loadFile = require('mocks').loadFile
 2 | const path = require('path')
 3 | 
 4 | const constant = require('../../lib/constants')
 5 | 
 6 | describe('runner', () => {
 7 |   let m
 8 | 
 9 |   beforeEach(() => {
10 |     m = loadFile(path.join(__dirname, '/../../lib/runner.js'))
11 |   })
12 | 
13 |   describe('parseExitCode', () => {
14 |     const EXIT = constant.EXIT_CODE
15 | 
16 |     it('should return 0 exit code if present in the buffer', () => {
17 |       const result = m.parseExitCode(Buffer.from(`something\nfake${EXIT}10`))
18 |       expect(result.exitCode).to.equal(0)
19 |     })
20 | 
21 |     it('should remove the exit code part of the returned buffer', () => {
22 |       const buffer = Buffer.from(`some${EXIT}01`)
23 |       const result = m.parseExitCode(buffer)
24 | 
25 |       expect(buffer.toString()).to.equal(`some${EXIT}01`)
26 |       expect(result.buffer.toString()).to.equal('some')
27 |     })
28 | 
29 |     it('should not touch buffer without exit code and return default', () => {
30 |       const msg = 'some nice \n messgae {}'
31 |       const buffer = Buffer.from(msg)
32 |       const result = m.parseExitCode(buffer, 10)
33 | 
34 |       expect(result.buffer.toString()).to.equal(msg)
35 |       expect(result.buffer).to.equal(buffer)
36 |       expect(result.exitCode).to.equal(10)
37 |     })
38 | 
39 |     it('should not slice buffer if smaller than exit code msg', () => {
40 |       // regression
41 |       const fakeBuffer = { length: 1, slice: () => null }
42 |       sinon.stub(fakeBuffer, 'slice')
43 | 
44 |       m.parseExitCode(fakeBuffer, 10)
45 |       expect(fakeBuffer.slice).not.to.have.been.called
46 |     })
47 | 
48 |     it('should return same buffer if smaller than exit code msg', () => {
49 |       // regression
50 |       const fakeBuffer = { length: 1, slice: () => null }
51 |       const result = m.parseExitCode(fakeBuffer, 10)
52 |       expect(fakeBuffer).to.equal(result.buffer)
53 |     })
54 | 
55 |     it('should parse any single digit exit code', () => {
56 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}01`)).exitCode).to.equal(1)
57 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}17`)).exitCode).to.equal(7)
58 |     })
59 | 
60 |     it('should return exit code 0 if failOnEmptyTestSuite is false and and non-empty int is 0', () => {
61 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}01`), undefined, false).exitCode).to.equal(0)
62 |     })
63 | 
64 |     it('should return exit code if failOnEmptyTestSuite is true', () => {
65 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}00`), undefined, true).exitCode).to.equal(0)
66 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}01`), undefined, true).exitCode).to.equal(1)
67 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}07`), undefined, true).exitCode).to.equal(7)
68 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}10`), undefined, true).exitCode).to.equal(0)
69 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}11`), undefined, true).exitCode).to.equal(1)
70 |       expect(m.parseExitCode(Buffer.from(`something\nfake${EXIT}17`), undefined, true).exitCode).to.equal(7)
71 |     })
72 |   })
73 | })
74 | 


--------------------------------------------------------------------------------
/test/unit/url.spec.js:
--------------------------------------------------------------------------------
 1 | const Url = require('../../lib/url')
 2 | 
 3 | describe('Url', () => {
 4 |   describe('detectType', () => {
 5 |     it('should detect type from the file extension in the path of the URL', () => {
 6 |       const file = new Url('https://example.com/path/to/file.js')
 7 |       expect(file.detectType()).to.equal('js')
 8 |     })
 9 | 
10 |     it('should detect type for URL with query params', () => {
11 |       const file = new Url('https://example.com/path/to/file.js?query=simple')
12 |       expect(file.detectType()).to.equal('js')
13 |     })
14 | 
15 |     it('should detect type for URL with a fragment', () => {
16 |       const file = new Url('https://example.com/path/to/file.js#fragment')
17 |       expect(file.detectType()).to.equal('js')
18 |     })
19 | 
20 |     it('should return empty string if URL does not have path', () => {
21 |       const file = new Url('https://example.com')
22 |       expect(file.detectType()).to.equal('')
23 |     })
24 | 
25 |     it('should return empty string if path in the URL does not have an extension', () => {
26 |       const file = new Url('https://example.com/path/to/file-without-extension')
27 |       expect(file.detectType()).to.equal('')
28 |     })
29 |   })
30 | })
31 | 


--------------------------------------------------------------------------------
/test/unit/utils/crypto-utils.spec.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const CryptoUtils = require('../../../lib/utils/crypto-utils')
 4 | 
 5 | describe('CryptoUtils.sha1', () => {
 6 |   it('create sha1 digest from string', () => {
 7 |     expect(CryptoUtils.sha1('Example text')).to.equal('cf3df620b86f0c9f586359136950217cb3b8c035')
 8 |   })
 9 | })
10 | 


--------------------------------------------------------------------------------
/test/unit/utils/net-utils.spec.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | 
 3 | const NetUtils = require('../../../lib/utils/net-utils')
 4 | const connect = require('connect')
 5 | const net = require('net')
 6 | 
 7 | describe('NetUtils.bindAvailablePort', () => {
 8 |   it('resolves server with bound port when it is available', (done) => {
 9 |     NetUtils.bindAvailablePort(9876, '127.0.0.1').then((boundServer) => {
10 |       const port = boundServer.address().port
11 |       expect(port).to.be.equal(9876)
12 |       expect(boundServer).not.to.be.null
13 |       const server = net.createServer(connect()).listen(boundServer, () => {
14 |         server.close(done)
15 |       })
16 |     })
17 |   })
18 | 
19 |   it('resolves with next available port', (done) => {
20 |     const server = net.createServer(connect()).listen(9876, '127.0.0.1', () => {
21 |       NetUtils.bindAvailablePort(9876, '127.0.0.1').then((boundServer) => {
22 |         const port = boundServer.address().port
23 |         expect(port).to.be.equal(9877)
24 |         expect(boundServer).not.to.be.null
25 |         boundServer.close()
26 |         server.close(done)
27 |       })
28 |     })
29 |   })
30 | 
31 |   it('rejects if a critical error occurs', (done) => {
32 |     const incorrectAddress = '123.321'
33 |     NetUtils.bindAvailablePort(9876, incorrectAddress).catch((err) => {
34 |       expect(err).not.to.be.null
35 |       done()
36 |     })
37 |   })
38 | })
39 | 


--------------------------------------------------------------------------------
/test/unit/utils/path-utils.spec.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | const PathUtils = require('../../../lib/utils/path-utils')
 3 | const fs = require('fs')
 4 | 
 5 | describe('PathUtils.calculateAbsolutePath', () => {
 6 |   it('returns absolute path from karma project relative path', () => {
 7 |     expect(fs.existsSync(PathUtils.calculateAbsolutePath('logo/banner.png'))).to.be.true
 8 |   })
 9 | })
10 | 


--------------------------------------------------------------------------------
/test/unit/utils/pattern-utils.spec.js:
--------------------------------------------------------------------------------
 1 | 'use strict'
 2 | const PatternUtils = require('../../../lib/utils/pattern-utils')
 3 | 
 4 | describe('PatternUtils.getBaseDir', () => {
 5 |   it('return parent directory without start', () => {
 6 |     expect(PatternUtils.getBaseDir('/some/path/**/more.js')).to.equal('/some/path')
 7 |     expect(PatternUtils.getBaseDir('/some/p*/file.js')).to.equal('/some')
 8 |   })
 9 | 
10 |   it('remove part with !(x)', () => {
11 |     expect(PatternUtils.getBaseDir('/some/p/!(a|b).js')).to.equal('/some/p')
12 |     expect(PatternUtils.getBaseDir('/some/p!(c|b)*.js')).to.equal('/some')
13 |   })
14 | 
15 |   it('remove part with +(x)', () => {
16 |     expect(PatternUtils.getBaseDir('/some/p/+(a|b).js')).to.equal('/some/p')
17 |     expect(PatternUtils.getBaseDir('/some/p+(c|bb).js')).to.equal('/some')
18 |   })
19 | 
20 |   it('remove part with (x)?', () => {
21 |     expect(PatternUtils.getBaseDir('/some/p/(a|b)?.js')).to.equal('/some/p')
22 |     expect(PatternUtils.getBaseDir('/some/p(c|b)?.js')).to.equal('/some')
23 |   })
24 | 
25 |   it('allow paths with parentheses', () => {
26 |     expect(PatternUtils.getBaseDir('/some/x (a|b)/a.js')).to.equal('/some/x (a|b)/a.js')
27 |     expect(PatternUtils.getBaseDir('/some/p(c|b)/*.js')).to.equal('/some/p(c|b)')
28 |   })
29 | 
30 |   it('ignore exact files', () => {
31 |     expect(PatternUtils.getBaseDir('/usr/local/bin.js')).to.equal('/usr/local/bin.js')
32 |   })
33 | })
34 | 


--------------------------------------------------------------------------------
/thesis.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karma-runner/karma/84f85e7016efc2266fa6b3465f494a3fa151c85c/thesis.pdf


--------------------------------------------------------------------------------
/tools/update-contributors.js:
--------------------------------------------------------------------------------
 1 | const { execSync } = require('child_process')
 2 | const { readFileSync, writeFileSync } = require('fs')
 3 | const { resolve } = require('path')
 4 | 
 5 | const prepare = async (pluginConfig, { logger }) => {
 6 |   // Example output:
 7 |   //   1042  Vojta Jina <vojta.jina@gmail.com>
 8 |   //    412  Friedel Ziegelmayer <friedel.ziegelmayer@gmail.com>
 9 |   //    206  dignifiedquire <friedel.ziegelmayer@gmail.com>
10 |   //    139  johnjbarton <johnjbarton@johnjbarton.com>
11 |   const stdout = execSync('git log --pretty=short | git shortlog -nse', { encoding: 'utf8' })
12 | 
13 |   const pkgPath = resolve(__dirname, '..', 'package.json')
14 |   const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
15 | 
16 |   // First line is already included as author field. Last line is dropped as it is an empty line.
17 |   pkg.contributors = stdout.split('\n').slice(1, -1).map((line) => line.replace(/^[\W\d]+/, ''))
18 |   writeFileSync(pkgPath, JSON.stringify(pkg, undefined, '  ') + '\n', 'utf8')
19 | 
20 |   logger.info('Updated contributors list.')
21 | }
22 | 
23 | module.exports = { prepare }
24 | 


--------------------------------------------------------------------------------
/tools/update-docs.js:
--------------------------------------------------------------------------------
 1 | const { execSync } = require('child_process')
 2 | const { dirSync } = require('tmp')
 3 | 
 4 | const success = async (pluginConfig, { nextRelease, logger }) => {
 5 |   const [major, minor] = nextRelease.version.split('.')
 6 |   const docsVersion = `${major}.${minor}`
 7 | 
 8 |   const { name: docsPath } = dirSync()
 9 | 
10 |   // This is a regular repository remote one would get if they click a Clone
11 |   // button on GitHub. The only added part is GITHUB_TOKEN value in the
12 |   // `userinfo` part of the URL (https://en.wikipedia.org/wiki/URL), which
13 |   // allows GitHub to authenticate a user and authorise the push to the
14 |   // repository.
15 |   const repoOrigin = `https://${process.env.GITHUB_TOKEN}@github.com/karma-runner/karma-runner.github.com.git`
16 | 
17 |   const options = { encoding: 'utf8', cwd: docsPath }
18 | 
19 |   logger.log(execSync(`git clone ${repoOrigin} .`, options))
20 |   logger.log(execSync('npm ci', options))
21 |   logger.log(execSync(`./sync-docs.sh "${nextRelease.gitTag}" "${docsVersion}"`, options))
22 |   logger.log(execSync('git push origin master', options))
23 | }
24 | 
25 | module.exports = { success }
26 | 


--------------------------------------------------------------------------------
/wallaby.js:
--------------------------------------------------------------------------------
 1 | 
 2 | module.exports = function (wallaby) {
 3 |   return {
 4 |     files: [
 5 |       {
 6 |         pattern: 'package.json',
 7 |         instrument: false
 8 |       },
 9 |       {
10 |         pattern: 'config.tpl.js',
11 |         instrument: false
12 |       },
13 |       {
14 |         pattern: 'test/unit/certificates/server.key',
15 |         instrument: false
16 |       },
17 |       {
18 |         pattern: 'test/unit/certificates/server.crt',
19 |         instrument: false
20 |       },
21 |       {
22 |         pattern: 'test/unit/**/*.spec.js',
23 |         ignore: true
24 |       },
25 |       'lib/**/*.js',
26 |       'test/unit/**/*.js',
27 |       'test/unit/mocha-globals.js'
28 |     ],
29 | 
30 |     tests: [
31 |       'test/unit/**/*.spec.js'
32 |     ],
33 | 
34 |     bootstrap: function (w) {
35 |       var path = require('path')
36 |       var mocha = w.testFramework
37 | 
38 |       mocha.suite.on('pre-require', function () {
39 |         // always passing wallaby.js globals to mocks.loadFile
40 |         var mocks = require('mocks')
41 |         var loadFile = mocks.loadFile
42 |         mocks.loadFile = function (filePath, mocks, globals, mockNested) {
43 |           mocks = mocks || {}
44 |           globals = globals || {}
45 |           globals.$_$wp = global.$_$wp || {}
46 |           globals.$_$wpe = global.$_$wpe || {}
47 |           globals.$_$w = global.$_$w || {}
48 |           globals.$_$wf = global.$_$wf || {}
49 |           globals.$_$tracer = global.$_$tracer || {}
50 |           return loadFile(filePath, mocks, globals, mockNested)
51 |         }
52 | 
53 |         // loading mocha-globals for each run
54 |         require(path.join(process.cwd(), 'test/unit/mocha-globals'))
55 |       })
56 |     },
57 | 
58 |     env: {
59 |       type: 'node',
60 |       params: {
61 |         runner: '--harmony --harmony_arrow_functions'
62 |       }
63 |     }
64 |   }
65 | }
66 | 


--------------------------------------------------------------------------------