Skip to content

Commit

Permalink
Major refactor
Browse files Browse the repository at this point in the history
pretendr objectsand template objects look the same, so I've made them the
same thing, and generating from a template is actually just cloning. This
has broken templates having their attributes copied deeply, and magic
primitive stuff, so that needs fixing
  • Loading branch information
nathanmacinnes committed Aug 3, 2012
1 parent 5e3ba3f commit b7ff337
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 226 deletions.
326 changes: 107 additions & 219 deletions lib/pretendr.js
Expand Up @@ -2,253 +2,141 @@


"use strict"; "use strict";


var enumerate, // fn - whether to enumerate the properties and recurse var nonPrimitive, // fn - whether to enumerate the properties and recurse
makeTemplate, // fn - get a template object makeTemplate, // fn - get a template object
makeBaseObj,
makeMock,
pretendr, // fn - called by module.exports pretendr, // fn - called by module.exports
templateIns = [], // list of template inputs pretendrFunction,
templateOuts = []; // template outputs they match to prevent infinit loops templateIns = [], // list of template inputs
templateOuts = []; // template outputs they match to prevent infinit loops


// Return whether the object passed in is of type 'object' or 'function' // Return whether the object passed in is of type 'object' or 'function'
// This is important, because string types cause infinite recursion and other // This is important, because string types cause infinite recursion and other
// types don't make much sense // types don't make much sense
enumerate = function (o) { nonPrimitive = function (o) {
return typeof o === 'object' || typeof o === 'function'; return typeof o === 'object' || typeof o === 'function';
}; };


// Return a template object based on the given template descriptor, so that makeBaseObj = function (descriptor, mock) {
// pretendr objects can be created with returnValue et al already set var ins = [],
makeTemplate = function (t) { outs = [],
var fake, subMakeBaseObj;
i,
index, subMakeBaseObj = function (descriptor, mock) {
ins, var attributes = {},
outs, baseObj = {},
ownTemplate, makeCopy,
returnValue, i,
setFake, indexNum,
setReturnValue, setFake,
setTemplate, setReturnValue,
subTemplate, setTemplate;
templateObj;
for (i = 0; i < templateIns.length; i++) { for (i = 0; i < ins.length; i++) {
if (templateIns[i] === t) { if (descriptor === ins[i]) {
return templateOuts[i]; return outs[i];
}
}

templateIns.push(t);
index = templateIns.length - 1;

setReturnValue = function (val) {
returnValue = val;
};
setFake = function (fn) {
fake = fn;
};
setTemplate = function (tem) {
ownTemplate = tem;
};
templateObj = {
generate : function (o) {
var i,
p;
p = this.apply(pretendr(t, o));
return p;
},
apply : function (p) {
var i;
if (returnValue !== undefined) {
p.returnValue(returnValue);
} }
if (fake !== undefined) { }
p.fake(fake); indexNum = ins.length;
ins.push(descriptor);
outs[indexNum] = baseObj;

setFake = function (fake) {
attributes.fake = fake;
};
setReturnValue = function (value) {
attributes.returnValue = value;
};
setTemplate = function (desc) {
attributes.template = makeBaseObj(desc);
return attributes.template;
};
makeCopy = function (obj) {
var copy = makeBaseObj(descriptor, obj);
if (attributes.hasOwnProperty('fake')) {
copy.fake(attributes.fake);
} }
if (ownTemplate !== undefined) { if (attributes.hasOwnProperty('returnValue')) {
p.template(ownTemplate); copy.returnValue(attributes.returnValue);
} }
for (i in t) { if (attributes.hasOwnProperty('template')) {
if (t.hasOwnProperty(i) && templateObj[i]) { copy.template(attributes.template.mock);
templateObj[i].apply(p[i]); }
return copy;
};
if (typeof descriptor === 'function') {
baseObj.fake = setFake;
baseObj.returnValue = setReturnValue;
baseObj.template = setTemplate;
}
baseObj.clone = makeCopy;
baseObj.mock = mock || makeMock(descriptor, baseObj, attributes);
if (nonPrimitive(descriptor)) {
for (i in descriptor) {
if (descriptor.hasOwnProperty(i)) {
baseObj[i] = subMakeBaseObj(descriptor[i]);
baseObj.mock[i] = baseObj[i].mock;
} }
} }
return p;
} }
return baseObj;
}; };
if (typeof t === 'function') { return subMakeBaseObj(descriptor, mock);
templateObj.returnValue = setReturnValue; };
templateObj.fake = setFake;
templateObj.template = setTemplate; makeMock = function (desc, base, attr) {
if (desc instanceof Array) {
return [];
} }
for (i in t) { if (typeof desc === 'object') {
if (t.hasOwnProperty(i) && enumerate(t[i])) { return {};
templateObj[i] = makeTemplate(t[i]); }
} if (typeof desc === 'function') {
return pretendrFunction(attr, base);
} }
templateOuts[index] = templateObj; return desc;
return templateObj;
}; };


// Create a pretendr object. If the optional second argument is provided, that pretendrFunction = function (attributes, baseObject) {
// object will be used as the mock object. This is to aid with instances of var fn;
// mock constructor functions. baseObject.calls = [];
pretendr = function (o, f) { baseObject.instances = [];
var ins = [], // objects which have been passed in to the pretendr function fn = function () {
outs = [], // objects which have been returned by pretendr var call = {},
pretendr, // inner fn so that ins and outs aren't recreated by recursion instance,
pretendrFunction, // fn for creating mock functions i;
pretendrPrimitive; // fn for creating mock primitives baseObject.calls.push(call);

call.args = arguments;
// an inner function is used so that ins and outs are created once per call.context = this;
// external call, then resued to prevent infinite recursion
pretendr = function (input, forced) { for (i = 0; i < arguments.length; i++) {
var i, if (typeof arguments[i] === 'function') {
indexNumber, call.callback = arguments[i];
mock,
output;

// loop through all objects which have been passed in before. If this
// matches, return the corresponding out[], so that we don't get
// infinite recursion
for (i = 0; i < ins.length; i++) {
if (input === ins[i]) {
return outs[i];
} }
} }
// otherwise, put in the input and store the number if (attributes.hasOwnProperty('returnValue')) {
ins.push(input); call.returned = attributes.returnValue;
indexNumber = ins.length - 1;

// We want to recurse through objects and functions, so do them
// separately
if (enumerate(input)) {
if (typeof input === 'function') {
output = pretendrFunction();
} else {

// if an object has been passed that should be used as a basis
// for the mock. Used when creating instances of templates.
if (forced) {
mock = forced;

// if it's an array, we want the mock to be an array too
} else if (input instanceof Array) {
mock = [];
} else {
mock = {};
}

output = {
mock : mock
};
}

// Store the output before we recurse, so that it can be returned
// if we come across the same input again
outs[indexNumber] = output;

// Loop through and recurse if necessary
for (i in input) {
if (input.hasOwnProperty(i)) {
if (enumerate(input[i])) {
output[i] = pretendr(input[i]);
output.mock[i] = output[i].mock;
} else {

// primitive objects must be processed separately so
// that they can have getter and setter functions
output[i] = pretendrPrimitive(input[i], output.mock, i);
}
}
}
return output;
} }
return pretendrPrimitive(input); if (attributes.hasOwnProperty('fake')) {
}; call.returned = attributes.fake.apply(this, arguments);

}
// Base objects of mocked functions must have several methods, and the mocks if (this instanceof fn) {
// themselves must of course be functions, so do these in their own function if (!attributes.hasOwnProperty('template')) {
pretendrFunction = function () { baseObject.template();
var fake,
pretendrObj,
returnValue,
template;
pretendrObj = {
calls : [],
instances : [],
returnValue : function (r) {
returnValue = r;
},
fake : function (fn) {
fake = fn;
},
template : function (t) {
template = makeTemplate(t);
return template;
},
mock : function () {
var call,
i;
call = {
args : arguments,
context : this
};
for (i = 0; i < arguments.length; i++) {
if (typeof arguments[i] === 'function') {
call.callback = arguments[i];
}
}
pretendrObj.calls.push(call);
if (this instanceof pretendrObj.mock) {
if (!template) {
template = pretendrObj.template();
}
pretendrObj.instances.push(template.generate(this));
} else if (fake) {
call.returned = fake.apply(this, arguments);
} else if (template) {
call.pretendr = template.generate();
call.returned = call.pretendr.mock;
} else {
call.returned = returnValue;
}
return call.returned;
} }
}; instance = attributes.template.clone(this);
return pretendrObj; baseObject.instances.push(instance);
}; } else if (attributes.hasOwnProperty('template')) {

call.pretendr = attributes.template.clone();
// Primitives must have getters and setters so that we can record what call.returned = call.pretendr.mock;
// happens to them, so these are processed in their own functions too.
pretendrPrimitive = function (val, parent, property) {
var get,
pretendrObj,
set;
pretendrObj = parent || {};
get = function () {
this.gets = this.gets + 1;
return val;
};
set = function (v) {
val = v;
this.values.push(v);
};
if (Object.defineProperty) {
pretendrObj.gets = 0;
pretendrObj.values = [];
Object.defineProperty(pretendrObj, property || 'mock', {
get : get,
set : set
});
} else {
pretendrObj.mock = val;
} }
return pretendrObj; return call.returned;
}; };

return fn;
return pretendr(o, f);
}; };


// hide the extra arg of pretendr from the public // hide the extra arg of pretendr from the public
module.exports = function (o) { module.exports = function (o) {
return pretendr(o); return makeBaseObj(o);
}; };

0 comments on commit b7ff337

Please sign in to comment.