/*

* Runs QUnit tests in PhantomJS and outputs test result data, in JSON format, to stdout.
*
* Tokens are placed around each test result to allow them to be parsed individually. The tokens
* match the constants in QUnited::Driver::ResultsCollector.
*
* Usage:
*   phantomjs runner.js PATH_TO_TESTS_HTML_PAGE
*/

var system = require(‘system’),

webpage = require('webpage');

if (system.args.length < 2) {

console.log('No tests file specified');
phantom.exit(1);

}

var page = webpage.create(),

testsHtmlFile = system.args[1],
config = {
  resultsCheckInterval: 200,        // Check for new results at this interval
  testsCompletedCheckInterval: 100, // Check for all tests completing at this interval
  testsTimeout: 10001               // Time out and indicate error after this many millis
};

/*

* Writes any collected QUnit results that are pending output to stdout (this is done with
* console.log in PhantomJS).
*
* Tokens are placed around each test result to allow them to be parsed out later. Note that the
* tokens must match the constants in QUnited::Driver::ResultsCollector.
*
* JSON.stringify must be called in the context of the page to properly serialize null values in
* results. If it is called here in the PhantomJS interpreter code then null values are serialized
* as empty strings. Finding out exactly why this happens would take more investigation. For now
* it seems that stringifying on the page is a decent solution, though it is slightly less robust
* since JSON serialization may be tampered with in user code.
*/

function writePendingTestResults() {

var serializedResults = page.evaluate(function() {
  var pendingResults = [];
  while (QUnited.testResultsPendingOutput.length > 0) {
    pendingResults.push(QUnited.util.jsonStringify(QUnited.testResultsPendingOutput.shift()));
  }
  return pendingResults;
});

var i, output;
for (i = 0; i < serializedResults.length; i++) {
  output = 'QUNITED_TEST_RESULT_START_TOKEN';
  output += serializedResults[i];
  output += 'QUNITED_TEST_RESULT_END_TOKEN';
  console.log(output);
}

}

/*

* Executes the given function once all tests have completed. If a timeout occurs then exit
* with a status code of 1.
*/

function whenTestsHaveCompleted(fn) {

var start = new Date().getTime(),
    testsHaveCompleted = false,
    interval = setInterval(function() {
      if ( (new Date().getTime() - start < config.testsTimeout) && !testsHaveCompleted ) {
        testsHaveCompleted = page.evaluate(function() { return QUnited.testsHaveCompleted; });
      } else {
        if (testsHaveCompleted) {
          fn();
          clearInterval(interval);
        } else {
          // Tests took too long
          console.log("ERROR: Timeout waiting for tests to complete");
          phantom.exit(1);
        }
      }
    }, config.testsCompletedCheckInterval);

};

/*

* Open the HTML page that contains all of our QUnit tests. As it is running, check for collected
* test results and output them if we have any. Also check whether tests have completed. Once they
* have completed, output any remaining results and exit.
*/

page.open(testsHtmlFile, function(status) {

if (status !== "success") {
    console.log("Could not open tests file");
    phantom.exit(1);
} else {
    setInterval(writePendingTestResults, config.resultsCheckInterval);

    whenTestsHaveCompleted(function() {
        writePendingTestResults();
        phantom.exit(0);
    });
}

});