/** * * JACK :: JavaScript Mocking. * Version: $Id$ * */ function jack() {} // This needs to be here to make error reporting work correctly in IE. (function (){ // START HIDING FROM GLOBAL SCOPE /** EXPORT JACK **/ window.jack = new Jack(); window.jack.matchers = new Matchers(); window.jack.util = new Util(); window.jack.FunctionSpecification = FunctionSpecification; window.jack.FunctionGrab = FunctionGrab; return; /** * Constructor for object that will be exposed as the global jack */ function Jack() { var functionGrabs = {}; var objectGrabs = {}; var environment = new Environment(); var reportMessages = []; var currentExpectation = null; var publicApi = createPublicApi(); return publicApi; function createPublicApi() { var api = jackFunction; api.grab = grab; api.create = create; api.inspect = inspect; api.expect = expect; api.verify = verify; api.report = report; api.reportAll = reportAll; api.env = environment; return api; } function jackFunction(delegate) { before(); firstPass(delegate); // secondPass(delegate); after(); } function before() { functionGrabs = {}; objectGrabs = {}; environment.reset(); } function firstPass(delegate) { delegate(); } function secondPass(delegate) { var oldExpect = publicApi.expect; publicApi.expect = function(name) { var fakeEx = {}; var grab = findGrab(name); if(grab._beenThroughSecondPass) { var ex = grab.expect(); for(prop in ex) { if(typeof ex[prop] == "function") { fakeEx[prop] = function() { return fakeEx; } } } } grab._beenThroughSecondPass = true; return fakeEx; }; var findMore = true; for(var i=0; findMore && i<10; i++) { try { delegate(); findMore = false; } catch(exception) { var line = -1; if(exception.lineNumber != null) { line = exception.lineNumber; } else if(exception['opera#sourceloc'] != null) { line = exception['opera#sourceloc']; } currentExpectation._lineNumber = line; } } publicApi.expect = oldExpect; } function after() { var reports = getTextReports(); resetGrabs(); if(reports.length > 0) { environment.report(reports[0]); } } function getTextReports() { var failedReports = []; for(var name in functionGrabs) { var reports = functionGrabs[name].reportAll(name); for(var i=0; i 1) { functionName = nameParts.pop(); if(parentObject == window) { var parentName = nameParts.join("."); eval("parentObject = " + parentName); } } functionGrabs[fullName] = new FunctionGrab(functionName, grabbed, parentObject); return functionGrabs[fullName]; } function grabObject(name, grabbed) { objectGrabs[name] = new ObjectGrab(name, grabbed); return objectGrabs[name]; } function create(objectName, functionNames) { var mockObject = {}; for(var i=0; i 1) { value = arguments[1]; prefix = arguments[0]; } if(value == undefined) { return displayValueNullOrUndefined(value); } var display = displayValueDefault(value); if('string' == typeof value) { display = displayValueString(value); } else if(value.constructor == Array) { display = displayValueArray(value); } else if(value.constructor == RegExp) { display = displayValueRegExp(value); } else if('object' == typeof value) { display = displayValueObject(value); } return prefix + display; } function displayValueDefault(value) { return value.toString(); } function displayValueString(value) { return "'" + value + "'"; } function displayValueArray(value) { var displayValues = []; for(var i=0; i= timing.expected; } else if(timing.modifier == -1) { return times <= timing.expected; } } function satisfies(other) { return other.test.apply(this, argumentValues); } function invoke() { timing.actual++; if(mockImplementation != null) { return mockImplementation.apply(this, arguments); } } function mock(implementation) { mockImplementation = implementation; return api; } function stub() { mockImplementation = function() {}; return api; } function returnValue(value) { mockImplementation = function() { return value; } } function hasMockImplementation() { return mockImplementation != null; } function invocations() { return { actual: timing.actual, expected: timing.expected }; } function getArgumentValues() { return argumentValues; } function describe(name) { return name +"(" + describeConstraints() + ") " + describeTimes(); } function describeConstraints() { if(constraints == null) { return ""; } var descriptions = []; for(var i=0; i 0) { description = "expected at least " + description; } else if(timing.modifier < 0) { description = "expected at most " + description; } description += ", called " + timing.actual + " time"; if(timing.actual != 1) { description += "s"; } return description; } } /** * */ function Matchers() { return { 'is': function(a, b) { return result(a==b, a, '', b); }, 'isNot': function(a, b) { return result(a!=b, a, 'not:', b); }, 'isType': function(a, b) { return result(b == typeof a, a, 'type:', b); }, 'matches': function(a, b) { return result(b.test(a), a, 'matching:', b) }, 'hasProperty': function(a, b, c) { var match = c ? a[b]==c : a[b]!=undefined; var bDisplay = b; if(c != null) { bDisplay = {}; bDisplay[b] = c; } return result(match, a, 'property:', bDisplay) }, 'hasProperties': function(a, b) { var match = true; for(var p in b) { if(a[p] != b[p]) { match = false; } } return result(match, a, 'properties:', b); }, 'isGreaterThan': function(a, b) { return result(b < a, a, '>', b); }, 'isLessThan': function(a, b) { return result(b > a, a, '<', b); }, 'isOneOf': function() { var a = arguments[0]; var b = []; for(var i=1; i