diff --git a/docs/config/01-configuration-file.md b/docs/config/01-configuration-file.md index ea58e3c0e..6198f6ea5 100644 --- a/docs/config/01-configuration-file.md +++ b/docs/config/01-configuration-file.md @@ -275,6 +275,12 @@ will return exit-code `0` and display a warning. See [config/files] for more information. +## forceJSONP +**Type:** Boolean + +**Default:** `false` + +**Description:** Force socket.io to use JSONP polling instead of XHR polling. ## frameworks **Type:** Array diff --git a/lib/config.js b/lib/config.js index 40621b402..7c90eb459 100644 --- a/lib/config.js +++ b/lib/config.js @@ -253,6 +253,7 @@ var Config = function () { this.reportSlowerThan = 0 this.loggers = [constant.CONSOLE_APPENDER] this.transports = ['polling', 'websocket'] + this.forceJSONP = false this.plugins = ['karma-*'] this.defaultClient = this.client = { args: [], diff --git a/lib/server.js b/lib/server.js index 4c8084419..a8fa70ce4 100644 --- a/lib/server.js +++ b/lib/server.js @@ -30,7 +30,8 @@ function createSocketIoServer (webServer, executor, config) { // avoid destroying http upgrades from socket.io to get proxied websockets working destroyUpgrade: false, path: config.urlRoot + 'socket.io/', - transports: config.transports + transports: config.transports, + forceJSONP: config.forceJSONP }) // hack to overcome circular dependency diff --git a/package.json b/package.json index 5561b5212..60bda893b 100644 --- a/package.json +++ b/package.json @@ -303,6 +303,7 @@ "grunt-mocha-test": "^0.12.7", "grunt-npm": "0.0.2", "jasmine-core": "^2.3.4", + "json3": "^3.3.2", "karma-browserify": "^5.0.1", "karma-browserstack-launcher": "^0.1.10", "karma-chrome-launcher": "*", diff --git a/test/client/karma.conf.js b/test/client/karma.conf.js index 67cc7a90c..4422ca7cb 100644 --- a/test/client/karma.conf.js +++ b/test/client/karma.conf.js @@ -1,41 +1,68 @@ -var TRAVIS_WITHOUT_SAUCE = process.env.TRAVIS_SECURE_ENV_VARS === 'false' +var TRAVIS_WITHOUT_BS = process.env.TRAVIS_SECURE_ENV_VARS === 'false' var launchers = { - sl_chrome: { - base: 'SauceLabs', - browserName: 'chrome', - platform: 'Windows 7', - version: '47' + bs_chrome: { + base: 'BrowserStack', + browser: 'chrome', + os: 'Windows', + os_version: '10' }, - sl_firefox: { - base: 'SauceLabs', - browserName: 'firefox', - version: '43' + bs_firefox: { + base: 'BrowserStack', + browser: 'firefox', + os: 'Windows', + os_version: '10' }, - sl_safari: { - base: 'SauceLabs', - browserName: 'safari', - version: '9', - platform: 'OS X 10.11' + bs_safari: { + base: 'BrowserStack', + browser: 'safari', + browser_version: '9.0', + os_version: 'El Capitan', + os: 'OS X' }, - sl_ie_11: { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 8.1', - version: '11' + bs_ie_11: { + base: 'BrowserStack', + browser: 'ie', + browser_version: '11.0', + os: 'Windows', + os_version: '10' }, - sl_ie_10: { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 7', - version: '10' + bs_ie_10: { + base: 'BrowserStack', + browser: 'ie', + browser_version: '10.0', + os: 'Windows', + os_version: '8' + }, + bs_ie_9: { + base: 'BrowserStack', + browser: 'ie', + browser_version: '9.0', + os: 'Windows', + os_version: '7' } + // TODO: Figure out why these fail on browserstack + // , + // bs_ie_8: { + // base: 'BrowserStack', + // browser: 'ie', + // browser_version: '8.0', + // os: 'Windows', + // os_version: '7' + // }, + // bs_ie_7: { + // base: 'BrowserStack', + // browser: 'ie', + // browser_version: '7.0', + // os: 'Windows', + // os_version: 'XP' + // } } var browsers = [] if (process.env.TRAVIS) { - if (TRAVIS_WITHOUT_SAUCE) { + if (TRAVIS_WITHOUT_BS) { browsers.push('Firefox') } else { browsers = Object.keys(launchers) @@ -67,7 +94,7 @@ module.exports = function (config) { // use dots reporter, as travis terminal does not support escaping sequences // possible values: 'dots', 'progress' // CLI --reporters progress - reporters: ['progress', 'junit', 'saucelabs'], + reporters: ['progress', 'junit'], junitReporter: { // will be resolved to basePath (in the same way as files/exclude patterns) @@ -122,7 +149,15 @@ module.exports = function (config) { 'karma-firefox-launcher', 'karma-junit-reporter', 'karma-browserify', - 'karma-sauce-launcher' - ] + 'karma-browserstack-launcher' + ], + + concurrency: 3, + + forceJSONP: true, + + browserStack: { + project: 'Karma' + } }) } diff --git a/test/client/karma.spec.js b/test/client/karma.spec.js index 09ff93ff7..f5669e847 100644 --- a/test/client/karma.spec.js +++ b/test/client/karma.spec.js @@ -1,7 +1,8 @@ +// shim all the things +require('core-js/es5') +global.JSON = require('json3') var sinon = require('sinon') -var chai = require('chai') -chai.use(require('sinon-chai')) -var expect = chai.expect +var assert = require('assert') var Karma = require('../../client/karma') var MockSocket = require('./mocks').Socket @@ -31,10 +32,10 @@ describe('Karma', function () { } socket.emit('execute', config) - expect(startSpy).to.not.have.been.called + assert(!startSpy.called) k.loaded() - expect(startSpy).to.have.been.calledWith(config) + assert(startSpy.calledWith(config)) }) it('should open a new window when useIFrame is false', function () { @@ -43,24 +44,24 @@ describe('Karma', function () { } socket.emit('execute', config) - expect(k.start).to.not.have.been.called + assert(!k.start.called) k.loaded() - expect(startSpy).to.have.been.calledWith(config) - expect(windowStub).to.have.been.calledWith('about:blank') + assert(startSpy.calledWith(config)) + assert(windowStub.calledWith('about:blank')) }) it('should stop execution', function () { sinon.spy(k, 'complete') socket.emit('stop') - expect(k.complete).to.have.been.called + assert(k.complete.called) }) it('should not start execution if any error during loading files', function () { k.error('syntax error', '/some/file.js', 11) k.loaded() sinon.spy(k, 'start') - expect(startSpy).to.not.have.been.called + assert(!startSpy.called) }) it('should remove reference to start even after syntax error', function () { @@ -69,11 +70,11 @@ describe('Karma', function () { k.start = ADAPTER_START_FN k.error('syntax error', '/some/file.js', 11) k.loaded() - expect(k.start).to.not.be.eql(ADAPTER_START_FN) + assert.notEqual(k.start, ADAPTER_START_FN) k.start = ADAPTER_START_FN k.loaded() - expect(k.start).to.not.be.eql(ADAPTER_START_FN) + assert.notEqual(k.start, ADAPTER_START_FN) }) it('should not set up context if there was an error', function () { @@ -88,9 +89,9 @@ describe('Karma', function () { k.error('page reload') k.setupContext(mockWindow) - expect(mockWindow.__karma__).to.not.exist - expect(mockWindow.onbeforeunload).to.not.exist - expect(mockWindow.onerror).to.not.exist + assert(mockWindow.__karma__ == null) + assert(mockWindow.onbeforeunloadK == null) + assert(mockWindow.onerror == null) }) it('should setup context if there was error but clearContext config is false', function () { @@ -105,14 +106,14 @@ describe('Karma', function () { k.error('page reload') k.setupContext(mockWindow) - expect(mockWindow.__karma__).to.exist - expect(mockWindow.onbeforeunload).to.exist - expect(mockWindow.onerror).to.exist + assert(mockWindow.__karma__ != null) + assert(mockWindow.onbeforeunload != null) + assert(mockWindow.onerror != null) }) it('should report navigator name', function () { var spyInfo = sinon.spy(function (info) { - expect(info.name).to.be.eql('Fake browser name') + assert(info.name === 'Fake browser name') }) windowNavigator.userAgent = 'Fake browser name' @@ -120,7 +121,7 @@ describe('Karma', function () { socket.on('register', spyInfo) socket.emit('connect') - expect(spyInfo).to.have.been.called + assert(spyInfo.called) }) it('should report browser id', function () { @@ -129,13 +130,13 @@ describe('Karma', function () { k = new Karma(socket, {}, windowStub, windowNavigator, windowLocation) var spyInfo = sinon.spy(function (info) { - expect(info.id).to.be.eql('567') + assert(info.id === '567') }) socket.on('register', spyInfo) socket.emit('connect') - expect(spyInfo).to.have.been.called + assert(spyInfo.called) }) describe('result', function () { @@ -150,11 +151,11 @@ describe('Karma', function () { k.result({id: i}) } - expect(spyResult).to.not.have.been.called + assert(!spyResult.called) k.result('result', {id: 50}) - expect(spyResult).to.have.been.called - expect(spyResult.args[0][0].length).to.be.eql(50) + assert(spyResult.called) + assert(spyResult.args[0][0].length === 50) }) it('should buffer results when polling', function () { @@ -169,8 +170,8 @@ describe('Karma', function () { } k.complete() - expect(spyResult).to.have.been.called - expect(spyResult.args[0][0].length).to.be.eql(40) + assert(spyResult.called) + assert(spyResult.args[0][0].length === 40) }) it('should emit "start" with total specs count first', function () { @@ -188,7 +189,7 @@ describe('Karma', function () { // adapter didn't call info({total: x}) k.result() - expect(log).to.be.eql(['start', 'result']) + assert.deepEqual(log, ['start', 'result']) }) it('should not emit "start" if already done by the adapter', function () { @@ -209,8 +210,8 @@ describe('Karma', function () { k.info({total: 321}) k.result() - expect(log).to.be.eql(['start', 'result']) - expect(spyStart).to.have.been.calledWith({total: 321}) + assert.deepEqual(log, ['start', 'result']) + assert(spyStart.calledWith({total: 321})) }) }) @@ -226,7 +227,7 @@ describe('Karma', function () { k.setupContext(mockWindow) mockWindow.alert('What?') - expect(k.log).to.have.been.calledWith('alert', ['What?']) + assert(k.log.calledWith('alert', ['What?'])) }) }) @@ -235,16 +236,16 @@ describe('Karma', function () { k.store('a', 10) k.store('b', [1, 2, 3]) - expect(k.store('a')).to.be.eql(10) - expect(k.store('b')).to.be.eql([1, 2, 3]) + assert.equal(k.store('a'), 10) + assert.deepEqual(k.store('b'), [1, 2, 3]) }) it('should clone arrays to avoid memory leaks', function () { var array = [1, 2, 3, 4, 5] k.store('one.array', array) - expect(k.store('one.array')).to.be.eql(array) - expect(k.store('one.array')).to.be.eql(array) + assert.deepEqual(k.store('one.array'), array) + assert.deepEqual(k.store('one.array'), array) }) }) @@ -270,10 +271,10 @@ describe('Karma', function () { k.result({id: i}) } - expect(spyResult).to.not.have.been.called + assert(!spyResult.called) k.complete() - expect(spyResult).to.have.been.called + assert(spyResult.called) }) it('should navigate the client to return_url if specified', function (done) { @@ -291,7 +292,7 @@ describe('Karma', function () { clock.tick(500) setTimeout(function () { - expect(windowLocation.href).to.be.eql('http://return.com') + assert(windowLocation.href === 'http://return.com') done() }, 5) clock.tick(10) @@ -309,8 +310,8 @@ describe('Karma', function () { k.setupContext(mockWindow) mockWindow.console.log('What?') - expect(k.log).to.have.been.calledWith('log') - expect(k.log.args[0][1][0]).to.be.eql('What?') + assert(k.log.calledWith('log')) + assert(k.log.args[0][1][0] === 'What?') }) it('should not patch the console if captureConsole is false', function () { @@ -325,7 +326,7 @@ describe('Karma', function () { k.setupContext(mockWindow) mockWindow.console.log('hello') - expect(k.log).to.not.have.been.called + assert(!k.log.called) }) it('should clear context window upon complete when clearContext config is true', function () { @@ -338,9 +339,11 @@ describe('Karma', function () { k.complete() - clock.tick(1) - - expect(iframe.src).to.not.be.eql(CURRENT_URL) + // clock.tick() does not work in IE 7 + setTimeout(function () { + clock.tick(1) + assert.notEqual(iframe.src, CURRENT_URL) + }, 10) }) it('should not clear context window upon complete when clearContext config is false', function () { @@ -355,7 +358,7 @@ describe('Karma', function () { clock.tick(1) - expect(iframe.src).to.be.eql(CURRENT_URL) + assert.equal(iframe.src, CURRENT_URL) }) }) }) diff --git a/test/client/stringify.spec.js b/test/client/stringify.spec.js index 476d902a4..ecd44d4b4 100644 --- a/test/client/stringify.spec.js +++ b/test/client/stringify.spec.js @@ -1,22 +1,21 @@ /* global __karma__ */ -var chai = require('chai') -var expect = chai.expect +var assert = require('assert') var stringify = require('../../client/stringify') describe('stringify', function () { it('should serialize string', function () { - expect(stringify('aaa')).to.be.eql("'aaa'") + assert.deepEqual(stringify('aaa'), "'aaa'") }) it('should serialize booleans', function () { - expect(stringify(true)).to.be.eql('true') - expect(stringify(false)).to.be.eql('false') + assert.deepEqual(stringify(true), 'true') + assert.deepEqual(stringify(false), 'false') }) it('should serialize null and undefined', function () { - expect(stringify(null)).to.be.eql('null') - expect(stringify()).to.be.eql('undefined') + assert.deepEqual(stringify(null), 'null') + assert.deepEqual(stringify(), 'undefined') }) it('should serialize functions', function () { @@ -28,59 +27,66 @@ describe('stringify', function () { var partsDef = ['function', '(d, e, f)', '{ ... }'] partsAbc.forEach(function (part) { - expect(abcString).to.contain(part) + assert(abcString.indexOf(part) > -1) }) var defString = stringify(def) partsDef.forEach(function (part) { - expect(defString).to.contain(part) + assert(defString.indexOf(part) > -1) }) }) it('should serialize arrays', function () { - expect(stringify(['a', 'b', null, true, false])).to.be.eql("['a', 'b', null, true, false]") + assert.deepEqual(stringify(['a', 'b', null, true, false]), "['a', 'b', null, true, false]") }) it('should serialize objects', function () { var obj obj = {a: 'a', b: 'b', c: null, d: true, e: false} - expect(stringify(obj)).to.contain("{a: 'a', b: 'b', c: null, d: true, e: false}") + assert(stringify(obj).indexOf("{a: 'a', b: 'b', c: null, d: true, e: false}") > -1) function MyObj () { this.a = 'a' } obj = new MyObj() - expect(stringify(obj)).to.contain("{a: 'a'}") + assert(stringify(obj).indexOf("{a: 'a'}") > -1) obj = {constructor: null} - expect(stringify(obj)).to.contain('{constructor: null}') + + // IE 7 serializes this to Object{} + var s = stringify(obj) + assert(s.indexOf('{constructor: null}') > -1 || s.indexOf('Object{}') > -1) obj = Object.create(null) obj.a = 'a' - expect(stringify(obj)).to.contain("{a: 'a'}") + + assert(stringify(obj).indexOf("{a: 'a'}") > -1) }) it('should serialize html', function () { var div = document.createElement('div') - expect(stringify(div)).to.be.eql('
') + assert.deepEqual(stringify(div).trim().toLowerCase(), '
') div.innerHTML = 'some text' - expect(stringify(div)).to.be.eql('
some text
') + assert.deepEqual(stringify(div).trim().toLowerCase(), '
some text
') }) it('should serialize DOMParser objects', function () { - var parser = new DOMParser() - var doc = parser.parseFromString('', 'application/xml') - expect(stringify(doc)).to.be.eql('') + if (typeof DOMParser !== 'undefined') { + // Test only works in IE 9 and above + var parser = new DOMParser() + var doc = parser.parseFromString('', 'application/xml') + assert.deepEqual(stringify(doc), '') + } }) it('should serialize across iframes', function () { var div = document.createElement('div') - expect(__karma__.stringify(div)).to.be.eql('
') + assert.deepEqual(__karma__.stringify(div).trim().toLowerCase(), '
') - expect(__karma__.stringify([1, 2])).to.be.eql('[1, 2]') + assert.deepEqual(__karma__.stringify([1, 2]), '[1, 2]') }) }) diff --git a/test/client/util.spec.js b/test/client/util.spec.js index 294e7f584..88bd4fa31 100644 --- a/test/client/util.spec.js +++ b/test/client/util.spec.js @@ -1,5 +1,4 @@ -var chai = require('chai') -var expect = chai.expect +var assert = require('assert') var util = require('../../client/util') @@ -7,6 +6,6 @@ describe('util', function () { it('parseQueryParams', function () { var params = util.parseQueryParams('?id=123&return_url=http://whatever.com') - expect(params).to.be.eql({id: '123', return_url: 'http://whatever.com'}) + assert.deepEqual(params, {id: '123', return_url: 'http://whatever.com'}) }) })