Skip to content

Instantly share code, notes, and snippets.

@gnerkus
Created September 22, 2015 07:13
Show Gist options
  • Save gnerkus/fe1d7fc34c5abe694cdd to your computer and use it in GitHub Desktop.
Save gnerkus/fe1d7fc34c5abe694cdd to your computer and use it in GitHub Desktop.
Verify object instance has instance attributes defined using Esprima
// This file is to executed in a Nodejs environment.
// `node constructor_check.js`
'use strict';
// You'll need to install the 'esprima', 'estraverse' and 'lodash' modules via npm.
var esprima = require('esprima');
var estraverse = require('estraverse');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
// Define a 'solution.js' file in the same directory as this file.
var filename = path.resolve() + "/solution.js";
console.log('Processing', filename);
var ast = esprima.parse(fs.readFileSync(filename));
// This is the list of parameters that must be defined in the constructor function.
var parameterList = ['firstName', 'lastName', 'age', 'profession', 'company'];
// Test the constructor defined in 'solution.js'
verifyConstructor(ast, 'Person', parameterList);
// Verify the variable defined is a Constructor.
function verifyConstructor(ast, variableName, parameterList) {
// Maintain a count of all the 'Person' functions in the source file.
var functionCount = 0;
// Look for functions with the identifier 'Person' while traversing the AST.
estraverse.traverse(ast, {
enter: function (node) {
if (isFunctionExpression(node) && isCorrectVariable(node, variableName)) {
functionCount += 1;
console.log(areParametersAssigned(node.init.body, parameterList));
}
}
});
if (functionCount === 0) {
return 'The `Person` constructor has not been defined yet.';
}
}
// Verify the node represents a function.
function isFunctionExpression (node) {
return node.type === 'VariableDeclarator' && node.init.type === 'FunctionExpression';
}
// Verify the node has the correct name.
function isCorrectVariable (node, variableName) {
return node.id.name === variableName;
}
// Verify a function has all the correct parameters defined.
function hasParameters (node, parameterList) {
var missingParameters = _.difference(_.pluck(node.init.params, 'name'), parameterList);
return _.eq(_.pluck(node.init.params, 'name'), parameterList) || 'You have not defined the parameters ' + missingParameters.join(' ') + 'for the Person constructor.';
}
// Verify all parameters in the functioned are assigned to a property.
function areParametersAssigned (functionNode, parameterList) {
var memberValues = [];
var refToThis = 0;
_.forEach(functionNode.body, function (expression) {
memberValues.push(expression.expression.right.name);
if (expression.expression.left.object.type === 'ThisExpression') {
refToThis += 1;
}
});
return _.eq(memberValues, parameterList) && refToThis === parameterList.length;
}
function getExpressionStatements (functionNode) {
return functionNode.init.body.body[1].expression.right;
}
// 'use strict';
var Person = function (firstName, lastName, age, profession, company) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.profession = profession;
this.company = company;
};
Person.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName + '.';
};
Person.prototype.getOccupation = function () {
return this.firstName + ' ' + this.lastName + ' is a ' + this.profession + ' at ' + this.company + '.';
};
Person.prototype.setOccupation = function (newOccupation) {
this.occupation = newOccupation;
};
Person.prototype.toString = function () {
return this.firstName + ' ' + this.lastName + ' is ' + this.age + ' years old and works as a ' + this.profession + ' at ' + this.company + '.';
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment