wyeworks/ember-qunit-nice-errors

View on GitHub
lib/transform-qunit-assertions.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict';

var supportedAssertions = require('./supported-assertions.js');
var recast = require('recast');
var builders = recast.types.builders;
var types = recast.types.namedTypes;

function isSupportedAssertion(node, assert) {
  return types.MemberExpression.check(node.callee) &&
    node.callee.object.name === assert &&
    supportedAssertions[node.callee.property.name];
}

function needsMessage(node) {
  var argumentCount = node.arguments.length;

  return isUnaryAssertion(node) && argumentCount === 1 ||
    isBinaryAssertion(node) && argumentCount === 2;
}

function isBinaryAssertion(node) {
  return supportedAssertions[node.callee.property.name] === 'binary';
}

function isUnaryAssertion(node) {
  return supportedAssertions[node.callee.property.name] === 'unary';
}

function buildMessage(node, options) {
  var str = recast.print(node).code;

  if (options && options.file) {
    str += ' at ' + location(node, options.file);
  }

  return str;
}

function addAssertionMessage(node, message) {
  node.arguments.push(builders.literal(message));
}

function replaceAssertionMessage(node, message) {
  node.arguments.pop();
  addAssertionMessage(node, message);
}

function processAssertion(node, options) {
  var message = buildMessage(node, options);

  if (needsMessage(node)) {
    addAssertionMessage(node, message);
  } else if (options && options.completeExistingMessages) {
    replaceAssertionMessage(node, message);
  }
}

function location(node, filePath) {
  return filePath + ":" + node.loc.start.line + ":" + node.loc.start.column;
}

function isTestCall(node) {
  return types.Identifier.check(node.callee) &&
    node.callee.name === 'test';
}

function assertName(node) {
  var func = node.arguments[1];

  if (types.FunctionExpression.check(func) && func.params.length > 0) {
    return func.params[0].name;
  }
}

module.exports = function transform(source, options) {
  try {
    var ast = recast.parse(source);
    var assert = 'assert';

    recast.visit(ast, {
      visitCallExpression: function(path){
        var node = path.node;

        if (isTestCall(node)) {
          assert = assertName(node);
        } else if (isSupportedAssertion(node, assert)) {
          processAssertion(node, options);
        }

        this.traverse(path);
      }
    });

    return recast.print(ast, {quote: 'single'}).code;
  } catch (err) {
    return source;
  }
};