├── test
├── e2e
│ ├── timeout
│ │ ├── fake-browser.sh
│ │ ├── specs.js
│ │ └── karma.conf.ignore.js
│ ├── requirejs
│ │ ├── shim.js
│ │ ├── dependency.js
│ │ ├── test.js
│ │ ├── main.js
│ │ └── karma.conf.js
│ ├── html2js
│ │ ├── template.html
│ │ ├── test.js
│ │ └── karma.conf.js
│ ├── coffee
│ │ ├── plus.coffee
│ │ ├── test.coffee
│ │ └── karma.conf.coffee
│ ├── basic
│ │ ├── plus.js
│ │ ├── test.js
│ │ └── karma.conf.js
│ ├── junit
│ │ ├── plus.js
│ │ ├── test.js
│ │ └── karma.conf.js
│ ├── qunit
│ │ ├── plus.js
│ │ ├── test.js
│ │ └── karma.conf.js
│ ├── sauce
│ │ ├── plus.js
│ │ ├── test.js
│ │ └── karma.conf.js
│ ├── browserstack
│ │ ├── plus.js
│ │ ├── test.js
│ │ └── karma.conf.js
│ ├── coverage
│ │ ├── lib
│ │ │ ├── minus.js
│ │ │ └── plus.js
│ │ ├── test
│ │ │ ├── minus.spec.js
│ │ │ └── plus.spec.js
│ │ └── karma.conf.js
│ ├── coverageQunit
│ │ ├── lib
│ │ │ ├── minus.js
│ │ │ └── plus.js
│ │ ├── test
│ │ │ ├── minus.spec.js
│ │ │ └── plus.spec.js
│ │ └── karma.conf.js
│ ├── coverageRequirejs
│ │ ├── dependency.js
│ │ ├── main.js
│ │ ├── test.js
│ │ └── karma.conf.js
│ ├── syntax-error
│ │ ├── under-test.js
│ │ ├── test.js
│ │ └── karma.conf.ignore.js
│ ├── dojo
│ │ ├── dependency.js
│ │ ├── test.js
│ │ ├── main.js
│ │ └── karma.conf.ignore.js
│ ├── pass-opts
│ │ ├── test.js
│ │ └── karma.conf.js
│ ├── angular-scenario
│ │ ├── index.html
│ │ ├── e2eSpec.js
│ │ └── karma.conf.js
│ └── mocha
│ │ ├── karma.conf.js
│ │ └── specs.js
├── unit
│ ├── server.spec.coffee
│ ├── logger.spec.coffee
│ ├── reporters
│ │ └── Base.spec.coffee
│ ├── mocha-globals.coffee
│ ├── executor.spec.coffee
│ ├── runner.spec.coffee
│ ├── completion.spec.coffee
│ ├── reporter.spec.coffee
│ ├── preprocessor.spec.coffee
│ ├── web-server.spec.coffee
│ ├── events.spec.coffee
│ ├── watcher.spec.coffee
│ ├── middleware
│ │ ├── source-files.spec.coffee
│ │ └── proxy.spec.coffee
│ ├── launcher.spec.coffee
│ └── cli.spec.coffee
└── client
│ ├── mocks.js
│ ├── karma.conf.js
│ └── karma.spec.js
├── thesis.pdf
├── scripts
├── init-dev-env.bat
└── init-dev-env.js
├── docs
├── index.md
├── plus
│ ├── 07-yeoman.md
│ ├── 06-angularjs.md
│ ├── 05-cloud9.md
│ ├── 04-semaphore.md
│ ├── 03-jenkins.md
│ └── 02-travis.md
├── about
│ ├── 01-versioning.md
│ └── 03-migration.md
├── dev
│ ├── 01-public-api.md
│ ├── 03-contributing.md
│ ├── 02-plugins.md
│ └── 04-git-commit-msg.md
├── config
│ ├── 05-plugins.md
│ ├── 02-files.md
│ ├── 04-preprocessors.md
│ └── 03-browsers.md
└── intro
│ ├── 03-how-it-works.md
│ ├── 01-installation.md
│ ├── 04-faq.md
│ └── 02-configuration.md
├── .mailmap
├── lib
├── index.js
├── reporters
│ ├── DotsColor.js
│ ├── ProgressColor.js
│ ├── Multi.js
│ ├── BaseColor.js
│ ├── Dots.js
│ ├── Progress.js
│ └── Base.js
├── constants.js
├── events.js
├── executor.js
├── plugin.js
├── middleware
│ ├── source-files.js
│ ├── common.js
│ ├── runner.js
│ ├── proxy.js
│ └── karma.js
├── runner.js
├── watcher.js
├── helper.js
├── preprocessor.js
├── web-server.js
├── reporter.js
├── launcher.js
├── logger.js
├── completion.js
└── launchers
│ └── Base.js
├── .npmignore
├── static
├── karma.wrapper
├── context.html
├── debug.html
└── client.html
├── .gitignore
├── .gitattributes
├── .travis.yml
├── tasks
├── init-dev-env.js
├── build.js
└── test.js
├── bin
└── karma
├── LICENSE
├── config.tpl.coffee
├── config.tpl.js
├── karma-completion.sh
├── CONTRIBUTING.md
└── Gruntfile.coffee
/test/e2e/timeout/fake-browser.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | read
4 |
--------------------------------------------------------------------------------
/thesis.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pl/karma/master/thesis.pdf
--------------------------------------------------------------------------------
/test/e2e/requirejs/shim.js:
--------------------------------------------------------------------------------
1 | window.global = 'some exported value';
2 |
--------------------------------------------------------------------------------
/test/e2e/html2js/template.html:
--------------------------------------------------------------------------------
1 |
content of the template
2 |
--------------------------------------------------------------------------------
/scripts/init-dev-env.bat:
--------------------------------------------------------------------------------
1 | set __dirname=%~dp0
2 | node %__dirname%init-dev-env.js
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | layout: homepage
2 | pageTitle: Spectacular Test Runner for Javascript
3 |
--------------------------------------------------------------------------------
/test/e2e/coffee/plus.coffee:
--------------------------------------------------------------------------------
1 | # Some code under test
2 | plus = (a, b) ->
3 | a + b
4 |
--------------------------------------------------------------------------------
/test/e2e/basic/plus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function plus(a, b) {
3 | return a + b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/junit/plus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function plus(a, b) {
3 | return a + b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/qunit/plus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function plus(a, b) {
3 | return a + b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/sauce/plus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function plus(a, b) {
3 | return a + b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/browserstack/plus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function plus(a, b) {
3 | return a + b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/coverage/lib/minus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function minus(a, b) {
3 | return a - b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/coverage/lib/plus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function plus(a, b) {
3 | return a + b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/coverageQunit/lib/minus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function minus(a, b) {
3 | return a - b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/coverageQunit/lib/plus.js:
--------------------------------------------------------------------------------
1 | // Some code under test
2 | function plus(a, b) {
3 | return a + b;
4 | }
5 |
--------------------------------------------------------------------------------
/test/e2e/coverageQunit/test/minus.spec.js:
--------------------------------------------------------------------------------
1 | test('should work', function() {
2 | ok(minus(1, 2) === -1, "Passed");
3 | });
4 |
--------------------------------------------------------------------------------
/test/e2e/coverageRequirejs/dependency.js:
--------------------------------------------------------------------------------
1 | define(function() {
2 | return function (a, b) {
3 | return a + b;
4 | };
5 | }
6 | );
7 |
--------------------------------------------------------------------------------
/test/e2e/syntax-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 |
--------------------------------------------------------------------------------
/.mailmap:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/test/e2e/coverage/test/minus.spec.js:
--------------------------------------------------------------------------------
1 | describe('minus', function() {
2 | it('should work', function() {
3 | expect(minus(1, 2)).toBe(-1);
4 | });
5 | });
6 |
--------------------------------------------------------------------------------
/test/e2e/timeout/specs.js:
--------------------------------------------------------------------------------
1 | describe('something', function() {
2 | it('should never happen anyway', function() {
3 | expect(true).toBe(true);
4 | });
5 | });
6 |
--------------------------------------------------------------------------------
/test/e2e/dojo/dependency.js:
--------------------------------------------------------------------------------
1 | define(function() {
2 | // return the module value
3 | return function (a, b) {
4 | return a + b;
5 | };
6 | }
7 | );
8 |
--------------------------------------------------------------------------------
/test/e2e/requirejs/dependency.js:
--------------------------------------------------------------------------------
1 | define(function() {
2 | // return the module value
3 | return function (a, b) {
4 | return a + b;
5 | };
6 | }
7 | );
8 |
--------------------------------------------------------------------------------
/test/e2e/coffee/test.coffee:
--------------------------------------------------------------------------------
1 | describe 'plus', ->
2 |
3 | it 'should pass', ->
4 | expect(true).toBe true
5 |
6 | it 'should work', ->
7 | expect(plus 1, 2).toBe 3
8 |
--------------------------------------------------------------------------------
/test/e2e/qunit/test.js:
--------------------------------------------------------------------------------
1 | test('should pass', function() {
2 | ok(true === true, "Passed");
3 | });
4 |
5 | test('should work', function() {
6 | ok(plus(1, 2) === 3, "Passed");
7 | });
--------------------------------------------------------------------------------
/docs/plus/07-yeoman.md:
--------------------------------------------------------------------------------
1 | [Yeoman](http://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 |
--------------------------------------------------------------------------------
/test/e2e/coverageQunit/test/plus.spec.js:
--------------------------------------------------------------------------------
1 | test('should pass', function() {
2 | ok(true === true, "Passed");
3 | });
4 |
5 | test('should work', function() {
6 | ok(plus(1, 2) === 3, "Passed");
7 | });
8 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | // index module
2 | exports.VERSION = require('./constants').VERSION;
3 | exports.server = require('./server');
4 | exports.runner = require('./runner');
5 | exports.launcher = require('./launcher');
6 |
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*
2 |
3 | tmp
4 | test
5 | tasks
6 | scripts
7 | docs
8 |
9 | TODO.md
10 | CONTRIBUTING.md
11 | Gruntfile.coffee
12 |
13 | Karma.sublime-*
14 |
15 | static/karma.src.js
16 | static/karma.wrapper
17 |
--------------------------------------------------------------------------------
/static/karma.wrapper:
--------------------------------------------------------------------------------
1 | (function(window, document, io) {
2 |
3 | %CONTENT%
4 |
5 |
6 | window.karma = new Karma(socket, document.getElementById('context'), window.navigator, window.location);
7 |
8 | })(window, document, window.io);
9 |
--------------------------------------------------------------------------------
/test/e2e/basic/test.js:
--------------------------------------------------------------------------------
1 | describe('plus', function() {
2 | it('should pass', function() {
3 | expect(true).toBe(true);
4 | });
5 |
6 | it('should work', function() {
7 | expect(plus(1, 2)).toBe(3);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/test/e2e/sauce/test.js:
--------------------------------------------------------------------------------
1 | describe('plus', function() {
2 | it('should pass', function() {
3 | expect(true).toBe(true);
4 | });
5 |
6 | it('should work', function() {
7 | expect(plus(1, 2)).toBe(3);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/test/e2e/browserstack/test.js:
--------------------------------------------------------------------------------
1 | describe('plus', function() {
2 | it('should pass', function() {
3 | expect(true).toBe(true);
4 | });
5 |
6 | it('should work', function() {
7 | expect(plus(1, 2)).toBe(3);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/test/e2e/syntax-error/test.js:
--------------------------------------------------------------------------------
1 | describe('plus', function() {
2 | it('should pass', function() {
3 | expect(true).toBe(true);
4 | });
5 |
6 | it('should work', function() {
7 | expect(plus(1, 2)).toBe(3);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/test/e2e/coverage/test/plus.spec.js:
--------------------------------------------------------------------------------
1 | describe('plus', function() {
2 | it('should pass', function() {
3 | expect(true).toBe(true);
4 | });
5 |
6 | it('should work', function() {
7 | expect(plus(1, 2)).toBe(3);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/test/e2e/html2js/test.js:
--------------------------------------------------------------------------------
1 | describe('template', function() {
2 | it('should expose the templates to __html__', function() {
3 | document.body.innerHTML = __html__['template.html'];
4 | expect(document.getElementById('tpl')).toBeDefined();
5 | })
6 | })
7 |
--------------------------------------------------------------------------------
/test/e2e/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','arg3']);
5 | });
6 | });
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | static/karma.js
3 | .idea/*
4 | *.iml
5 | tmp/*
6 | docs/_build
7 | *.swp
8 | *.swo
9 | test/e2e/coverage/coverage
10 | test/e2e/coverageQunit/coverage
11 | test/e2e/coverageRequirejs/coverage
12 | test-results.xml
13 | test/unit/test.log
14 |
--------------------------------------------------------------------------------
/test/unit/server.spec.coffee:
--------------------------------------------------------------------------------
1 | # TODO(vojta):
2 | 'should try next port if already in use'
3 | 'should launch browsers after web server started'
4 |
5 | # single run
6 | 'should run tests when all browsers captured'
7 | 'should run tests when first browser captured if no browser configured'
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 |
--------------------------------------------------------------------------------
/test/e2e/junit/test.js:
--------------------------------------------------------------------------------
1 | describe('plus', function() {
2 | it('should pass', function() {
3 | expect(true).toBe(true);
4 | });
5 |
6 | it('should work', function() {
7 | console.log("First parameter: 1");
8 | console.log("Second parameter: 2");
9 | expect(plus(1, 2)).toBe(3);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/test/e2e/coverageRequirejs/main.js:
--------------------------------------------------------------------------------
1 | require.config({
2 | // Karma serves files under /base, which is the basePath from your config file
3 | baseUrl: '/base',
4 |
5 | // load test.js
6 | deps: ['test'],
7 |
8 | // we have to kick of jasmine, as it is asynchronous
9 | callback: window.__karma__.start
10 | });
11 |
--------------------------------------------------------------------------------
/test/e2e/requirejs/test.js:
--------------------------------------------------------------------------------
1 | define(['dependency'], function(dep) {
2 |
3 | // jasmine
4 | describe('something', function() {
5 | it('should pass', function() {
6 | expect(true).toBe(true);
7 | });
8 |
9 | it('should sum', function() {
10 | expect(dep(1, 2)).toBe(3);
11 | });
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/lib/reporters/DotsColor.js:
--------------------------------------------------------------------------------
1 | var DotsReporter = require('./Dots');
2 | var BaseColorReporter = require('./BaseColor');
3 |
4 |
5 | var DotsColorReporter = function(formatError, reportSlow) {
6 | DotsReporter.call(this, formatError, reportSlow);
7 | BaseColorReporter.call(this);
8 | };
9 |
10 |
11 | // PUBLISH
12 | module.exports = DotsColorReporter;
13 |
--------------------------------------------------------------------------------
/lib/reporters/ProgressColor.js:
--------------------------------------------------------------------------------
1 | var ProgressReporter = require('./Progress');
2 | var BaseColorReporter = require('./BaseColor');
3 |
4 |
5 | var ProgressColorReporter = function(formatError, reportSlow) {
6 | ProgressReporter.call(this, formatError, reportSlow);
7 | BaseColorReporter.call(this);
8 | };
9 |
10 |
11 | // PUBLISH
12 | module.exports = ProgressColorReporter;
13 |
--------------------------------------------------------------------------------
/test/e2e/pass-opts/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['jasmine'],
4 |
5 | files: [
6 | '*.js'
7 | ],
8 |
9 | browsers: [ process.env.TRAVIS ? 'Firefox' : 'Chrome' ],
10 |
11 | reporters: ['dots'],
12 |
13 | plugins: [
14 | 'karma-jasmine',
15 | 'karma-chrome-launcher',
16 | 'karma-firefox-launcher'
17 | ],
18 | });
19 | };
20 |
--------------------------------------------------------------------------------
/test/e2e/angular-scenario/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sample Angular App
6 |
7 |
8 |
9 |
10 | Name:
11 |
12 |
13 | Hello {{yourName}}!
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/e2e/coverageRequirejs/test.js:
--------------------------------------------------------------------------------
1 | define(['chai'], function(chai) {
2 | var expect = chai.expect;
3 |
4 | describe('something', function() {
5 | it('should pass', function() {
6 | expect(true).to.equal(true);
7 | });
8 |
9 | it('should sum', function(done) {
10 | require(['dependency'], function(dep) {
11 | expect(dep(1, 2)).to.equal(3);
12 | done();
13 | });
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/test/e2e/basic/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['jasmine'],
4 |
5 | files: [
6 | '*.js'
7 | ],
8 |
9 | autoWatch: true,
10 |
11 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
12 |
13 | reporters: ['dots'],
14 |
15 | plugins: [
16 | 'karma-jasmine',
17 | 'karma-chrome-launcher',
18 | 'karma-firefox-launcher'
19 | ],
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/test/e2e/qunit/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['qunit'],
4 |
5 | files: [
6 | '*.js'
7 | ],
8 |
9 | autoWatch: true,
10 |
11 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
12 |
13 | reporters: ['dots'],
14 |
15 | plugins: [
16 | 'karma-qunit',
17 | 'karma-chrome-launcher',
18 | 'karma-firefox-launcher'
19 | ],
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/test/e2e/angular-scenario/e2eSpec.js:
--------------------------------------------------------------------------------
1 | /** A Sample Angular E2E test */
2 |
3 | describe('My Sample App', function() {
4 |
5 | it('should let Angular do its work', function() {
6 | browser().navigateTo('/index.html');
7 | input('yourName').enter('A Pirate!');
8 | expect(element('.ng-binding').text()).toEqual('Hello A Pirate!!');
9 | });
10 |
11 | xit('should skip this e2e test', function() {
12 | sleep(15);
13 | browser().navigateTo('/index.html');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/test/e2e/dojo/test.js:
--------------------------------------------------------------------------------
1 | define(['local/dependency','dojo/_base/lang'], function(dep,lang) {
2 |
3 | // jasmine
4 | describe('something', function() {
5 | it('should pass', function() {
6 | expect(true).toBe(true);
7 | });
8 |
9 | it('should sum', function() {
10 | expect(dep(1, 2)).toBe(3);
11 | });
12 |
13 | it('should trim using a dojo AMD module',function(){
14 | expect(lang.trim(" len4 ").length).toEqual(4);
15 | });
16 |
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/test/e2e/mocha/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['mocha'],
4 |
5 | files: [
6 | '*.js'
7 | ],
8 |
9 | autoWatch: true,
10 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
11 | singleRun: false,
12 |
13 | browsers: ['Chrome'],
14 |
15 | reporters: ['dots'],
16 |
17 | plugins: [
18 | 'karma-mocha',
19 | 'karma-chrome-launcher',
20 | 'karma-firefox-launcher'
21 | ],
22 | });
23 | };
24 |
--------------------------------------------------------------------------------
/test/e2e/html2js/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['jasmine'],
4 |
5 | files: [
6 | '*.js',
7 | '*.html'
8 | ],
9 |
10 | autoWatch: true,
11 |
12 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
13 |
14 | reporters: ['dots'],
15 |
16 | plugins: [
17 | 'karma-jasmine',
18 | 'karma-html2js-preprocessor',
19 | 'karma-chrome-launcher',
20 | 'karma-firefox-launcher'
21 | ],
22 | });
23 | };
24 |
--------------------------------------------------------------------------------
/test/e2e/syntax-error/karma.conf.ignore.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['jasmine'],
4 |
5 | // files to load
6 | files: [
7 | '*.js'
8 | ],
9 |
10 | autoWatch: true,
11 | logLevel: config.LOG_INFO,
12 | logColors: true,
13 |
14 | browsers: ['Chrome'],
15 |
16 | reporters: ['dots'],
17 |
18 | plugins: [
19 | 'karma-jasmine',
20 | 'karma-chrome-launcher',
21 | 'karma-firefox-launcher'
22 | ],
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/lib/reporters/Multi.js:
--------------------------------------------------------------------------------
1 | var helper = require('../helper');
2 |
3 |
4 | var MultiReporter = function(reporters) {
5 |
6 | this.addAdapter = function(adapter) {
7 | reporters.forEach(function(reporter) {
8 | reporter.adapters.push(adapter);
9 | });
10 | };
11 |
12 | this.removeAdapter = function(adapter) {
13 | reporters.forEach(function(reporter) {
14 | helper.arrayRemove(reporter.adapters, adapter);
15 | });
16 | };
17 | };
18 |
19 |
20 | // PUBLISH
21 | module.exports = MultiReporter;
22 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.8
4 | - 0.10
5 |
6 | env:
7 | global:
8 | - SAUCE_USERNAME: vojtajina
9 | - BROWSER_STACK_USERNAME: vojta.jina@gmail.com
10 | - secure: "k2x1puIrj42LqnWF8SoDod19xpeJhJN3RDMgRLK2LTYffcdJV8TXilPRUk4pXyN6u8B79fxAW8WucsrlxB6CaaB5cXsFTDG+jpO21AlFc3mFjPh6sTkp/1L/bxc3cJNc9w18Ol2+8MO5s37xuy/lJaQeBRFxQleZZWCLLjtXytg="
11 |
12 | before_script:
13 | - export DISPLAY=:99.0
14 | - sh -e /etc/init.d/xvfb start
15 | - npm install -g grunt-cli
16 | - rm -rf node_modules/karma
17 |
18 | script:
19 | - grunt
20 |
--------------------------------------------------------------------------------
/docs/plus/06-angularjs.md:
--------------------------------------------------------------------------------
1 | pageTitle: AngularJS
2 | menuTitle: AngularJS
3 |
4 | If you're using [AngularJS](http://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.
5 |
6 | Here is also a blog article explaining how to setup Grunt and Karma to test out an AngularJS application: [Full Spectrum Testing with AngularJS and Karma](http://www.yearofmoo.com/2013/01/full-spectrum-testing-with-angularjs-and-karma.html)
7 |
--------------------------------------------------------------------------------
/tasks/init-dev-env.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | /**
4 | * Initialize development environment for Karma
5 | *
6 | * - register git hooks (commit-msg)
7 | */
8 | grunt.registerTask('init-dev-env', 'Initialize dev environment.', function() {
9 | var fs = require('fs');
10 | var done = this.async();
11 |
12 | fs.symlink('../../tasks/lib/validate-commit-msg.js', '.git/hooks/commit-msg', function(e) {
13 | if (!e) {
14 | grunt.log.ok('Hook "validate-commit-msg" installed.');
15 | }
16 | done();
17 | });
18 | });
19 | };
20 |
--------------------------------------------------------------------------------
/test/e2e/angular-scenario/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['ng-scenario'],
4 |
5 | files: [
6 | 'e2eSpec.js'
7 | ],
8 |
9 | urlRoot: '/__karma/',
10 |
11 | autoWatch: true,
12 |
13 | proxies: {
14 | '/': 'http://localhost:8000/test/e2e/angular-scenario/'
15 | },
16 |
17 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
18 |
19 | reporters: ['dots'],
20 |
21 | plugins: [
22 | 'karma-ng-scenario',
23 | 'karma-chrome-launcher',
24 | 'karma-firefox-launcher'
25 | ]
26 | });
27 | };
28 |
--------------------------------------------------------------------------------
/test/e2e/coffee/karma.conf.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (config) ->
2 | config.set
3 | frameworks: ['jasmine']
4 |
5 | files: [
6 | '*.coffee'
7 | ]
8 |
9 | autoWatch: true
10 |
11 | browsers: [if process.env.TRAVIS then 'Firefox' else 'Chrome']
12 |
13 | coffeePreprocessor:
14 | options:
15 | sourceMap: true
16 |
17 | preprocessors:
18 | '**/*.coffee': 'coffee'
19 |
20 | reporters: ['dots']
21 |
22 | plugins: [
23 | 'karma-jasmine'
24 | 'karma-coffee-preprocessor'
25 | 'karma-chrome-launcher'
26 | 'karma-firefox-launcher'
27 | ]
28 |
--------------------------------------------------------------------------------
/test/e2e/junit/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['jasmine'],
4 |
5 | files: [
6 | '*.js'
7 | ],
8 |
9 | autoWatch: true,
10 |
11 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
12 |
13 | reporters: ['dots', 'junit'],
14 |
15 | logLevel: config.LOG_DEBUG,
16 |
17 | junitReporter: {
18 | outputFile: 'test-results.xml'
19 | },
20 |
21 | plugins: [
22 | 'karma-jasmine',
23 | 'karma-chrome-launcher',
24 | 'karma-firefox-launcher',
25 | 'karma-junit-reporter'
26 | ],
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/test/e2e/requirejs/main.js:
--------------------------------------------------------------------------------
1 | var allTestFiles = [];
2 | var TEST_REGEXP = /test\.js$/;
3 |
4 | Object.keys(window.__karma__.files).forEach(function(file) {
5 | if (TEST_REGEXP.test(file)) {
6 | allTestFiles.push(file);
7 | }
8 | });
9 |
10 | require.config({
11 | // Karma serves files under /base, which is the basePath from your config file
12 | baseUrl: '/base',
13 |
14 | // example of using shim, to load non AMD libraries (such as Backbone, jquery)
15 | shim: {
16 | '/base/shim.js': {
17 | exports: 'global'
18 | }
19 | },
20 |
21 | // dynamically load all test files
22 | deps: allTestFiles,
23 |
24 | // we have to kick of jasmine, as it is asynchronous
25 | callback: window.__karma__.start
26 | });
27 |
--------------------------------------------------------------------------------
/test/e2e/mocha/specs.js:
--------------------------------------------------------------------------------
1 | var expect = chai.expect;
2 |
3 | describe('Array', function() {
4 |
5 | describe('.push()', function() {
6 |
7 | it('should append a value', function() {
8 | var arr = [];
9 |
10 | arr.push('foo');
11 | arr.push('bar');
12 |
13 | expect(arr[0]).to.equal('foo');
14 | expect(arr[1]).to.equal('bar');
15 | });
16 |
17 |
18 | it('should return the length', function() {
19 | var i = 100;
20 | var some = 'x';
21 |
22 | while (i--) {
23 | some = some + 'xxx';
24 | }
25 |
26 | var arr = [];
27 | var n = arr.push('foo');
28 | expect(n).to.equal(1);
29 |
30 | var n = arr.push('bar');
31 | expect(n).to.equal(2);
32 | });
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/test/e2e/sauce/karma.conf.js:
--------------------------------------------------------------------------------
1 | var TRAVIS_WITHOUT_SAUCE = process.env.TRAVIS_SECURE_ENV_VARS === 'false';
2 |
3 | module.exports = function(config) {
4 | config.set({
5 | frameworks: ['jasmine'],
6 |
7 | files: [
8 | '*.js'
9 | ],
10 |
11 | autoWatch: true,
12 |
13 | browsers: [TRAVIS_WITHOUT_SAUCE ? 'Firefox' : 'sl_chrome_linux'],
14 |
15 | reporters: ['dots'],
16 |
17 | logLevel: config.LOG_DEBUG,
18 |
19 | plugins: [
20 | 'karma-jasmine',
21 | 'karma-sauce-launcher',
22 | 'karma-firefox-launcher'
23 | ],
24 |
25 | customLaunchers: {
26 | sl_chrome_linux: {
27 | base: 'SauceLabs',
28 | browserName: 'chrome',
29 | platform: 'linux'
30 | }
31 | }
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/docs/about/01-versioning.md:
--------------------------------------------------------------------------------
1 | Karma uses [Semantic Versioning] with a little exception:
2 | - even versions (eg. `0.6.x`, `0.8.x`) are considered stable - no breaking changes or new features, only bug fixes will pushed into this branch,
3 | - odd versions (eg. `0.7.x`, `0.9.x`) are unstable - anything can happen ;-)
4 |
5 | Therefore it is recommended to rely on the latest stable version, which gives you automatic bug fixes, but does not break you:
6 | ```javascript
7 | {
8 | "devDependencies": {
9 | "karma": "~0.10"
10 | }
11 | }
12 | ```
13 |
14 | ## Stable channel (branch `stable`)
15 | ```bash
16 | $ npm install karma
17 | ```
18 |
19 | ## Canary channel (branch `master`)
20 | ```bash
21 | $ npm install karma@canary
22 | ```
23 |
24 | [Semantic Versioning]: http://semver.org/
25 |
--------------------------------------------------------------------------------
/test/e2e/coverageRequirejs/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['mocha', 'requirejs'],
4 |
5 | files: [
6 | 'main.js',
7 | {pattern: '*.js', included: false},
8 | ],
9 |
10 | autoWatch: true,
11 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
12 | singleRun: false,
13 |
14 | reporters: ['progress', 'coverage'],
15 |
16 | preprocessors: {
17 | 'dependency.js': 'coverage'
18 | },
19 |
20 | coverageReporter: {
21 | type : 'html',
22 | dir : 'coverage/'
23 | },
24 |
25 | plugins: [
26 | 'karma-mocha',
27 | 'karma-requirejs',
28 | 'karma-coverage',
29 | 'karma-chrome-launcher',
30 | 'karma-firefox-launcher'
31 | ],
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/lib/reporters/BaseColor.js:
--------------------------------------------------------------------------------
1 | require('colors');
2 |
3 | var BaseColorReporter = function() {
4 | this.USE_COLORS = true;
5 |
6 | this.LOG_SINGLE_BROWSER = '%s: ' + '%s'.cyan + '\n';
7 | this.LOG_MULTI_BROWSER = '%s %s: ' + '%s'.cyan + '\n';
8 |
9 | this.SPEC_FAILURE = '%s %s FAILED'.red + '\n';
10 | this.SPEC_SLOW = '%s SLOW %s: %s'.yellow + '\n';
11 | this.ERROR = '%s ERROR'.red + '\n';
12 |
13 | this.FINISHED_ERROR = ' ERROR'.red;
14 | this.FINISHED_SUCCESS = ' SUCCESS'.green;
15 | this.FINISHED_DISCONNECTED = ' DISCONNECTED'.red;
16 |
17 | this.X_FAILED = ' (%d FAILED)'.red;
18 |
19 | this.TOTAL_SUCCESS = 'TOTAL: %d SUCCESS'.green + '\n';
20 | this.TOTAL_FAILED = 'TOTAL: %d FAILED, %d SUCCESS'.red + '\n';
21 | };
22 |
23 |
24 | // PUBLISH
25 | module.exports = BaseColorReporter;
26 |
--------------------------------------------------------------------------------
/test/client/mocks.js:
--------------------------------------------------------------------------------
1 | /**
2 | Mocks for testing static/karma.js
3 | Needs to be loaded before karma.js
4 | */
5 |
6 | var Emitter = function() {
7 | var listeners = {};
8 |
9 | this.on = function(event, fn) {
10 | if (!listeners[event]) {
11 | listeners[event] = [];
12 | }
13 |
14 | listeners[event].push(fn);
15 | };
16 |
17 | this.emit = function(event) {
18 | var eventListeners = listeners[event];
19 |
20 | if (!eventListeners) return;
21 |
22 | var i = 0;
23 | while (i < eventListeners.length) {
24 | eventListeners[i].apply(null, Array.prototype.slice.call(arguments, 1));
25 | i++;
26 | }
27 | };
28 | };
29 |
30 | var MockSocket = Emitter;
31 |
32 | var io = {
33 | connect: function() {
34 | return new MockSocket();
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/lib/constants.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | var pkg = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString());
4 |
5 | exports.VERSION = pkg.version;
6 |
7 | exports.DEFAULT_PORT = 9876;
8 | exports.DEFAULT_HOSTNAME = 'localhost';
9 |
10 | // log levels
11 | exports.LOG_DISABLE = 'OFF';
12 | exports.LOG_ERROR = 'ERROR';
13 | exports.LOG_WARN = 'WARN';
14 | exports.LOG_INFO = 'INFO';
15 | exports.LOG_DEBUG = 'DEBUG';
16 |
17 | // Default patterns for the pattern layout.
18 | exports.COLOR_PATTERN = '%[%p [%c]: %]%m';
19 | exports.NO_COLOR_PATTERN = '%p [%c]: %m';
20 |
21 | // Default console appender
22 | exports.CONSOLE_APPENDER = {
23 | type: 'console',
24 | layout: {
25 | type: 'pattern',
26 | pattern: exports.COLOR_PATTERN
27 | }
28 | };
29 |
30 | exports.EXIT_CODE = '\x1FEXIT';
31 |
--------------------------------------------------------------------------------
/tasks/build.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | /**
4 | * Build given file - wrap it with a function call
5 | *
6 | * grunt build
7 | * grunt build:client
8 | * grunt build:jasmine
9 | * grunt build:mocha
10 | * grunt build:ngScenario
11 | * grunt build:require
12 | */
13 | grunt.registerMultiTask('build', 'Wrap given file into a function call.', function() {
14 |
15 | var src = grunt.file.expand(this.data).pop();
16 | var dest = src.replace('src.js', 'js');
17 | var wrapper = src.replace('src.js', 'wrapper');
18 |
19 | grunt.file.copy(wrapper, dest, {process: function(content) {
20 | var wrappers = content.split(/%CONTENT%\r?\n/);
21 | return wrappers[0] + grunt.file.read(src) + wrappers[1];
22 | }});
23 |
24 | grunt.log.ok('Created ' + dest);
25 | });
26 | };
27 |
--------------------------------------------------------------------------------
/bin/karma:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var path = require('path');
4 | var fs = require('fs');
5 |
6 | // Try to find a local install
7 | var dir = path.resolve(process.cwd(), 'node_modules', 'karma', 'lib');
8 |
9 | // Check if the local install exists else we use the install we are in
10 | if (!fs.existsSync(dir)) {
11 | dir = path.join('..', 'lib');
12 | }
13 |
14 | var cli = require(path.join(dir, 'cli'));
15 | var config = cli.process();
16 |
17 | switch (config.cmd) {
18 | case 'start':
19 | require(path.join(dir, 'server')).start(config);
20 | break;
21 | case 'run':
22 | require(path.join(dir, 'runner')).run(config);
23 | break;
24 | case 'init':
25 | require(path.join(dir, 'init')).init(config);
26 | break;
27 | case 'completion':
28 | require(path.join(dir, 'completion')).completion(config);
29 | break;
30 | }
31 |
--------------------------------------------------------------------------------
/docs/dev/01-public-api.md:
--------------------------------------------------------------------------------
1 | Most of the time, you will be using Karma directly from the command line.
2 | You can, however, call Karma programmatically from your node module. Here is the public API.
3 |
4 |
5 | ## karma.server
6 |
7 | ### **server.start(options, [callback=process.exit])**
8 |
9 | Equivalent of `karma start`.
10 |
11 | ```javascript
12 | var server = require('karma').server;
13 | server.start({port: 9876}, function(exitCode) {
14 | console.log('Karma has exited with ' + exitCode);
15 | process.exit(exitCode);
16 | });
17 | ```
18 |
19 | ## karma.runner
20 |
21 | ### **runner.run(options, [callback=process.exit])**
22 |
23 | Equivalent of `karma run`.
24 |
25 | ```javascript
26 | var runner = require('karma').runner;
27 | runner.run({port: 9876}, function(exitCode) {
28 | console.log('Karma has exited with ' + exitCode);
29 | process.exit(exitCode);
30 | });
31 | ```
32 |
--------------------------------------------------------------------------------
/test/e2e/dojo/main.js:
--------------------------------------------------------------------------------
1 | var allTestFiles = [];
2 | var TEST_REGEXP = /test.*\.js$/;
3 |
4 | Object.keys(window.__karma__.files).forEach(function(file) {
5 | if (TEST_REGEXP.test(file)) {
6 | allTestFiles.push(file);
7 | }
8 | });
9 |
10 | var dojoConfig = {
11 | packages: [
12 | { name:"local" ,location:"/base"},
13 | { name: "dojo", location: "http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo" },
14 | { name: "dojox", location: "http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojox" }
15 | ],
16 | asynch: true
17 | };
18 |
19 |
20 | /**
21 | * This function must be defined and is called back by the dojo adapter
22 | * @returns {string} a list of dojo spec/test modules to register with your testing framework
23 | */
24 | window.__karma__.dojoStart = function(){
25 | return allTestFiles;
26 | }
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/test/unit/logger.spec.coffee:
--------------------------------------------------------------------------------
1 | #==============================================================================
2 | # lib/logger.js module
3 | #==============================================================================
4 |
5 | describe 'logger', ->
6 | loadFile = require('mocks').loadFile
7 | logSpy = m = null
8 | beforeEach ->
9 | logSpy = sinon.spy()
10 | m = loadFile __dirname + '/../../lib/logger.js'
11 |
12 | #============================================================================
13 | # setup()
14 | #============================================================================
15 | describe 'setup', ->
16 | it 'should allow for configuration via setup() using an array', ->
17 | m.setup 'INFO', true, [
18 | type: 'file'
19 | filename: 'test/unit/test.log'
20 | ]
21 |
22 | expect(m.log4js.appenders).to.have.keys ['console', 'file']
23 |
--------------------------------------------------------------------------------
/docs/plus/05-cloud9.md:
--------------------------------------------------------------------------------
1 | [Cloud9 IDE] is an open source web-based cloud integrated development environment that supports several programming languages, with a focus on the web stack (specifically JavaScript and [NodeJS]). It is written almost entirely in JavaScript, and uses [NodeJS] on the back-end.
2 |
3 |
4 | There are two possibilities in order to run unit tests with Karma in [Cloud9 IDE]:
5 |
6 | ## Capture the browser manually on the local machine
7 |
8 | Open `http://..c9.io/` in your browser.
9 |
10 | ## Run Karma unit tests with PhantomJS in cloud9 IDE
11 |
12 | ### Install PhantomJS
13 | PhantomJS must be installed with `npm install phantomjs`.
14 |
15 | ### Configure Karma
16 | The `karma.conf.js` file must include the following entries:
17 |
18 | ```javascript
19 | browsers: ['PhantomJS'],
20 | hostname: process.env.IP,
21 | port: process.env.PORT
22 | ```
23 |
24 | [Cloud9 IDE]: https://c9.io/
25 | [NodeJS]: http://nodejs.org/
26 |
--------------------------------------------------------------------------------
/test/e2e/coverage/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['jasmine'],
4 |
5 | files: [
6 | 'lib/*.js',
7 | 'test/*.js'
8 | ],
9 |
10 | autoWatch: true,
11 |
12 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
13 |
14 | reporters: ['progress', 'coverage'],
15 |
16 | preprocessors: {
17 | 'lib/*.js': 'coverage'
18 | },
19 |
20 | //Code Coverage options. report type available:
21 | //- html (default)
22 | //- lcov (lcov and html)
23 | //- lcovonly
24 | //- text (standard output)
25 | //- text-summary (standard output)
26 | //- cobertura (xml format supported by Jenkins)
27 | coverageReporter: {
28 | // cf. http://gotwarlost.github.io/istanbul/public/apidocs/
29 | type : 'html',
30 | dir : 'coverage/'
31 | },
32 |
33 | plugins: [
34 | 'karma-jasmine',
35 | 'karma-coverage',
36 | 'karma-chrome-launcher',
37 | 'karma-firefox-launcher'
38 | ],
39 | });
40 | };
41 |
--------------------------------------------------------------------------------
/test/e2e/coverageQunit/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: ['qunit'],
4 |
5 | files: [
6 | 'lib/*.js',
7 | 'test/*.js'
8 | ],
9 |
10 | autoWatch: true,
11 |
12 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
13 |
14 | reporters: ['progress', 'coverage'],
15 |
16 | preprocessors: {
17 | 'lib/*.js': 'coverage'
18 | },
19 |
20 | //Code Coverage options. report type available:
21 | //- html (default)
22 | //- lcov (lcov and html)
23 | //- lcovonly
24 | //- text (standard output)
25 | //- text-summary (standard output)
26 | //- cobertura (xml format supported by Jenkins)
27 | coverageReporter: {
28 | // cf. http://gotwarlost.github.io/istanbul/public/apidocs/
29 | type : 'html',
30 | dir : 'coverage/'
31 | },
32 |
33 | plugins: [
34 | 'karma-qunit',
35 | 'karma-coverage',
36 | 'karma-chrome-launcher',
37 | 'karma-firefox-launcher'
38 | ],
39 | });
40 | };
41 |
--------------------------------------------------------------------------------
/test/e2e/browserstack/karma.conf.js:
--------------------------------------------------------------------------------
1 | var TRAVIS_WITHOUT_BS = process.env.TRAVIS_SECURE_ENV_VARS === 'false';
2 |
3 | module.exports = function(config) {
4 | config.set({
5 | frameworks: ['jasmine'],
6 |
7 | files: [
8 | '*.js'
9 | ],
10 |
11 | autoWatch: true,
12 |
13 | browsers: TRAVIS_WITHOUT_BS ? ['Firefox'] : ['bs_ff_mac', 'bs_ch_mac'],
14 |
15 | reporters: ['dots'],
16 |
17 | browserStack: {
18 | username: 'vojta.jina@gmail.com',
19 | accessKey: process.env.BROWSER_STACK_ACCESS_KEY
20 | },
21 |
22 | customLaunchers: {
23 | bs_ff_mac: {
24 | base: 'BrowserStack',
25 | browser: 'firefox',
26 | os: 'mac',
27 | version: '21.0'
28 | },
29 | bs_ch_mac: {
30 | base: 'BrowserStack',
31 | browser: 'chrome',
32 | os: 'mac',
33 | version: 'latest'
34 | }
35 | },
36 |
37 | plugins: [
38 | 'karma-jasmine',
39 | 'karma-firefox-launcher',
40 | 'karma-browserstack-launcher'
41 | ]
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/static/context.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
24 |
25 | %SCRIPTS%
26 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/unit/reporters/Base.spec.coffee:
--------------------------------------------------------------------------------
1 | #==============================================================================
2 | # lib/reporters/Base.js module
3 | #==============================================================================
4 | describe 'reporter', ->
5 | loadFile = require('mocks').loadFile
6 | m = null
7 |
8 | beforeEach ->
9 | m = loadFile __dirname + '/../../../lib/reporters/Base.js'
10 |
11 | describe 'Progress', ->
12 | adapter = reporter = null
13 |
14 | beforeEach ->
15 |
16 | adapter = sinon.spy()
17 | reporter = new m.BaseReporter null, null, adapter
18 |
19 |
20 | it 'should write to all registered adapters', ->
21 | anotherAdapter = sinon.spy()
22 | reporter.adapters.push anotherAdapter
23 |
24 | reporter.write 'some'
25 | expect(adapter).to.have.been.calledWith 'some'
26 | expect(anotherAdapter).to.have.been.calledWith 'some'
27 |
28 |
29 | it 'should format', ->
30 | reporter.write 'Success: %d Failure: %d', 10, 20
31 |
32 | expect(adapter).to.have.been.calledWith 'Success: 10 Failure: 20'
33 |
34 |
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (C) 2011-2013 Vojta Jína and contributors.
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 |
--------------------------------------------------------------------------------
/lib/events.js:
--------------------------------------------------------------------------------
1 | var events = require('events');
2 | var util = require('util');
3 | var Q = require('q');
4 |
5 | var helper = require('./helper');
6 |
7 |
8 | var bindAllEvents = function(object, context) {
9 | context = context || this;
10 |
11 | for (var method in object) {
12 | if (helper.isFunction(object[method]) && method.substr(0, 2) === 'on') {
13 | context.on(helper.camelToSnake(method.substr(2)), object[method].bind(object));
14 | }
15 | }
16 | };
17 |
18 | // TODO(vojta): log.debug all events
19 | var EventEmitter = function() {
20 | this.bind = bindAllEvents;
21 |
22 | this.emitAsync = function(name) {
23 | // TODO(vojta): allow passing args
24 | // TODO(vojta): ignore/throw if listener call done() multiple times
25 | var pending = this.listeners(name).length;
26 | var deferred = Q.defer();
27 | var done = function() {
28 | if (!--pending) {
29 | deferred.resolve();
30 | }
31 | };
32 |
33 | this.emit(name, done);
34 |
35 | return deferred.promise;
36 | };
37 | };
38 |
39 | util.inherits(EventEmitter, events.EventEmitter);
40 |
41 | // PUBLISH
42 | exports.EventEmitter = EventEmitter;
43 | exports.bindAll = bindAllEvents;
44 |
--------------------------------------------------------------------------------
/lib/reporters/Dots.js:
--------------------------------------------------------------------------------
1 | var BaseReporter = require('./Base');
2 |
3 |
4 | var DotsReporter = function(formatError, reportSlow) {
5 | BaseReporter.call(this, formatError, reportSlow);
6 |
7 | var DOTS_WRAP = 80;
8 |
9 | this.onRunStart = function(browsers) {
10 | this._browsers = browsers;
11 | this._dotsCount = 0;
12 | };
13 |
14 | this.writeCommonMsg = function(msg) {
15 | if (this._dotsCount) {
16 | this._dotsCount = 0;
17 | msg = '\n' + msg;
18 | }
19 |
20 | this.write(msg);
21 |
22 | };
23 |
24 |
25 | this.specSuccess = function() {
26 | this._dotsCount = (this._dotsCount + 1) % DOTS_WRAP;
27 | this.write(this._dotsCount ? '.' : '.\n');
28 | };
29 |
30 | this.onRunComplete = function(browsers, results) {
31 | this.writeCommonMsg(browsers.map(this.renderBrowser).join('\n') + '\n');
32 |
33 | if (browsers.length > 1 && !results.disconnected && !results.error) {
34 | if (!results.failed) {
35 | this.write(this.TOTAL_SUCCESS, results.success);
36 | } else {
37 | this.write(this.TOTAL_FAILED, results.failed, results.success);
38 | }
39 | }
40 | };
41 | };
42 |
43 |
44 | // PUBLISH
45 | module.exports = DotsReporter;
46 |
--------------------------------------------------------------------------------
/lib/reporters/Progress.js:
--------------------------------------------------------------------------------
1 | var BaseReporter = require('./Base');
2 |
3 |
4 | var ProgressReporter = function(formatError, reportSlow) {
5 | BaseReporter.call(this, formatError, reportSlow);
6 |
7 |
8 | this.writeCommonMsg = function(msg) {
9 | this.write(this._remove() + msg + this._render());
10 | };
11 |
12 |
13 | this.specSuccess = function() {
14 | this.write(this._refresh());
15 | };
16 |
17 |
18 | this.onBrowserComplete = function() {
19 | this.write(this._refresh());
20 | };
21 |
22 |
23 | this.onRunStart = function(browsers) {
24 | this._browsers = browsers;
25 | this._isRendered = false;
26 | };
27 |
28 |
29 | this._remove = function() {
30 | if (!this._isRendered) {
31 | return '';
32 | }
33 |
34 | var cmd = '';
35 | this._browsers.forEach(function() {
36 | cmd += '\x1B[1A' + '\x1B[2K';
37 | });
38 |
39 | this._isRendered = false;
40 |
41 | return cmd;
42 | };
43 |
44 | this._render = function() {
45 | this._isRendered = true;
46 |
47 | return this._browsers.map(this.renderBrowser).join('\n') + '\n';
48 | };
49 |
50 | this._refresh = function() {
51 | return this._remove() + this._render();
52 | };
53 | };
54 |
55 |
56 | // PUBLISH
57 | module.exports = ProgressReporter;
58 |
--------------------------------------------------------------------------------
/docs/config/05-plugins.md:
--------------------------------------------------------------------------------
1 | Karma can be easily extended through plugins.
2 | In fact, all the existing preprocessors, reporters, browser launchers and frameworks are also plugins.
3 |
4 | ## Installation
5 |
6 | Karma plugins are NPM modules, so the recommended way is to keep all the plugins your project requires in `package.json`:
7 |
8 | ```javascript
9 | {
10 | "devDependencies": {
11 | "karma": "~0.10",
12 | "karma-mocha": "~0.0.1",
13 | "karma-growl-reporter": "~0.0.1",
14 | "karma-firefox-launcher": "~0.0.1"
15 | }
16 | }
17 | ```
18 |
19 | Therefore, a simple way how to install a plugin is
20 | ```bash
21 | npm install karma- --save-dev
22 | ```
23 |
24 |
25 | ## Loading Plugins
26 | By default, Karma loads all NPM modules that are siblinks to it and their name matches `karma-*`.
27 |
28 | You can also explicitly list plugins you want to load via the `plugins` configuration setting. The configuration value can either be
29 | a string (module name), which will be required by Karma, or an object (inlined plugin).
30 |
31 | ```javascript
32 | plugins: [
33 | // these plugins will be require() by Karma
34 | 'karma-jasmine',
35 | 'karma-chrome-launcher'
36 |
37 | // inelined plugins
38 | {'framework:xyz', ['factory', factoryFn]},
39 | require('./plugin-required-from-config')
40 | ]
41 | ```
42 |
43 | There are already many [existing plugins]. Of course, you write [your own plugins] too!
44 |
45 | [existing plugins]: https://npmjs.org/browse/keyword/karma-plugin
46 | [your own plugins]: ../dev/plugins.html
47 |
--------------------------------------------------------------------------------
/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 for each test against each browser are examined and displayed via the command line to the developer
3 | such that they can to 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 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 | However, if you are interested in learning more, Karma itself originates from a university thesis, which goes into detail about the design
18 | and implementation, and it is available to read [right here].
19 |
20 | [right here]: https://github.com/karma-runner/karma/raw/master/thesis.pdf
21 | [browsers]: ../config/browsers.html
22 |
--------------------------------------------------------------------------------
/test/e2e/timeout/karma.conf.ignore.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Sun Sep 30 2012 22:44:01 GMT-0700 (PDT)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path, that will be used to resolve files and exclude
8 | basePath: '',
9 |
10 | frameworks: ['jasmine'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | '*.js'
15 | ],
16 |
17 |
18 | // test results reporter to use
19 | // possible values: 'dots', 'progress', 'junit'
20 | reporters: ['progress'],
21 |
22 |
23 | // web server port
24 | port: 8080,
25 |
26 |
27 | // enable / disable colors in the output (reporters and logs)
28 | colors: true,
29 |
30 |
31 | // level of logging
32 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
33 | logLevel: config.LOG_INFO,
34 |
35 |
36 | // enable / disable watching file and executing tests whenever any file changes
37 | autoWatch: true,
38 |
39 |
40 | // Start these browsers, currently available:
41 | // - Chrome
42 | // - ChromeCanary
43 | // - Firefox
44 | // - Opera
45 | // - Safari (only Mac)
46 | // - PhantomJS
47 | // - IE (only Windows)
48 | browsers: [__dirname + '/fake-browser.sh'],
49 |
50 |
51 | // Continuous Integration mode
52 | // if true, it capture browsers, run tests and exit
53 | singleRun: false,
54 |
55 | plugins: [
56 | 'karma-jasmine',
57 | 'karma-chrome-launcher',
58 | 'karma-firefox-launcher'
59 | ],
60 | });
61 | };
62 |
--------------------------------------------------------------------------------
/test/unit/mocha-globals.coffee:
--------------------------------------------------------------------------------
1 | sinon = require 'sinon'
2 | chai = require 'chai'
3 | logger = require '../../lib/logger'
4 |
5 | # publish globals that all specs can use
6 | global.timer = require 'timer-shim'
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 |
15 | # TODO(vojta): remove this global stub
16 | sinon.stub(timer, 'setTimeout').callsArg 0
17 |
18 | beforeEach ->
19 | global.sinon = sinon.sandbox.create()
20 |
21 | # set logger to log INFO, but do not append to console
22 | # so that we can assert logs by logger.on('info', ...)
23 | logger.setup 'INFO', false, []
24 |
25 | afterEach ->
26 | global.sinon.restore()
27 |
28 |
29 |
30 | # TODO(vojta): move to helpers or something
31 | chai.use (chai, utils) ->
32 | chai.Assertion.addMethod 'beServedAs', (expectedStatus, expectedBody) ->
33 | response = utils.flag @, 'object'
34 |
35 | @assert response._status is expectedStatus,
36 | "expected response status '#{response._status}' to be '#{expectedStatus}'"
37 | @assert response._body is expectedBody,
38 | "expected response body '#{response._body}' to be '#{expectedBody}'"
39 |
40 | chai.Assertion.addMethod 'beNotServed', ->
41 | response = utils.flag @, 'object'
42 |
43 | @assert response._status is null,
44 | "expected response status to not be set, it was '#{response._status}'"
45 | @assert response._body is null,
46 | "expected response body to not be set, it was '#{response._body}'"
47 |
--------------------------------------------------------------------------------
/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 | # frameworks to use
11 | frameworks: [%FRAMEWORKS%]
12 |
13 | # list of files / patterns to load in the browser
14 | files: [
15 | %FILES%
16 | ]
17 |
18 | # list of files to exclude
19 | exclude: [
20 | %EXCLUDE%
21 | ]
22 |
23 | # test results reporter to use
24 | # possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
25 | reporters: ['progress']
26 |
27 | # web server port
28 | port: 9876
29 |
30 | # enable / disable colors in the output (reporters and logs)
31 | colors: true
32 |
33 | # level of logging
34 | # possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
35 | logLevel: config.LOG_INFO
36 |
37 | # enable / disable watching file and executing tests whenever any file changes
38 | autoWatch: %AUTO_WATCH%
39 |
40 | # Start these browsers, currently available:
41 | # - Chrome
42 | # - ChromeCanary
43 | # - Firefox
44 | # - Opera
45 | # - Safari (only Mac)
46 | # - PhantomJS
47 | # - IE (only Windows)
48 | browsers: [%BROWSERS%]
49 |
50 | # If browser does not capture in given timeout [ms], kill it
51 | captureTimeout: 60000
52 |
53 | # Continuous Integration mode
54 | # if true, it capture browsers, run tests and exit
55 | singleRun: false
56 |
--------------------------------------------------------------------------------
/test/e2e/dojo/karma.conf.ignore.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | // base path, that will be used to resolve files and exclude
4 | basePath: '',
5 |
6 | frameworks: ['jasmine', 'dojo'],
7 |
8 | // list of files / patterns to load in the browser
9 | files: [
10 | 'main.js',
11 |
12 | // all the sources, tests
13 | {pattern: '*.js', included: false}
14 | ],
15 |
16 |
17 | // test results reporter to use
18 | // possible values: dots || progress
19 | reporters: ['dots'],
20 |
21 |
22 | // web server port
23 | port: 9876,
24 |
25 |
26 | // cli runner port
27 | runnerPort: 9100,
28 |
29 |
30 | // enable / disable colors in the output (reporters and logs)
31 | colors: true,
32 |
33 |
34 | // level of logging
35 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
36 | logLevel: config.LOG_INFO,
37 |
38 |
39 | // enable / disable watching file and executing tests whenever any file changes
40 | autoWatch: true,
41 |
42 |
43 | // Start these browsers, currently available:
44 | // - Chrome
45 | // - ChromeCanary
46 | // - Firefox
47 | // - Opera
48 | // - Safari
49 | // - PhantomJS
50 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
51 |
52 |
53 | // Continuous Integration mode
54 | // if true, it capture browsers, run tests and exit
55 | singleRun: false,
56 |
57 | plugins: [
58 | 'karma-dojo',
59 | 'karma-jasmine',
60 | 'karma-chrome-launcher',
61 | 'karma-firefox-launcher'
62 | ]
63 | });
64 | };
65 |
--------------------------------------------------------------------------------
/lib/executor.js:
--------------------------------------------------------------------------------
1 | var log = require('./logger').create();
2 |
3 | var Executor = function(capturedBrowsers, config, emitter) {
4 | var self = this;
5 | var executionScheduled = false;
6 | var pendingCount = 0;
7 | var runningBrowsers;
8 |
9 | var schedule = function() {
10 | var nonReady = [];
11 |
12 | if (!capturedBrowsers.length) {
13 | log.warn('No captured browser, open http://%s:%s%s', config.hostname, config.port,
14 | config.urlRoot);
15 | return false;
16 | }
17 |
18 | if (capturedBrowsers.areAllReady(nonReady)) {
19 | log.debug('All browsers are ready, executing');
20 | executionScheduled = false;
21 | capturedBrowsers.setAllIsReadyTo(false);
22 | capturedBrowsers.clearResults();
23 | pendingCount = capturedBrowsers.length;
24 | runningBrowsers = capturedBrowsers.clone();
25 | emitter.emit('run_start', runningBrowsers);
26 | self.socketIoSockets.emit('execute', config.client);
27 | return true;
28 | }
29 |
30 | log.info('Delaying execution, these browsers are not ready: ' + nonReady.join(', '));
31 | executionScheduled = true;
32 | return false;
33 | };
34 |
35 | this.schedule = schedule;
36 |
37 | this.onRunComplete = function() {
38 | if (executionScheduled) {
39 | schedule();
40 | }
41 | };
42 |
43 | this.onBrowserComplete = function() {
44 | pendingCount--;
45 |
46 | if (!pendingCount) {
47 | emitter.emit('run_complete', runningBrowsers, runningBrowsers.getResults());
48 | }
49 | };
50 |
51 | // bind all the events
52 | emitter.bind(this);
53 | };
54 |
55 |
56 | module.exports = Executor;
57 |
--------------------------------------------------------------------------------
/test/e2e/requirejs/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Thu Jul 26 2012 14:35:23 GMT-0700 (PDT)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | frameworks: ['jasmine', 'requirejs'],
10 |
11 | // list of files / patterns to load in the browser
12 | files: [
13 | 'main.js',
14 |
15 | // all the sources, tests
16 | {pattern: '*.js', included: false}
17 | ],
18 |
19 |
20 | // test results reporter to use
21 | // possible values: dots || progress
22 | reporters: ['dots'],
23 |
24 |
25 | // web server port
26 | port: 9876,
27 |
28 |
29 | // enable / disable colors in the output (reporters and logs)
30 | colors: true,
31 |
32 |
33 | // level of logging
34 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
35 | logLevel: config.LOG_INFO,
36 |
37 |
38 | // enable / disable watching file and executing tests whenever any file changes
39 | autoWatch: true,
40 |
41 |
42 | // Start these browsers, currently available:
43 | // - Chrome
44 | // - ChromeCanary
45 | // - Firefox
46 | // - Opera
47 | // - Safari
48 | // - PhantomJS
49 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
50 |
51 |
52 | // Continuous Integration mode
53 | // if true, it capture browsers, run tests and exit
54 | singleRun: false,
55 |
56 | plugins: [
57 | 'karma-requirejs',
58 | 'karma-jasmine',
59 | 'karma-chrome-launcher',
60 | 'karma-firefox-launcher'
61 | ],
62 | });
63 | };
64 |
--------------------------------------------------------------------------------
/test/unit/executor.spec.coffee:
--------------------------------------------------------------------------------
1 | describe 'executor', ->
2 | Browser = require('../../lib/browser').Browser
3 | BrowserCollection = require('../../lib/browser').Collection
4 | EventEmitter = require('../../lib/events').EventEmitter
5 | Executor = require '../../lib/executor'
6 |
7 | executor = emitter = capturedBrowsers = config = spy = null
8 |
9 | beforeEach ->
10 | config = {client: {}}
11 | emitter = new EventEmitter
12 | capturedBrowsers = new BrowserCollection emitter
13 | capturedBrowsers.add new Browser
14 | executor = new Executor capturedBrowsers, config, emitter
15 | executor.socketIoSockets = new EventEmitter
16 |
17 | spy =
18 | onRunStart: -> null
19 | onSocketsExecute: -> null
20 |
21 | sinon.spy spy, 'onRunStart'
22 | sinon.spy spy, 'onSocketsExecute'
23 |
24 | emitter.on 'run_start', spy.onRunStart
25 | executor.socketIoSockets.on 'execute', spy.onSocketsExecute
26 |
27 |
28 | it 'should start the run and pass client config', ->
29 | capturedBrowsers.areAllReady = -> true
30 |
31 | executor.schedule()
32 | expect(spy.onRunStart).to.have.been.called
33 | expect(spy.onSocketsExecute).to.have.been.calledWith config.client
34 |
35 |
36 | it 'should wait for all browsers to finish', ->
37 | capturedBrowsers.areAllReady = -> false
38 |
39 | # they are not ready yet
40 | executor.schedule()
41 | expect(spy.onRunStart).not.to.have.been.called
42 | expect(spy.onSocketsExecute).not.to.have.been.called
43 |
44 | capturedBrowsers.areAllReady = -> true
45 | emitter.emit 'run_complete'
46 | expect(spy.onRunStart).to.have.been.called
47 | expect(spy.onSocketsExecute).to.have.been.called
48 |
--------------------------------------------------------------------------------
/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 files and exclude
8 | basePath: '%BASE_PATH%',
9 |
10 |
11 | // frameworks to use
12 | frameworks: [%FRAMEWORKS%],
13 |
14 |
15 | // list of files / patterns to load in the browser
16 | files: [
17 | %FILES%
18 | ],
19 |
20 |
21 | // list of files to exclude
22 | exclude: [
23 | %EXCLUDE%
24 | ],
25 |
26 |
27 | // test results reporter to use
28 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
29 | reporters: ['progress'],
30 |
31 |
32 | // web server port
33 | port: 9876,
34 |
35 |
36 | // enable / disable colors in the output (reporters and logs)
37 | colors: true,
38 |
39 |
40 | // level of logging
41 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
42 | logLevel: config.LOG_INFO,
43 |
44 |
45 | // enable / disable watching file and executing tests whenever any file changes
46 | autoWatch: %AUTO_WATCH%,
47 |
48 |
49 | // Start these browsers, currently available:
50 | // - Chrome
51 | // - ChromeCanary
52 | // - Firefox
53 | // - Opera
54 | // - Safari (only Mac)
55 | // - PhantomJS
56 | // - IE (only Windows)
57 | browsers: [%BROWSERS%],
58 |
59 |
60 | // If browser does not capture in given timeout [ms], kill it
61 | captureTimeout: 60000,
62 |
63 |
64 | // Continuous Integration mode
65 | // if true, it capture browsers, run tests and exit
66 | singleRun: false
67 | });
68 | };
69 |
--------------------------------------------------------------------------------
/lib/plugin.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var path = require('path');
3 |
4 | var helper = require('./helper');
5 | var log = require('./logger').create('plugin');
6 |
7 |
8 | exports.resolve = function(plugins) {
9 | var modules = [];
10 |
11 | var requirePlugin = function(name) {
12 | log.debug('Loading plugin %s.', name);
13 | try {
14 | modules.push(require(name));
15 | } catch (e) {
16 | if (e.code === 'MODULE_NOT_FOUND' && e.message.indexOf(name) !== -1) {
17 | log.warn('Cannot find plugin "%s".\n Did you forget to install it ?\n' +
18 | ' npm install %s --save-dev', name, name);
19 | } else {
20 | log.warn('Error during loading "%s" plugin:\n %s', name, e.message);
21 | }
22 | }
23 | };
24 |
25 | plugins.forEach(function(plugin) {
26 | if (helper.isString(plugin)) {
27 | if (plugin.indexOf('*') !== -1) {
28 | var pluginDirectory = path.normalize(__dirname + '/../..');
29 | var regexp = new RegExp('^' + plugin.replace('*', '.*'));
30 |
31 | log.debug('Loading %s from %s', plugin, pluginDirectory);
32 | fs.readdirSync(pluginDirectory).filter(function(pluginName) {
33 | return regexp.test(pluginName);
34 | }).forEach(function(pluginName) {
35 | requirePlugin(pluginDirectory + '/' + pluginName);
36 | });
37 | } else {
38 | requirePlugin(plugin);
39 | }
40 | } else if (helper.isObject(plugin)) {
41 | log.debug('Loading inlined plugin (defining %s).', Object.keys(plugin).join(', '));
42 | modules.push(plugin);
43 | } else {
44 | log.warn('Invalid plugin %s', plugin);
45 | }
46 | });
47 |
48 | return modules;
49 | };
50 |
--------------------------------------------------------------------------------
/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/npm
8 | #
9 |
10 | if type complete &>/dev/null; then
11 | __karma_completion () {
12 | local si="$IFS"
13 | IFS=$'\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=$'\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 |
--------------------------------------------------------------------------------
/test/unit/runner.spec.coffee:
--------------------------------------------------------------------------------
1 | #==============================================================================
2 | # lib/runner.js module
3 | #==============================================================================
4 | describe 'runner', ->
5 | loadFile = require('mocks').loadFile
6 | constant = require '../../lib/constants'
7 | m = null
8 |
9 | beforeEach ->
10 | m = loadFile __dirname + '/../../lib/runner.js'
11 |
12 | #============================================================================
13 | # runner.parseExitCode
14 | #============================================================================
15 | describe 'parseExitCode', ->
16 | EXIT = constant.EXIT_CODE
17 |
18 | it 'should return 0 exit code if present in the buffer', ->
19 | expect(m.parseExitCode new Buffer 'something\nfake' + EXIT + '0').to.equal 0
20 |
21 |
22 | it 'should null the exit code part of the buffer', ->
23 | buffer = new Buffer 'some' + EXIT + '1'
24 | m.parseExitCode buffer
25 |
26 | expect(buffer.toString()).to.equal 'some\0\0\0\0\0\0'
27 |
28 |
29 | it 'should not touch buffer without exit code and return default', ->
30 | msg = 'some nice \n messgae {}'
31 | buffer = new Buffer msg
32 | code = m.parseExitCode buffer, 10
33 |
34 | expect(buffer.toString()).to.equal msg
35 | expect(code).to.equal 10
36 |
37 |
38 | it 'should not slice buffer if smaller than exit code msg', ->
39 | # regression
40 | fakeBuffer = {length: 1, slice: -> null}
41 | sinon.stub fakeBuffer, 'slice'
42 |
43 | code = m.parseExitCode fakeBuffer, 10
44 | expect(fakeBuffer.slice).not.to.have.been.called
45 |
46 |
47 | it 'should parse any single digit exit code', ->
48 | expect(m.parseExitCode new Buffer 'something\nfake' + EXIT + '1').to.equal 1
49 | expect(m.parseExitCode new Buffer 'something\nfake' + EXIT + '7').to.equal 7
50 |
--------------------------------------------------------------------------------
/lib/middleware/source-files.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Source Files middleware is responsible for serving all the source files under the test.
3 | */
4 |
5 | var querystring = require('querystring');
6 | var common = require('./common');
7 | var pause = require('connect').utils.pause;
8 |
9 |
10 | var findByPath = function(files, path) {
11 | for (var i = 0; i < files.length; i++) {
12 | if (files[i].path === path) {
13 | return files[i];
14 | }
15 | }
16 |
17 | return null;
18 | };
19 |
20 |
21 | var createSourceFilesMiddleware = function(filesPromise, serveFile,
22 | /* config.basePath */ basePath) {
23 |
24 | return function(request, response, next) {
25 | var requestedFilePath = querystring.unescape(request.url)
26 | .replace(/\?.*/, '')
27 | .replace(/^\/absolute/, '')
28 | .replace(/^\/base/, basePath);
29 |
30 | // Need to pause the request because of proxying, see:
31 | // https://groups.google.com/forum/#!topic/q-continuum/xr8znxc_K5E/discussion
32 | // TODO(vojta): remove once we don't care about Node 0.8
33 | var pausedRequest = pause(request);
34 |
35 | return filesPromise.then(function(files) {
36 | // TODO(vojta): change served to be a map rather then an array
37 | var file = findByPath(files.served, requestedFilePath);
38 |
39 | if (file) {
40 | serveFile(file.contentPath, response, function() {
41 | if (/\?\d+/.test(request.url)) {
42 | // files with timestamps - cache one year, rely on timestamps
43 | common.setHeavyCacheHeaders(response);
44 | } else {
45 | // without timestamps - no cache (debug)
46 | common.setNoCacheHeaders(response);
47 | }
48 | });
49 | } else {
50 | next();
51 | }
52 |
53 | pausedRequest.resume();
54 | });
55 | };
56 | };
57 |
58 |
59 | // PUBLIC API
60 | exports.create = createSourceFilesMiddleware;
61 |
--------------------------------------------------------------------------------
/lib/runner.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 |
3 | var constant = require('./constants');
4 | var helper = require('./helper');
5 | var cfg = require('./config');
6 |
7 |
8 | var parseExitCode = function(buffer, defaultCode) {
9 | var tailPos = buffer.length - Buffer.byteLength(constant.EXIT_CODE) - 1;
10 |
11 | if (tailPos < 0) {
12 | return defaultCode;
13 | }
14 |
15 | // tail buffer which might contain the message
16 | var tail = buffer.slice(tailPos);
17 | var tailStr = tail.toString();
18 | if (tailStr.substr(0, tailStr.length - 1) === constant.EXIT_CODE) {
19 | tail.fill('\x00');
20 | return parseInt(tailStr.substr(-1), 10);
21 | }
22 |
23 | return defaultCode;
24 | };
25 |
26 |
27 | // TODO(vojta): read config file (port, host, urlRoot)
28 | exports.run = function(config, done) {
29 | done = helper.isFunction(done) ? done : process.exit;
30 | config = cfg.parseConfig(config.configFile, config);
31 |
32 | var exitCode = 1;
33 | var options = {
34 | hostname: config.hostname,
35 | path: config.urlRoot + 'run',
36 | port: config.port,
37 | method: 'POST',
38 | headers: {
39 | 'Content-Type': 'application/json'
40 | }
41 | };
42 |
43 | var request = http.request(options, function(response) {
44 | response.on('data', function(buffer) {
45 | exitCode = parseExitCode(buffer, exitCode);
46 | process.stdout.write(buffer);
47 | });
48 |
49 | response.on('end', function() {
50 | done(exitCode);
51 | });
52 | });
53 |
54 | request.on('error', function(e) {
55 | if (e.code === 'ECONNREFUSED') {
56 | console.error('There is no server listening on port %d', options.port);
57 | done(1);
58 | } else {
59 | throw e;
60 | }
61 | });
62 |
63 | request.end(JSON.stringify({
64 | args: config.clientArgs,
65 | removedFiles: config.removedFiles,
66 | changedFiles: config.changedFiles,
67 | addedFiles: config.addedFiles,
68 | refresh: config.refresh
69 | }));
70 | };
71 |
--------------------------------------------------------------------------------
/docs/intro/01-installation.md:
--------------------------------------------------------------------------------
1 | Karma runs on [Node.js] and is available via [NPM].
2 |
3 | ## Requirements
4 |
5 |
6 |
7 |
8 |
9 | There are installers for both Mac and Windows. On Linux, we recommend using
10 | NVM .
11 |
12 |
13 |
14 |
15 |
16 | NPM is a package manager for Node.js which is used to install Karma. This should
17 | automatically be installed when Node.js is installed, but if not then please install
18 | it afterwards.
19 |
20 |
21 |
22 |
23 | ## Global "System-Wide" Installation
24 | This is the recommended approach to installing and making use of Karma. It will install Karma into your global
25 | `node_modules` directory and create a symlink in your system path, so that you can run the `karma`
26 | command from any directory. This means that the `karma` command (which is the central command that Karma uses to run
27 | tests) can be executed anywhere via the command line.
28 |
29 | The following command will install Karma globally:
30 |
31 | ```bash
32 | $ npm install -g karma
33 | ```
34 |
35 | Please note, that the `karma` command will always look for a locally installed instance of Karma first and
36 | before resorting to a global install and, if present, then the local version will be utilized.
37 | This allows you to use different version of Karma per project.
38 |
39 | ## Local Installation
40 | A local installation will install Karma into your current directory's `node_modules`.
41 |
42 | ```bash
43 | $ npm install karma --save-dev
44 | ```
45 |
46 | The karma command can now be also executed directly from the node_modules directory:
47 |
48 | ```bash
49 | $ ./node_modules/.bin/karma
50 | ```
51 |
52 |
53 | [Node.js]: http://nodejs.org/
54 | [NPM]: npmjs.org/package/karma
55 | [NVM]: https://github.com/creationix/nvm
56 |
--------------------------------------------------------------------------------
/lib/middleware/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This module contains some common helpers shared between middlewares
3 | */
4 |
5 | var mime = require('mime');
6 | var log = require('../logger').create('web-server');
7 |
8 | var PromiseContainer = function() {
9 | var promise;
10 |
11 | this.then = function(success, error) {
12 | return promise.then(success, error);
13 | };
14 |
15 | this.set = function(newPromise) {
16 | promise = newPromise;
17 | };
18 | };
19 |
20 |
21 | var serve404 = function(response, path) {
22 | log.warn('404: ' + path);
23 | response.writeHead(404);
24 | return response.end('NOT FOUND');
25 | };
26 |
27 |
28 | var createServeFile = function(fs, directory) {
29 | return function(filepath, response, transform) {
30 | if (directory) {
31 | filepath = directory + filepath;
32 | }
33 |
34 | return fs.readFile(filepath, function(error, data) {
35 | if (error) {
36 | return serve404(response, filepath);
37 | }
38 |
39 | response.setHeader('Content-Type', mime.lookup(filepath, 'text/plain'));
40 |
41 | // call custom transform fn to transform the data
42 | var responseData = transform && transform(data.toString()) || data;
43 |
44 | response.writeHead(200);
45 |
46 | log.debug('serving: ' + filepath);
47 | return response.end(responseData);
48 | });
49 | };
50 | };
51 |
52 |
53 | var setNoCacheHeaders = function(response) {
54 | response.setHeader('Cache-Control', 'no-cache');
55 | response.setHeader('Pragma', 'no-cache');
56 | response.setHeader('Expires', (new Date(0)).toString());
57 | };
58 |
59 |
60 | var setHeavyCacheHeaders = function(response) {
61 | response.setHeader('Cache-Control', ['public', 'max-age=31536000']);
62 | };
63 |
64 |
65 | // PUBLIC API
66 | exports.PromiseContainer = PromiseContainer;
67 | exports.createServeFile = createServeFile;
68 | exports.setNoCacheHeaders = setNoCacheHeaders;
69 | exports.setHeavyCacheHeaders = setHeavyCacheHeaders;
70 | exports.serve404 = serve404;
71 |
--------------------------------------------------------------------------------
/test/unit/completion.spec.coffee:
--------------------------------------------------------------------------------
1 | #==============================================================================
2 | # lib/completion.js module
3 | #==============================================================================
4 | describe 'completion', ->
5 | c = require '../../lib/completion'
6 | completion = null
7 |
8 | mockEnv = (line) ->
9 | words = line.split ' '
10 |
11 | words: words
12 | count: words.length
13 | last: words[words.length - 1]
14 | prev: words[words.length - 2]
15 |
16 | beforeEach ->
17 | sinon.stub console, 'log', (msg) -> completion.push msg
18 | completion = []
19 |
20 | describe 'opositeWord', ->
21 |
22 | it 'should handle --no-x args', ->
23 | expect(c.opositeWord '--no-single-run').to.equal '--single-run'
24 |
25 |
26 | it 'should handle --x args', ->
27 | expect(c.opositeWord '--browsers').to.equal '--no-browsers'
28 |
29 |
30 | it 'should ignore args without --', ->
31 | expect(c.opositeWord 'start').to.equal null
32 |
33 |
34 | describe 'sendCompletion', ->
35 |
36 | it 'should filter only words matching last typed partial', ->
37 | c.sendCompletion ['start', 'init', 'run'], mockEnv 'in'
38 | expect(completion).to.deep.equal ['init']
39 |
40 |
41 | it 'should filter out already used words/args', ->
42 | c.sendCompletion ['--single-run', '--port', '--xxx'], mockEnv 'start --single-run '
43 | expect(completion).to.deep.equal ['--port', '--xxx']
44 |
45 |
46 | it 'should filter out already used oposite words', ->
47 | c.sendCompletion ['--auto-watch', '--port'], mockEnv 'start --no-auto-watch '
48 | expect(completion).to.deep.equal ['--port']
49 |
50 |
51 | describe 'complete', ->
52 |
53 | it 'should complete the basic commands', ->
54 | c.complete mockEnv ''
55 | expect(completion).to.deep.equal ['start', 'init', 'run']
56 |
57 | completion.length = 0 # reset
58 | c.complete mockEnv 's'
59 | expect(completion).to.deep.equal ['start']
60 |
--------------------------------------------------------------------------------
/static/debug.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | Karma DEBUG RUNNER
10 |
11 |
12 |
13 |
14 |
15 |
19 |
42 |
43 | %SCRIPTS%
44 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/docs/plus/04-semaphore.md:
--------------------------------------------------------------------------------
1 | pageTitle: Semaphore CI
2 | menuTitle: Semaphore CI
3 |
4 | [Semaphore] is a popular continuous integration service for
5 | Ruby developers that [integrates] with [GitHub]. It also includes
6 | [Node.js], [PhantomJS] and headless Firefox in its [platform],
7 | making it fit for testing JavaScript applications as well.
8 | This article assumes you already have a Semaphore account.
9 |
10 | ## Configure Your Project
11 |
12 | If you do not already have a `package.json` in your project root,
13 | create one now. This will both document your configuration and
14 | make it easy to run your tests. Here's an example:
15 |
16 | ```javascript
17 | // ...snip...
18 | 'devDependencies': {
19 | 'karma': '~0.10'
20 | },
21 | // ...snip...
22 | 'scripts': {
23 | 'test': './node_modules/.bin/karma start --single-run --browsers PhantomJS'
24 | }
25 | // ...snip...
26 | ```
27 |
28 | Another option is to use Firefox as your test browser. To do this, change
29 | the last part to:
30 |
31 | ```javascript
32 | 'scripts': {
33 | 'test': './node_modules/.bin/karma start --single-run --browsers Firefox'
34 | }
35 | ```
36 |
37 | Now running `npm test` within your project will run your tests with Karma.
38 |
39 | ## Add Your Project to Semaphore
40 |
41 | Follow the process as shown in the [screencast] on the Semaphore homepage.
42 |
43 | After the analysis is finished, ignore the Ruby version Semaphore has set
44 | for you, choose to customize your build commands and use these:
45 |
46 | ```bash
47 | npm install
48 | npm test
49 | ```
50 |
51 | That's it - proceed to your first build. In case you're using Firefox as
52 | your test browser, Semaphore will automatically run it in a virtual screen
53 | during your builds.
54 |
55 | Also, if necessary, build commands can be further [customized] at any time.
56 |
57 |
58 | [screencast]: https://semaphoreapp.com/
59 | [Semaphore]: https://semaphoreapp.com
60 | [integrates]: https://semaphoreapp.com/features
61 | [Github]: https://github.com/
62 | [Node.js]: http://nodejs.org
63 | [PhantomJS]: http://phantomjs.org/
64 | [platform]: http://docs.semaphoreapp.com/version-information
65 | [customized]: http://docs.semaphoreapp.com/custom-build-commands
66 |
--------------------------------------------------------------------------------
/docs/dev/03-contributing.md:
--------------------------------------------------------------------------------
1 | If you are thinking about making Karma better, or you just want to hack on it, that’s great!
2 | Here are some tips to get you started.
3 |
4 | ## Getting Started
5 |
6 | * Make sure you have a [GitHub account](https://github.com/signup/free)
7 | * [Submit](https://github.com/karma-runner/karma/issues/new) a ticket for your issue, assuming one does not
8 | already exist.
9 | * Clearly describe the issue including steps to reproduce when it is a bug.
10 | * Make sure you fill in the earliest version that you know has the issue.
11 | * Fork the repository on GitHub
12 |
13 | ## Making Changes
14 | * Clone your fork
15 | ```bash
16 | $ git clone git@github.com:/karma.git
17 | ```
18 | * Init your workspace
19 |
20 | ```bash
21 | $ ./scripts/init-dev-env.js
22 | ```
23 |
24 | * Checkout a new branch (usually based on `master`) and name it accordingly to what
25 | you intend to do
26 | * Features get the prefix `feature-`
27 | * Bug fixes get the prefix `fix-`
28 | * Improvements to the documentation get the prefix `docs-`
29 |
30 | ## Testing and Building
31 | Run the tests via
32 | ```bash
33 | # All tests
34 | $ grunt test
35 |
36 | $ grunt test:unit
37 | $ grunt test:e2e
38 | $ grunt test:client
39 | ```
40 | Lint the files via
41 | ```bash
42 | $ grunt lint
43 | ```
44 | Build the project via
45 | ```bash
46 | $ grunt build
47 | ```
48 | The default task, just calling `grunt` will run `build lint test`.
49 |
50 | If grunt fails, make sure grunt-0.4x is installed: https://github.com/gruntjs/grunt/wiki/Getting-started.
51 |
52 | ## Submitting Changes
53 |
54 | * One branch per feature/fix
55 | * Follow http://nodeguide.com/style.html (with exception of 100 characters per line)
56 | * Please follow [commit message conventions].
57 | * Send a pull request to the `master` branch.
58 |
59 |
60 | ## Additional Resources
61 |
62 | * [Issue tracker](https://github.com/karma-runner/karma/issues)
63 | * [Mailing List](https://groups.google.com/forum/#!forum/karma-users)
64 | * [General GitHub documentation](http://help.github.com/)
65 | * [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
66 | * [@JsKarma](http://twitter.com/JsKarma)
67 |
68 | [commit message conventions]: git-commit-msg.html
69 |
--------------------------------------------------------------------------------
/docs/config/02-files.md:
--------------------------------------------------------------------------------
1 | **The `files` array determines which files are included in the browser and which files are watched and served by Karma.**
2 |
3 |
4 | ## Pattern matching and `basePath`
5 | - All of the relative patterns will get resolved to `basePath` first.
6 | - If the `basePath` is a relative path, it gets resolved to the
7 | directory where the configuration file is.
8 | - Eventually, all the patterns will get resolved into files using
9 | [glob], so you can use expressions like `test/unit/**/*.spec.js`.
10 |
11 | ## Ordering
12 | - The order of patterns determines the order of files in which they
13 | are included in the browser.
14 | - Multiple files matching a single pattern are sorted alphabetically.
15 | - Each file is included exactly once. If multiple patterns match the
16 | same file, it's included as if it only matched the first pattern.
17 |
18 | ## Included, served, watched
19 | Each pattern is either a simple string or an object with four properties:
20 |
21 | ### `pattern`
22 | * **Description.** The pattern to use for matching. This property is mandatory.
23 |
24 | ### `watched`
25 | * **Type.** Boolean
26 | * **Default.** `true`
27 | * **Description.** If `autoWatch` is `true` all files that have set `watched` to true will be
28 | watched for changes.
29 |
30 | ### `included`
31 | * **Type.** Boolean
32 | * **Default.** `true`
33 | * **Description.** Should the files be included in the browser using
34 | `
113 |
114 |