getJasmineRequireObj().Env = function(j$) { function Env(options) { options = options || {}; var self = this; var global = options.global || j$.getGlobal(); var totalSpecsDefined = 0; var catchExceptions = true; var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global)); var runnableLookupTable = {}; var spies = []; var currentSpec = null; var currentSuite = null; var reporter = new j$.ReportDispatcher([ 'jasmineStarted', 'jasmineDone', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); this.specFilter = function() { return true; }; var equalityTesters = []; var customEqualityTesters = []; this.addCustomEqualityTester = function(tester) { customEqualityTesters.push(tester); }; j$.Expectation.addCoreMatchers(j$.matchers); var nextSpecId = 0; var getNextSpecId = function() { return 'spec' + nextSpecId++; }; var nextSuiteId = 0; var getNextSuiteId = function() { return 'suite' + nextSuiteId++; }; var expectationFactory = function(actual, spec) { return j$.Expectation.Factory({ util: j$.matchersUtil, customEqualityTesters: customEqualityTesters, actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { return spec.addExpectationResult(passed, result); } }; var specStarted = function(spec) { currentSpec = spec; reporter.specStarted(spec.result); }; var beforeFns = function(suite) { return function() { var befores = []; while(suite) { befores = befores.concat(suite.beforeFns); suite = suite.parentSuite; } return befores.reverse(); }; }; var afterFns = function(suite) { return function() { var afters = []; while(suite) { afters = afters.concat(suite.afterFns); suite = suite.parentSuite; } return afters; }; }; var getSpecName = function(spec, suite) { return suite.getFullName() + ' ' + spec.description; }; // TODO: we may just be able to pass in the fn instead of wrapping here var buildExpectationResult = j$.buildExpectationResult, exceptionFormatter = new j$.ExceptionFormatter(), expectationResultFactory = function(attrs) { attrs.messageFormatter = exceptionFormatter.message; attrs.stackFormatter = exceptionFormatter.stack; return buildExpectationResult(attrs); }; // TODO: fix this naming, and here's where the value comes in this.catchExceptions = function(value) { catchExceptions = !!value; return catchExceptions; }; this.catchingExceptions = function() { return catchExceptions; }; var maximumSpecCallbackDepth = 20; var currentSpecCallbackDepth = 0; function clearStack(fn) { currentSpecCallbackDepth++; if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) { currentSpecCallbackDepth = 0; realSetTimeout(fn, 0); } else { fn(); } } var catchException = function(e) { return j$.Spec.isPendingSpecException(e) || catchExceptions; }; var queueRunnerFactory = function(options) { options.catchException = catchException; options.clearStack = options.clearStack || clearStack; options.timer = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; new j$.QueueRunner(options).execute(); }; var topSuite = new j$.Suite({ env: this, id: getNextSuiteId(), description: 'Jasmine__TopLevel__Suite', queueRunner: queueRunnerFactory, resultCallback: function() {} // TODO - hook this up }); runnableLookupTable[topSuite.id] = topSuite; currentSuite = topSuite; this.topSuite = function() { return topSuite; }; this.execute = function(runnablesToRun) { runnablesToRun = runnablesToRun || [topSuite.id]; var allFns = []; for(var i = 0; i < runnablesToRun.length; i++) { var runnable = runnableLookupTable[runnablesToRun[i]]; allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable)); } reporter.jasmineStarted({ totalSpecsDefined: totalSpecsDefined }); queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone}); }; this.addReporter = function(reporterToAdd) { reporter.addReporter(reporterToAdd); }; this.addMatchers = function(matchersToAdd) { j$.Expectation.addMatchers(matchersToAdd); }; this.spyOn = function(obj, methodName) { if (j$.util.isUndefined(obj)) { throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()'); } if (j$.util.isUndefined(obj[methodName])) { throw new Error(methodName + '() method does not exist'); } if (obj[methodName] && j$.isSpy(obj[methodName])) { //TODO?: should this return the current spy? Downside: may cause user confusion about spy state throw new Error(methodName + ' has already been spied upon'); } var spy = j$.createSpy(methodName, obj[methodName]); spies.push({ spy: spy, baseObj: obj, methodName: methodName, originalValue: obj[methodName] }); obj[methodName] = spy; return spy; }; var suiteFactory = function(description) { var suite = new j$.Suite({ env: self, id: getNextSuiteId(), description: description, parentSuite: currentSuite, queueRunner: queueRunnerFactory, onStart: suiteStarted, resultCallback: function(attrs) { reporter.suiteDone(attrs); } }); runnableLookupTable[suite.id] = suite; return suite; }; this.describe = function(description, specDefinitions) { var suite = suiteFactory(description); var parentSuite = currentSuite; parentSuite.addChild(suite); currentSuite = suite; var declarationError = null; try { specDefinitions.call(suite); } catch (e) { declarationError = e; } if (declarationError) { this.it('encountered a declaration exception', function() { throw declarationError; }); } currentSuite = parentSuite; return suite; }; this.xdescribe = function(description, specDefinitions) { var suite = this.describe(description, specDefinitions); suite.disable(); return suite; }; var specFactory = function(description, fn, suite) { totalSpecsDefined++; var spec = new j$.Spec({ id: getNextSpecId(), beforeFns: beforeFns(suite), afterFns: afterFns(suite), expectationFactory: expectationFactory, exceptionFormatter: exceptionFormatter, resultCallback: specResultCallback, getSpecName: function(spec) { return getSpecName(spec, suite); }, onStart: specStarted, description: description, expectationResultFactory: expectationResultFactory, queueRunnerFactory: queueRunnerFactory, fn: fn }); runnableLookupTable[spec.id] = spec; if (!self.specFilter(spec)) { spec.disable(); } return spec; function removeAllSpies() { for (var i = 0; i < spies.length; i++) { var spyEntry = spies[i]; spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; } spies = []; } function specResultCallback(result) { removeAllSpies(); j$.Expectation.resetMatchers(); customEqualityTesters = []; currentSpec = null; reporter.specDone(result); } }; var suiteStarted = function(suite) { reporter.suiteStarted(suite.result); }; this.it = function(description, fn) { var spec = specFactory(description, fn, currentSuite); currentSuite.addChild(spec); return spec; }; this.xit = function(description, fn) { var spec = this.it(description, fn); spec.pend(); return spec; }; this.expect = function(actual) { if (!currentSpec) { throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out'); } return currentSpec.expect(actual); }; this.beforeEach = function(beforeEachFunction) { currentSuite.beforeEach(beforeEachFunction); }; this.afterEach = function(afterEachFunction) { currentSuite.afterEach(afterEachFunction); }; this.pending = function() { throw j$.Spec.pendingSpecExceptionMessage; }; } return Env; };