title: Full Integration Testing for NodeJS and the Client Side with Yeoman and Mocha
body: <p>I got awesome client side tests that I run with Yeoman. Yeoman compiles my CoffeeScript, opens up the test page in a server, visit it with PhantomJS and pass all the tests results to the command line. The process is pretty hacky, the test results are passed via <code>alert()</code> messages to the Phantom process which creates a temporary file and fills it with the messages as JSON. Yeoman (well, Grunt) loops over the temporary file, parses the tests and displays them in the command line.</p>
  
  <p>The reason I explained the process is that I want to add a few things to it. I got server side tests as well. They use mocha and supertest to check the API endpoints and a Redis client to make sure the database state is as expected. But I want to merge those two test suites!</p>
  
  <p>I don't want to write client side mock response for the server calls. I don't want to send the server mock data. Somewhere along the way I'll change the server or the client and the test will not fail. I want to do a real integration testing. So, whenever a test finishes in the client side I want a hook to run a relevant test on the server side (checking db state, session state, moving to a different test page).</p>
  
  <p>Are there any solutions to this? Or, altenatively, where do I start hacking on Yeoman / Grunt / grunt-mocha to make this work?</p>
  
  <p>I think the Phantom Handlers in grunt-mocha is a good place to start:</p>
  
  <pre><code>// Handle methods passed from PhantomJS, including Mocha hooks.
    var phantomHandlers = {
      // Mocha hooks.
      suiteStart: function(name) {
        unfinished[name] = true;
        currentModule = name;
      },
      suiteDone: function(name, failed, passed, total) {
        delete unfinished[name];
      },
      testStart: function(name) {
        currentTest = (currentModule ? currentModule + ' - ' : '') + name;
        verbose.write(currentTest + '...');
      },
      testFail: function(name, result) {
          result.testName = currentTest;
          failedAssertions.push(result);
      },
      testDone: function(title, state) {
        // Log errors if necessary, otherwise success.
        if (state == 'failed') {
          // list assertions
          if (option('verbose')) {
            log.error();
            logFailedAssertions();
          } else {
            log.write('F'.red);
          }
        } else {
          verbose.ok().or.write('.');
        }
      },
      done: function(failed, passed, total, duration) {
        var nDuration = parseFloat(duration) || 0;
        status.failed += failed;
        status.passed += passed;
        status.total += total;
        status.duration += Math.round(nDuration*100)/100;
        // Print assertion errors here, if verbose mode is disabled.
        if (!option('verbose')) {
          if (failed &gt; 0) {
            log.writeln();
            logFailedAssertions();
          } else {
            log.ok();
          }
        }
      },
      // Error handlers.
      done_fail: function(url) {
        verbose.write('Running PhantomJS...').or.write('...');
        log.error();
        grunt.warn('PhantomJS unable to load "' + url + '" URI.', 90);
      },
      done_timeout: function() {
        log.writeln();
        grunt.warn('PhantomJS timed out, possibly due to a missing Mocha run() call.', 90);
      },
  
      // console.log pass-through.
      // console: console.log.bind(console),
      // Debugging messages.
      debug: log.debug.bind(log, 'phantomjs')
    };
  </code></pre>
  
  <p>Thanks! There will be a bounty on this.</p>