fnando/test_squad

View on GitHub
phantomjs/runner.js

Summary

Maintainability
A
2 hrs
Test Coverage
/* jshint -W100 */
/* global phantom */
(function () {
  'use strict';

  var system = require('system');
  var webpage = require('webpage');
  var helpers = require('./helpers');
  var output = helpers.output;
  var colored = helpers.colored;
  var textForSelector = helpers.textForSelector;

  var args = system.args;
  var url = args[1];
  var timeout = parseInt(args[2], 10);
  var page = webpage.create();
  var suites = [];
  var suite, test;
  var errorBuffer = [];
  var pendingBuffer = [];
  var resourceErrors = [];

  page.viewportSize = {
    width: 800,
    height: 800
  };

  var testOutput = function(test) {
    if (test.passed) {
      output('•', 'green');
    } else if (test.pending) {
      output('*', 'yellow');
      pendingBuffer.push(testDescription(test, 'yellow'));
    } else {
      output('×', 'red');
      errorBuffer.push(testDescription(test, 'red'));
    }
  };

  var testDescription = function(test, color) {
    var status = test.pending? '[PENDING]' : '[FAILURE]';

    var description = test.suites.map(function(suite) {
      return suite.title;
    }).join(' ') + ' ' + test.title;

    description = status + ' ' + description.replace(/^\s+/gm, '');

    if (test.failure) {
      description += '\n';
      description += test.failure.replace(/^\s*/gm, '    ');
    }

    description = colored(description, color);

    if (test.logging.length) {
      description += '\n';

      for (var i = 0; i < test.logging.length; i++) {
        var lines = test.logging[i].split(/\r?\n/);
        description += '\n';
        description += colored('    => ' + lines.shift(), 'gray');

        if (lines.length > 0) {
          description += '\n';
          description += colored(lines.join('\n').replace(/^/gm, '       '), 'gray');
        }
      }
    }

    return description;
  };

  var pluralize = function(count, singular, plural) {
    if (!plural) {
      plural = singular + 's';
    }

    return count + ' ' + (count === 1 ? singular : plural);
  };

  page.onConsoleMessage = function(message) {
    if (test) {
      test.logging.push(message);
    } else {
      console.log(colored('=> ' + message, 'gray'));
    }
  };

  page.onResourceError = function(error) {
    resourceErrors.push(error);
  };

  page.onCallback = function(message){
    if (message.name === 'suite start') {
      suite = {tests: []};
      suite.title = message.title;

      if (message.title) {
        suites.push(suite);
      }
    } else if (message.name === 'suite end') {
      suites = [];
    } else if (message.name === 'test start') {
      test = {logging: []};
      test.suites = suites;
      suite.tests.push(test);
    } else if (message.name === 'test end') {
      test.title = message.title;
      test.passed = message.passed;
      test.pending = message.pending;
      test.failure = message.failure;
      testOutput(test);
      test = null;
    } else if (message.name === 'end') {
      var stats = message.stats;
      var summary = [];
      var color;
      var hasErrorBuffer = errorBuffer.length > 0;
      var hasPendingBuffer = pendingBuffer.length > 0;

      if (hasErrorBuffer || hasPendingBuffer) {
        console.log('');
      }

      if (hasErrorBuffer) {
        console.log('\n' + errorBuffer.join('\n\n'));
      }

      if (hasPendingBuffer) {
        console.log('\n' + pendingBuffer.join('\n'));
      }

      console.log('');

      if (stats.tests > 0) {
        summary.push(pluralize(stats.tests, 'test'));

        if (stats.assertions) {
          summary.push(pluralize(stats.assertions, 'assertion'));
        }

        if (stats.pending) {
          summary.push(pluralize(stats.pending, 'test') + ' pending');
        }

        if (stats.fails) {
          summary.push(pluralize(stats.fails, 'test') + ' failed');
        }
      } else {
        summary.push('No tests were found.');
      }

      if (stats.fails) {
        color = 'red';
      } else if (stats.pending) {
        color = 'yellow';
      } else {
        console.log('');
        color = 'green';
      }

      summary = colored(summary.join(', '), color);
      summary += colored(' (' + (stats.elapsed / 1000) + 's)', 'gray');

      console.log(summary);
      exit(stats.fails ? 1 : 0);
    }
  };

  page.open(url, function(status){
    if (status === 'fail') {
      console.error('Unable to access network: ' + status);
      exit(1);
    } else {
      if (resourceErrors.length === 1 && resourceErrors[0].url === url) {
        var errorHeader = textForSelector(page, 'header > h1');
        var errorDescription = textForSelector(page, '#container > pre');
        var matches = errorDescription.match(/^(.*?) \(in (.*?)\)$/)

        console.error(colored('ERROR:', 'red'), errorHeader, '-', matches[1]);
        console.error(colored(matches[2], 'gray'));
        exit(1);
      } else {
        setTimeout(function(){
          console.error(colored('ERROR:', 'red'), 'The specified timeout of ' + timeout + ' seconds has expired. Aborting...');
          exit(1);
        }, timeout * 1000);
      }
    }
  });

  function exit(code) {
    if (page) {
      page.close();
    }

    setTimeout(function(){
      phantom.exit(code);
    }, 0);
  }
})();