karma-runner / grunt-karma

Grunt plugin for Karma.
MIT License
468 stars 116 forks source link

PhantomJS not killed on error or ctrl+c #93

Open apaleslimghost opened 10 years ago

apaleslimghost commented 10 years ago

If a preprocessor or framework errors, or you Ctrl+C Grunt, PhantomJS is left running in the background. Next time Karma is run, you get two [PhantomJS] Connected on socket lines.

jpommerening commented 10 years ago

I had the same issue on Linux recently, with grunt-karma 0.8.2 and karma-phantomjs-launcher 0.1.2.

The problem seems to only occur with Grunt: I replicated my Grunt task configuration (singleRun, with Jasmine, RequireJS and Proxies) in a simple Karma config file and tried to reproduce the problem calling karma directly. Outside the Grunt process it works just fine.

Looks like the problem is that Grunt exits the process before Karma has a chance to disconnect the browsers.

I've patched require.load() to unconditionally throw an error and this is what happens (some paths stripped out):

$ karma start test.js
INFO [karma]: Karma v0.12.2 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.7 (Linux)]: Connected on socket gZwhkY83Idp_3v0_cT8t with id 92693974
PhantomJS 1.9.7 (Linux) ERROR
  Error: hi mom!
  at /.../node_modules/.../adapter.js:1

ERROR [karma]: [TypeError: Cannot call method 'att' of undefined]
TypeError: Cannot call method 'att' of undefined
    at onBrowserComplete (/.../node_modules/karma-junit-reporter/index.js:50:11)
    at null.<anonymous> (/.../node_modules/karma/lib/events.js:15:22)
    at EventEmitter.emit (events.js:117:20)
    at onComplete (/.../node_modules/karma/lib/browser.js:145:13)
    at Socket.<anonymous> (/.../node_modules/karma/lib/events.js:15:22)
    at Socket.EventEmitter.emit [as $emit] (events.js:117:20)
    at SocketNamespace.handlePacket (/.../node_modules/karma/node_modules/socket.io/lib/namespace.js:335:22)
    at Manager.onClientMessage (/.../node_modules/karma/node_modules/socket.io/lib/manager.js:488:38)
    at WebSocket.Transport.onMessage (/.../node_modules/karma/node_modules/socket.io/lib/transport.js:387:20)
    at Parser.<anonymous> (/../node_modules/karma/node_modules/socket.io/lib/transports/websocket/default.js:36:10)
$ grunt karma
Running "karma:test" (karma) task
INFO [karma]: Karma v0.12.2 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.7 (Linux)]: Connected on socket W0TcZ2eyRyIRXyXlbnLj with id 73082112
PhantomJS 1.9.7 (Linux) ERROR
  Error: hi mom!
  at /.../node_modules/.../adapter.js:1

Fatal error: Cannot call method 'att' of undefined

The first error ("hi mom!") is properly logged by Karma (via PhantomJS), then grunt.fail.fatal() is called with a TypeError. In this case the culprit seems to be the JUnit reporter trying to report the error on a suite that has not started yet. Whatever the case – it looks like the second error raised from the reporter gets caught by Grunt's uncaughtException handler which in turn calls grunt.util.exit().

The only solution I can come up with (other than fixing the preprocessors or frameworks) is removing Grunt's uncaughtException handler before starting Karma and resetting it when the task is done:

var uncaughtListeners = process.listeners('uncaughtException');
process.removeAllListeners('uncaughtException');

function resetListeners(code) {
  uncaughtListeners.forEach( process.on.bind(process, 'uncaughtException') );
  done(code);
}

server.start(data, finished.bind(resetListeners));

Is there a better way to do it?