diff --git a/GOALS_2.0.md b/GOALS_2.0.md new file mode 100644 index 00000000..e6e9a45a --- /dev/null +++ b/GOALS_2.0.md @@ -0,0 +1,46 @@ +# (Vague) Jasmine 2.0 Goals/(Guidelines) + +1. No globals! + * jasmine library is entirely inside `jasmine` namespace + * globals required for backwards compatibility should be added in `boot.js` (EG, var describe = jasmine.getCurrentEnv().describe lives in boot.js) +1. Don't use properties as getters. Use methods. + * Properties aren't encapsulated -- can be mutated, unsafe. +1. Reporters get data objects (no methods). + * easier to refactor as needed +1. More unit tests - fewer nasty integration tests + +## Remaining non-story-able work: +* Make a `TODO` list + +### Hard +* Finish killing Globals + * Guidelines: + * New objects can have constructors on `jasmine` + * Top level functions can live on `jasmine` + * Top level (i.e., any `jasmine` property) should only be referenced inside the `Env` constructor + * Spies + * isA functions: + * isArray_ - used in matchers and spies + * isString_ + * isDOMNode_ + * isA_ + * unimplementedMethod_, used by PrettyPrinter + * jasmine.util should be util closure inside of env or something + * argsToArray is used for Spies and matching + * inherit is for how matchers are added/mixed in, reporters, and pretty printers + * formatException is used only inside Env/spec + * htmlEscape is for messages in matchers - should this be HTML at all? Is that * Matchers improvements + * move AddMatchers to Env & global (away from spec) + * make matchers unit-testable + * write doc on how to make a matcher + +### Easy + +## Other Topics + +* Build - can we, should we redo the build and release process AGAIN in order to make it less arcane + * Want to add JSHint to build + * Use a standard JS/Node based concat system instead of custom Ruby? +* Docs + * JsDoc is a pain to host and RubyMine is pretty good at navigating. I say we kill it officially + * Docco has gone over well. Should we annotate all the sources and then have Pages be more complex, having tutorials and annotated source like Backbone? Are we small enough? diff --git a/Gemfile b/Gemfile index 5d929af7..845c0c50 100644 --- a/Gemfile +++ b/Gemfile @@ -1,11 +1,11 @@ source :rubygems gem "rake" -gem "jasmine", git: 'https://github.com/pivotal/jasmine-gem.git' +gem "jasmine", :git => 'https://github.com/pivotal/jasmine-gem.git', :branch => '2_0' unless ENV["TRAVIS"] group :debug do gem 'debugger' end end - + gemspec diff --git a/Rakefile b/Rakefile index 18530c8a..c2f7160d 100644 --- a/Rakefile +++ b/Rakefile @@ -55,9 +55,10 @@ end namespace :jasmine do task :server do port = ENV['JASMINE_PORT'] || 8888 - Jasmine.load_configuration_from_yaml(File.join(Dir.pwd, 'spec', 'jasmine.yml')) + jasmine_yml = ENV['JASMINE_YML'] || 'jasmine.yml' + Jasmine.load_configuration_from_yaml(File.join(Dir.pwd, 'spec', jasmine_yml)) config = Jasmine.config - server = Jasmine::Server.new(8888, Jasmine::Application.app(config)) + server = Jasmine::Server.new(port, Jasmine::Application.app(config)) server.start puts "your tests are here:" diff --git a/lib/jasmine-core.rb b/lib/jasmine-core.rb index ecf968a6..3bf880c3 100644 --- a/lib/jasmine-core.rb +++ b/lib/jasmine-core.rb @@ -23,6 +23,14 @@ module Jasmine spec_files("node") end + def boot_files + ["boot.js"] + end + + def boot_dir + File.join(path, 'boot') + end + def spec_files(type) raise ArgumentError.new("Unrecognized spec type") unless SPEC_TYPES.include?(type) (Dir.glob(File.join(path, "spec", type, "*.js"))).map { |f| File.join("spec", type, File.basename(f)) }.uniq diff --git a/lib/jasmine-core/boot/boot.js b/lib/jasmine-core/boot/boot.js new file mode 100644 index 00000000..bd649473 --- /dev/null +++ b/lib/jasmine-core/boot/boot.js @@ -0,0 +1,92 @@ +(function() { + var env = jasmine.getEnv(); + + var jasmineInterface = { + describe: function(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + xdescribe: function(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + it: function(desc, func) { + return env.it(desc, func); + }, + + xit: function(desc, func) { + return env.xit(desc, func); + }, + + beforeEach: function(beforeEachFunction) { + return env.beforeEach(beforeEachFunction); + }, + + afterEach: function(afterEachFunction) { + return env.afterEach(afterEachFunction); + }, + + expect: function(actual) { + return env.expect(actual); + }, + + addMatchers: function(matchers) { + return env.addMatchers(matchers); + }, + + spyOn: function(obj, methodName) { + return env.spyOn(obj, methodName); + }, + + clock: env.clock, + setTimeout: env.clock.setTimeout, + clearTimeout: env.clock.clearTimeout, + setInterval: env.clock.setInterval, + clearInterval: env.clock.clearInterval, + + jsApiReporter: new jasmine.JsApiReporter(jasmine) + }; + + if (typeof window == "undefined" && typeof exports == "object") { + extend(exports, jasmineInterface); + } else { + extend(window, jasmineInterface); + } + + var queryString = new jasmine.QueryString({ + getWindowLocation: function() { return window.location; } + }); + + env.catchExceptions(queryString.getParam("catch")); + + var htmlReporter = new jasmine.HtmlReporter({ + env: env, + queryString: queryString, + getContainer: function() { return document.body; }, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + env.addReporter(jasmineInterface.jsApiReporter); + env.addReporter(htmlReporter); + + env.specFilter = function(spec) { + return htmlReporter.specFilter(spec); + }; + + var currentWindowOnload = window.onload; + + window.onload = function() { + if (currentWindowOnload) { + currentWindowOnload(); + } + htmlReporter.initialize(); + env.execute(); + }; + + function extend(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; + } + +}()); diff --git a/lib/jasmine-core/example/SpecRunner.html b/lib/jasmine-core/example/SpecRunner.html deleted file mode 100644 index ea58503d..00000000 --- a/lib/jasmine-core/example/SpecRunner.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - Jasmine Spec Runner - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 543d5696..17b91020 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -1,681 +1,319 @@ -jasmine.HtmlReporterHelpers = {}; +jasmine.HtmlReporter = function(options) { + var env = options.env || {}, + getContainer = options.getContainer, + now = options.now || function() { return new Date().getTime();}, + createElement = options.createElement, + createTextNode = options.createTextNode, + results = [], + queryString = options.queryString, + startTime, + specsExecuted = 0, + failureCount = 0, + htmlReporterMain, + symbols; -jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); + this.initialize = function() { + htmlReporterMain = createDom("div", {className: "html-reporter"}, + createDom("div", {className: "banner"}, + createDom("span", {className: "title"}, "Jasmine"), + createDom("span", {className: "version"}, env.versionString()) + ), + createDom("ul", {className: "symbol-summary"}), + createDom("div", {className: "alert"}), + createDom("div", {className: "results"}, + createDom("div", {className: "failures"}) + ) + ); + getContainer().appendChild(htmlReporterMain); - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { - el.appendChild(child); - } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { - var results = child.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - - return status; -}; - -jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { - var parentDiv = this.dom.summary; - var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; - var parent = child[parentSuite]; - - if (parent) { - if (typeof this.views.suites[parent.id] == 'undefined') { - this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); - } - parentDiv = this.views.suites[parent.id].element; - } - - parentDiv.appendChild(childElement); -}; - - -jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { - for(var fn in jasmine.HtmlReporterHelpers) { - ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; - } -}; - -jasmine.HtmlReporter = function(_doc) { - var self = this; - var doc = _doc || window.document; - - var reporterView; - - var dom = {}; - - // Jasmine Reporter Public Interface - self.logRunningSpecs = false; - - self.reportRunnerStarting = function(runner) { - var specs = runner.specs() || []; - - if (specs.length == 0) { - return; - } - - createReporterDom(runner.env.versionString()); - doc.body.appendChild(dom.reporter); - setExceptionHandling(); - - reporterView = new jasmine.HtmlReporter.ReporterView(dom); - reporterView.addSpecs(specs, self.specFilter); + symbols = find(".symbol-summary")[0]; }; - self.reportRunnerResults = function(runner) { - reporterView && reporterView.complete(); - }; + var specFilterPattern; - self.reportSuiteResults = function(suite) { - reporterView.suiteComplete(suite); - }; - - self.reportSpecStarting = function(spec) { - if (self.logRunningSpecs) { - self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } - }; - - self.reportSpecResults = function(spec) { - reporterView.specComplete(spec); - }; - - self.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } - }; - - self.specFilter = function(spec) { - if (!focusedSpecName()) { + this.specFilter = function(spec) { + if (!isFiltered()) { return true; } - return spec.getFullName().indexOf(focusedSpecName()) === 0; + var specName = spec.getFullName(); + + return !!(specName.match(specFilterPattern)); }; - return self; - - function focusedSpecName() { - var specName; - - (function memoizeFocusedSpec() { - if (specName) { - return; - } - - var paramMap = []; - var params = jasmine.HtmlReporter.parameters(doc); - - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - specName = paramMap.spec; - })(); - - return specName; - } - - function createReporterDom(version) { - dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, - dom.banner = self.createDom('div', { className: 'banner' }, - self.createDom('span', { className: 'title' }, "Jasmine "), - self.createDom('span', { className: 'version' }, version)), - - dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), - dom.alert = self.createDom('div', {className: 'alert'}, - self.createDom('span', { className: 'exceptions' }, - self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'), - self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))), - dom.results = self.createDom('div', {className: 'results'}, - dom.summary = self.createDom('div', { className: 'summary' }), - dom.details = self.createDom('div', { id: 'details' })) - ); - } - - function noTryCatch() { - return window.location.search.match(/catch=false/); - } - - function searchWithCatch() { - var params = jasmine.HtmlReporter.parameters(window.document); - var removed = false; - var i = 0; - - while (!removed && i < params.length) { - if (params[i].match(/catch=/)) { - params.splice(i, 1); - removed = true; - } - i++; - } - if (jasmine.CATCH_EXCEPTIONS) { - params.push("catch=false"); - } - - return params.join("&"); - } - - function setExceptionHandling() { - var chxCatch = document.getElementById('no_try_catch'); - - if (noTryCatch()) { - chxCatch.setAttribute('checked', true); - jasmine.CATCH_EXCEPTIONS = false; - } - chxCatch.onclick = function() { - window.location.search = searchWithCatch(); - }; - } -}; -jasmine.HtmlReporter.parameters = function(doc) { - var paramStr = doc.location.search.substring(1); - var params = []; - - if (paramStr.length > 0) { - params = paramStr.split('&'); - } - return params; -} -jasmine.HtmlReporter.sectionLink = function(sectionName) { - var link = '?'; - var params = []; - - if (sectionName) { - params.push('spec=' + encodeURIComponent(sectionName)); - } - if (!jasmine.CATCH_EXCEPTIONS) { - params.push("catch=false"); - } - if (params.length > 0) { - link += params.join("&"); - } - - return link; -}; -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); -jasmine.HtmlReporter.ReporterView = function(dom) { - this.startedAt = new Date(); - this.runningSpecCount = 0; - this.completeSpecCount = 0; - this.passedCount = 0; - this.failedCount = 0; - this.skippedCount = 0; - - this.createResultsMenu = function() { - this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, - this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), - ' | ', - this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); - - this.summaryMenuItem.onclick = function() { - dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); - }; - - this.detailsMenuItem.onclick = function() { - showDetails(); - }; + var totalSpecsDefined; + this.jasmineStarted = function(options) { + totalSpecsDefined = options.totalSpecsDefined || 0; + startTime = now(); }; - this.addSpecs = function(specs, specFilter) { - this.totalSpecCount = specs.length; + var summary = createDom("div", {className: "summary"}); - this.views = { - specs: {}, - suites: {} - }; + var topResults = new jasmine.ResultsNode({}, "", null), + currentParent = topResults; - for (var i = 0; i < specs.length; i++) { - var spec = specs[i]; - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); - if (specFilter(spec)) { - this.runningSpecCount++; - } - } + this.suiteStarted = function(result) { + currentParent.addChild(result, "suite"); + currentParent = currentParent.last(); }; - this.specComplete = function(spec) { - this.completeSpecCount++; - - if (isUndefined(this.views.specs[spec.id])) { - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); - } - - var specView = this.views.specs[spec.id]; - - switch (specView.status()) { - case 'passed': - this.passedCount++; - break; - - case 'failed': - this.failedCount++; - break; - - case 'skipped': - this.skippedCount++; - break; - } - - specView.refresh(); - this.refresh(); - }; - - this.suiteComplete = function(suite) { - var suiteView = this.views.suites[suite.id]; - if (isUndefined(suiteView)) { + this.suiteDone = function(result) { + if (currentParent == topResults) { return; } - suiteView.refresh(); + + currentParent = currentParent.parent; }; - this.refresh = function() { - - if (isUndefined(this.resultsMenu)) { - this.createResultsMenu(); - } - - // currently running UI - if (isUndefined(this.runningAlert)) { - this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" }); - dom.alert.appendChild(this.runningAlert); - } - this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); - - // skipped specs UI - if (isUndefined(this.skippedAlert)) { - this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" }); - } - - this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.skippedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.skippedAlert); - } - - // passing specs UI - if (isUndefined(this.passedAlert)) { - this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" }); - } - this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); - - // failing specs UI - if (isUndefined(this.failedAlert)) { - this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); - } - this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); - - if (this.failedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.failedAlert); - dom.alert.appendChild(this.resultsMenu); - } - - // summary info - this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); - this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; + this.specStarted = function(result) { + currentParent.addChild(result, "spec"); }; - this.complete = function() { - dom.alert.removeChild(this.runningAlert); - - this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.failedCount === 0) { - dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); - } else { - showDetails(); + var failures = []; + this.specDone = function(result) { + if (result.status != "disabled") { + specsExecuted++; } - dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); + symbols.appendChild(createDom("li", { + className: result.status, + id: "spec_" + result.id} + )); + + if (result.status == "failed") { + failureCount++; + + var failure = + createDom("div", {className: "spec-detail failed"}, + createDom("a", {className: "description", title: result.fullName, href: specHref(result)}, result.fullName), + createDom("div", {className: "messages"}) + ); + var messages = failure.childNodes[1]; + + for (var i = 0; i < result.failedExpectations.length; i++) { + var expectation = result.failedExpectations[i]; + var stack = (expectation.trace && expectation.trace.stack) || ""; + messages.appendChild(createDom("div", {className: "result-message"}, expectation.message)); + messages.appendChild(createDom("div", {className: "stack-trace"}, stack)); + } + + failures.push(failure); + } + }; + + this.jasmineDone = function() { + var elapsed = now() - startTime; + + var banner = find(".banner")[0]; + banner.appendChild(createDom("span", {className: "duration"}, "finished in " + elapsed / 1000 + "s")); + + var alert = find(".alert")[0]; + + alert.appendChild(createDom("span", { className: "exceptions" }, + createDom("label", { className: "label", 'for': "raise-exceptions" }, "raise exceptions"), + createDom("input", { + className: "raise", + id: "raise-exceptions", + type: "checkbox" + }) + )); + var checkbox = find("input")[0]; + + checkbox.checked = !env.catchingExceptions(); + checkbox.onclick = function() { + queryString.setParam("catch", !checkbox.checked); + }; + + if (specsExecuted < totalSpecsDefined) { + var skippedMessage = "Ran " + specsExecuted + " of " + totalSpecsDefined + " specs - run all"; + alert.appendChild( + createDom("span", {className: "bar skipped"}, + createDom("a", {href: "?", title: "Run all specs"}, skippedMessage) + ) + ); + } + var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount), + statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); + alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage)); + + var results = find(".results")[0]; + results.appendChild(summary); + + summaryList(topResults, summary); + + function summaryList(resultsTree, domParent) { + var specListNode; + for (var i = 0; i < resultsTree.children.length; i++) { + var resultNode = resultsTree.children[i]; + if (resultNode.type == "suite") { + var suiteListNode = createDom("ul", {className: "suite", id: "suite-" + resultNode.result.id}, + createDom("li", {className: "suite-detail"}, + createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) + ) + ); + + summaryList(resultNode, suiteListNode); + domParent.appendChild(suiteListNode); + } + if (resultNode.type == "spec") { + if (domParent.getAttribute("class") != "specs") { + specListNode = createDom("ul", {className: "specs"}); + domParent.appendChild(specListNode); + } + specListNode.appendChild( + createDom("li", { + className: resultNode.result.status, + id: "spec-" + resultNode.result.id + }, + createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) + ) + ); + } + } + } + + if (failures.length) { + alert.appendChild( + createDom('span', {className: "menu bar spec-list"}, + createDom("span", {}, "Spec List | "), + createDom('a', {className: "failures-menu", href: "#"}, "Failures"))); + alert.appendChild( + createDom('span', {className: "menu bar failure-list"}, + createDom('a', {className: "spec-list-menu", href: "#"}, "Spec List"), + createDom("span", {}, " | Failures "))); + + find(".failures-menu")[0].onclick = function() { + setMenuModeTo('failure-list'); + }; + find(".spec-list-menu")[0].onclick = function() { + setMenuModeTo('spec-list'); + }; + + setMenuModeTo('failure-list'); + + var failureNode = find(".failures")[0]; + for (var i = 0; i < failures.length; i++) { + failureNode.appendChild(failures[i]); + } + } }; return this; - function showDetails() { - if (dom.reporter.className.search(/showDetails/) === -1) { - dom.reporter.className += " showDetails"; + function find(selector) { + if (selector.match(/^\./)) { + var className = selector.substring(1); + return getContainer().getElementsByClassName(className); + } else { + return getContainer().getElementsByTagName(selector); } } - function isUndefined(obj) { - return typeof obj === 'undefined'; - } + function createDom(type, attrs, childrenVarArgs) { + var el = createElement(type); - function isDefined(obj) { - return !isUndefined(obj); - } + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; - function specPluralizedFor(count) { - var str = count + " spec"; - if (count > 1) { - str += "s" - } - return str; - } - -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); - - -jasmine.HtmlReporter.SpecView = function(spec, dom, views) { - this.spec = spec; - this.dom = dom; - this.views = views; - - this.symbol = this.createDom('li', { className: 'pending' }); - this.dom.symbolSummary.appendChild(this.symbol); - - this.summary = this.createDom('div', { className: 'specSummary' }, - this.createDom('a', { - className: 'description', - href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.description) - ); - - this.detail = this.createDom('div', { className: 'specDetail' }, - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.getFullName()) - ); -}; - -jasmine.HtmlReporter.SpecView.prototype.status = function() { - return this.getSpecStatus(this.spec); -}; - -jasmine.HtmlReporter.SpecView.prototype.refresh = function() { - this.symbol.className = this.status(); - - switch (this.status()) { - case 'skipped': - break; - - case 'passed': - this.appendSummaryToSuiteDiv(); - break; - - case 'failed': - this.appendSummaryToSuiteDiv(); - this.appendFailureDetail(); - break; - } -}; - -jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { - this.summary.className += ' ' + this.status(); - this.appendToSummary(this.spec, this.summary); -}; - -jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { - this.detail.className += ' ' + this.status(); - - var resultItems = this.spec.results().getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + if (typeof child === 'string') { + el.appendChild(createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } } } - } - if (messagesDiv.childNodes.length > 0) { - this.detail.appendChild(messagesDiv); - this.dom.details.appendChild(this.detail); - } -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { - this.suite = suite; - this.dom = dom; - this.views = views; - - this.element = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description) - ); - - this.appendToSummary(this.suite, this.element); -}; - -jasmine.HtmlReporter.SuiteView.prototype.status = function() { - return this.getSpecStatus(this.suite); -}; - -jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { - this.element.className += " " + this.status(); -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); - -/* @deprecated Use jasmine.HtmlReporter instead - */ -jasmine.TrivialReporter = function(doc) { - this.document = doc || document; - this.suiteDivs = {}; - this.logRunningSpecs = false; -}; - -jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { el.appendChild(child); } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { - var showPassed, showSkipped; - - this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, - this.createDom('div', { className: 'banner' }, - this.createDom('div', { className: 'logo' }, - this.createDom('span', { className: 'title' }, "Jasmine"), - this.createDom('span', { className: 'version' }, runner.env.versionString())), - this.createDom('div', { className: 'options' }, - "Show ", - showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), - showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") - ) - ), - - this.runnerDiv = this.createDom('div', { className: 'runner running' }, - this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), - this.runnerMessageSpan = this.createDom('span', {}, "Running..."), - this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) - ); - - this.document.body.appendChild(this.outerDiv); - - var suites = runner.suites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - var suiteDiv = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); - this.suiteDivs[suite.id] = suiteDiv; - var parentDiv = this.outerDiv; - if (suite.parentSuite) { - parentDiv = this.suiteDivs[suite.parentSuite.id]; - } - parentDiv.appendChild(suiteDiv); - } - - this.startedAt = new Date(); - - var self = this; - showPassed.onclick = function(evt) { - if (showPassed.checked) { - self.outerDiv.className += ' show-passed'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); - } - }; - - showSkipped.onclick = function(evt) { - if (showSkipped.checked) { - self.outerDiv.className += ' show-skipped'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); - } - }; -}; - -jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { - var results = runner.results(); - var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; - this.runnerDiv.setAttribute("class", className); - //do it twice for IE - this.runnerDiv.setAttribute("className", className); - var specs = runner.specs(); - var specCount = 0; - for (var i = 0; i < specs.length; i++) { - if (this.specFilter(specs[i])) { - specCount++; - } - } - var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); - message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; - this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); - - this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); -}; - -jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { - var results = suite.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.totalCount === 0) { // todo: change this to check results.skipped - status = 'skipped'; - } - this.suiteDivs[suite.id].className += " " + status; -}; - -jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { - if (this.logRunningSpecs) { - this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } -}; - -jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { - var results = spec.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - var specDiv = this.createDom('div', { className: 'spec ' + status }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(spec.getFullName()), - title: spec.getFullName() - }, spec.description)); - - - var resultItems = results.getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); } } + + return el; } - if (messagesDiv.childNodes.length > 0) { - specDiv.appendChild(messagesDiv); + function pluralize(singular, count) { + var word = (count == 1 ? singular : singular + "s"); + + return "" + count + " " + word; } - this.suiteDivs[spec.suite.id].appendChild(specDiv); -}; + function specHref(result) { + return "?spec=" + encodeURIComponent(result.fullName); + } -jasmine.TrivialReporter.prototype.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + function isFiltered() { + buildSpecFilter(); + + return !!specFilterPattern; + } + + function buildSpecFilter() { + var specFilterParam = queryString.getParam("spec") || ""; + + specFilterPattern = new RegExp(specFilterParam); + } + + function setMenuModeTo(mode) { + htmlReporterMain.setAttribute("class", "html-reporter " + mode); + } +};jasmine.ResultsNode = function(result, type, parent) { + this.result = result; + this.type = type; + this.parent = parent; + + this.children = []; + + this.addChild = function(result, type) { + this.children.push(new jasmine.ResultsNode(result, type, this)); + }; + + this.last = function() { + return this.children[this.children.length-1]; + } +};jasmine.QueryString = function(options) { + + this.setParam = function(key, value) { + var paramMap = queryStringToParamMap(); + paramMap[key] = value; + options.getWindowLocation().search = toQueryString(paramMap); + }; + + this.getParam = function(key) { + return queryStringToParamMap()[key]; + }; + + return this; + + function toQueryString(paramMap) { + var qStrPairs = []; + for (var prop in paramMap) { + qStrPairs.push(encodeURIComponent(prop) + "=" + encodeURIComponent(paramMap[prop])); } - } -}; - -jasmine.TrivialReporter.prototype.getLocation = function() { - return this.document.location; -}; - -jasmine.TrivialReporter.prototype.specFilter = function(spec) { - var paramMap = {}; - var params = this.getLocation().search.substring(1).split('&'); - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + return "?" + qStrPairs.join('&'); } - if (!paramMap.spec) { - return true; + function queryStringToParamMap() { + var paramStr = options.getWindowLocation().search.substring(1), + params = [], + paramMap = {}; + + if (paramStr.length > 0) { + params = paramStr.split('&'); + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + var value = decodeURIComponent(p[1]); + if (value === "true" || value === "false") { + value = JSON.parse(value); + } + paramMap[decodeURIComponent(p[0])] = value; + } + } + + return paramMap; } - return spec.getFullName().indexOf(paramMap.spec) === 0; -}; + +}; \ No newline at end of file diff --git a/lib/jasmine-core/jasmine.css b/lib/jasmine-core/jasmine.css index 8c008dc7..91147778 100644 --- a/lib/jasmine-core/jasmine.css +++ b/lib/jasmine-core/jasmine.css @@ -1,82 +1,53 @@ body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } -#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } -#HTMLReporter a { text-decoration: none; } -#HTMLReporter a:hover { text-decoration: underline; } -#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } -#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } -#HTMLReporter #jasmine_content { position: fixed; right: 100%; } -#HTMLReporter .version { color: #aaaaaa; } -#HTMLReporter .banner { margin-top: 14px; } -#HTMLReporter .duration { color: #aaaaaa; float: right; } -#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } -#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } -#HTMLReporter .symbolSummary li.passed { font-size: 14px; } -#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } -#HTMLReporter .symbolSummary li.failed { line-height: 9px; } -#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } -#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } -#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } -#HTMLReporter .symbolSummary li.pending { line-height: 11px; } -#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } -#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } -#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } -#HTMLReporter .runningAlert { background-color: #666666; } -#HTMLReporter .skippedAlert { background-color: #aaaaaa; } -#HTMLReporter .skippedAlert:first-child { background-color: #333333; } -#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } -#HTMLReporter .passingAlert { background-color: #a6b779; } -#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } -#HTMLReporter .failingAlert { background-color: #cf867e; } -#HTMLReporter .failingAlert:first-child { background-color: #b03911; } -#HTMLReporter .results { margin-top: 14px; } -#HTMLReporter #details { display: none; } -#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } -#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } -#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } -#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter.showDetails .summary { display: none; } -#HTMLReporter.showDetails #details { display: block; } -#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter .summary { margin-top: 14px; } -#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } -#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } -#HTMLReporter .summary .specSummary.failed a { color: #b03911; } -#HTMLReporter .description + .suite { margin-top: 0; } -#HTMLReporter .suite { margin-top: 14px; } -#HTMLReporter .suite a { color: #333333; } -#HTMLReporter #details .specDetail { margin-bottom: 28px; } -#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } -#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } -#HTMLReporter .resultMessage span.result { display: block; } -#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } - -#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } -#TrivialReporter a:visited, #TrivialReporter a { color: #303; } -#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } -#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } -#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } -#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } -#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } -#TrivialReporter .runner.running { background-color: yellow; } -#TrivialReporter .options { text-align: right; font-size: .8em; } -#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } -#TrivialReporter .suite .suite { margin: 5px; } -#TrivialReporter .suite.passed { background-color: #dfd; } -#TrivialReporter .suite.failed { background-color: #fdd; } -#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } -#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } -#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } -#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } -#TrivialReporter .spec.skipped { background-color: #bbb; } -#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } -#TrivialReporter .passed { background-color: #cfc; display: none; } -#TrivialReporter .failed { background-color: #fbb; } -#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } -#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } -#TrivialReporter .resultMessage .mismatch { color: black; } -#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } -#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } -#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } -#TrivialReporter #jasmine_content { position: fixed; right: 100%; } -#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } +.html-reporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } +.html-reporter a { text-decoration: none; } +.html-reporter a:hover { text-decoration: underline; } +.html-reporter p, .html-reporter h1, .html-reporter h2, .html-reporter h3, .html-reporter h4, .html-reporter h5, .html-reporter h6 { margin: 0; line-height: 14px; } +.html-reporter .banner, .html-reporter .symbol-summary, .html-reporter .summary, .html-reporter .result-message, .html-reporter .spec .description, .html-reporter .spec-detail .description, .html-reporter .alert .bar, .html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; } +.html-reporter .banner .version { margin-left: 14px; } +.html-reporter #jasmine_content { position: fixed; right: 100%; } +.html-reporter .version { color: #aaaaaa; } +.html-reporter .banner { margin-top: 14px; } +.html-reporter .duration { color: #aaaaaa; float: right; } +.html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } +.html-reporter .symbol-summary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } +.html-reporter .symbol-summary li.passed { font-size: 14px; } +.html-reporter .symbol-summary li.passed:before { color: #5e7d00; content: "\02022"; } +.html-reporter .symbol-summary li.failed { line-height: 9px; } +.html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } +.html-reporter .symbol-summary li.disabled { font-size: 14px; } +.html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } +.html-reporter .symbol-summary li.pending { line-height: 11px; } +.html-reporter .symbol-summary li.pending:before { color: #aaaaaa; content: "-"; } +.html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } +.html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +.html-reporter .bar.failed { background-color: #b03911; } +.html-reporter .bar.passed { background-color: #a6b779; } +.html-reporter .bar.skipped { background-color: #bababa; } +.html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; } +.html-reporter .bar.menu a { color: #333333; } +.html-reporter .bar a { color: white; } +.html-reporter.spec-list .bar.menu.failure-list, .html-reporter.spec-list .results .failures { display: none; } +.html-reporter.failure-list .bar.menu.spec-list, .html-reporter.failure-list .summary { display: none; } +.html-reporter .running-alert { background-color: #666666; } +.html-reporter .results { margin-top: 14px; } +.html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } +.html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } +.html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } +.html-reporter.showDetails .summary { display: none; } +.html-reporter.showDetails #details { display: block; } +.html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } +.html-reporter .summary { margin-top: 14px; } +.html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } +.html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } +.html-reporter .summary li.passed a { color: #5e7d00; } +.html-reporter .summary li.failed a { color: #b03911; } +.html-reporter .description + .suite { margin-top: 0; } +.html-reporter .suite { margin-top: 14px; } +.html-reporter .suite a { color: #333333; } +.html-reporter .failures .spec-detail { margin-bottom: 28px; } +.html-reporter .failures .spec-detail .description { display: block; color: white; background-color: #b03911; } +.html-reporter .result-message { padding-top: 14px; color: #333333; } +.html-reporter .result-message span.result { display: block; } +.html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 6b3459b9..d5f4f8d2 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -1,4 +1,3 @@ -var isCommonJS = typeof window == "undefined" && typeof exports == "object"; /** * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. @@ -6,7 +5,12 @@ var isCommonJS = typeof window == "undefined" && typeof exports == "object"; * @namespace */ var jasmine = {}; -if (isCommonJS) exports.jasmine = jasmine; + +// TODO: do we need this now that we have boot.js? +if (typeof window == "undefined" && typeof exports == "object") { + exports.jasmine = jasmine +} + /** * @private */ @@ -22,12 +26,6 @@ jasmine.unimplementedMethod_ = function() { */ jasmine.undefined = jasmine.___undefined___; -/** - * Show diagnostic messages in the console if set to true - * - */ -jasmine.VERBOSE = false; - /** * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. * @@ -44,13 +42,6 @@ jasmine.MAX_PRETTY_PRINT_DEPTH = 40; */ jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; -/** - * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite. - * Set to false to let the exception bubble up in the browser. - * - */ -jasmine.CATCH_EXCEPTIONS = true; - jasmine.getGlobal = function() { function getGlobal() { return this; @@ -59,75 +50,12 @@ jasmine.getGlobal = function() { return getGlobal(); }; -/** - * Allows for bound functions to be compared. Internal use only. - * - * @ignore - * @private - * @param base {Object} bound 'this' for the function - * @param name {Function} function to find - */ -jasmine.bindOriginal_ = function(base, name) { - var original = base[name]; - if (original.apply) { - return function() { - return original.apply(base, arguments); - }; - } else { - // IE support - return jasmine.getGlobal()[name]; - } -}; - -jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); -jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); -jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); -jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); - -jasmine.MessageResult = function(values) { - this.type = 'log'; - this.values = values; - this.trace = new Error(); // todo: test better -}; - -jasmine.MessageResult.prototype.toString = function() { - var text = ""; - for (var i = 0; i < this.values.length; i++) { - if (i > 0) text += " "; - if (jasmine.isString_(this.values[i])) { - text += this.values[i]; - } else { - text += jasmine.pp(this.values[i]); - } - } - return text; -}; - -jasmine.ExpectationResult = function(params) { - this.type = 'expect'; - this.matcherName = params.matcherName; - this.passed_ = params.passed; - this.expected = params.expected; - this.actual = params.actual; - this.message = this.passed_ ? 'Passed.' : params.message; - - var trace = (params.trace || new Error(this.message)); - this.trace = this.passed_ ? '' : trace; -}; - -jasmine.ExpectationResult.prototype.toString = function () { - return this.message; -}; - -jasmine.ExpectationResult.prototype.passed = function () { - return this.passed_; -}; - /** * Getter for the Jasmine environment. Ensures one gets created */ -jasmine.getEnv = function() { - var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); +jasmine.getEnv = function(options) { + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(options); + //jasmine. singletons in here (setTimeout blah blah). return env; }; @@ -451,198 +379,6 @@ jasmine.createSpyObj = function(baseName, methodNames) { } return obj; }; - -/** - * All parameters are pretty-printed and concatenated together, then written to the current spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.log = function() { - var spec = jasmine.getEnv().currentSpec; - spec.log.apply(spec, arguments); -}; - -/** - * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. - * - * @example - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops - * - * @see jasmine.createSpy - * @param obj - * @param methodName - * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods - */ -var spyOn = function(obj, methodName) { - return jasmine.getEnv().currentSpec.spyOn(obj, methodName); -}; -if (isCommonJS) exports.spyOn = spyOn; - -/** - * Creates a Jasmine spec that will be added to the current suite. - * - * // TODO: pending tests - * - * @example - * it('should be true', function() { - * expect(true).toEqual(true); - * }); - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var it = function(desc, func) { - return jasmine.getEnv().it(desc, func); -}; -if (isCommonJS) exports.it = it; - -/** - * Creates a disabled Jasmine spec. - * - * A convenience method that allows existing specs to be disabled temporarily during development. - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var xit = function(desc, func) { - return jasmine.getEnv().xit(desc, func); -}; -if (isCommonJS) exports.xit = xit; - -/** - * Starts a chain for a Jasmine expectation. - * - * It is passed an Object that is the actual value and should chain to one of the many - * jasmine.Matchers functions. - * - * @param {Object} actual Actual value to test against and expected value - * @return {jasmine.Matchers} - */ -var expect = function(actual) { - return jasmine.getEnv().currentSpec.expect(actual); -}; -if (isCommonJS) exports.expect = expect; - -/** - * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. - * - * @param {Function} func Function that defines part of a jasmine spec. - */ -var runs = function(func) { - jasmine.getEnv().currentSpec.runs(func); -}; -if (isCommonJS) exports.runs = runs; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -var waits = function(timeout) { - jasmine.getEnv().currentSpec.waits(timeout); -}; -if (isCommonJS) exports.waits = waits; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); -}; -if (isCommonJS) exports.waitsFor = waitsFor; - -/** - * A function that is called before each spec in a suite. - * - * Used for spec setup, including validating assumptions. - * - * @param {Function} beforeEachFunction - */ -var beforeEach = function(beforeEachFunction) { - jasmine.getEnv().beforeEach(beforeEachFunction); -}; -if (isCommonJS) exports.beforeEach = beforeEach; - -/** - * A function that is called after each spec in a suite. - * - * Used for restoring any state that is hijacked during spec execution. - * - * @param {Function} afterEachFunction - */ -var afterEach = function(afterEachFunction) { - jasmine.getEnv().afterEach(afterEachFunction); -}; -if (isCommonJS) exports.afterEach = afterEach; - -/** - * Defines a suite of specifications. - * - * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared - * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization - * of setup in some tests. - * - * @example - * // TODO: a simple suite - * - * // TODO: a simple suite with a nested describe block - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var describe = function(description, specDefinitions) { - return jasmine.getEnv().describe(description, specDefinitions); -}; -if (isCommonJS) exports.describe = describe; - -/** - * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var xdescribe = function(description, specDefinitions) { - return jasmine.getEnv().xdescribe(description, specDefinitions); -}; -if (isCommonJS) exports.xdescribe = xdescribe; - - -// Provide the XMLHttpRequest class for IE 5.x-6.x: -jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { - function tryIt(f) { - try { - return f(); - } catch(e) { - } - return null; - } - - var xhr = tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.6.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.3.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP"); - }) || - tryIt(function() { - return new ActiveXObject("Microsoft.XMLHTTP"); - }); - - if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); - - return xhr; -} : XMLHttpRequest; /** * @namespace */ @@ -703,485 +439,572 @@ jasmine.util.argsToArray = function(args) { var arrayOfArgs = []; for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); return arrayOfArgs; +};jasmine.exceptionMessageFor = function(e) { + var message = e.name + + ': ' + + e.message + + ' in ' + + (e.fileName || e.sourceURL || '') + + ' (line ' + + (e.line || e.lineNumber || '') + + ')'; + + return message; }; - -jasmine.util.extend = function(destination, source) { - for (var property in source) destination[property] = source[property]; - return destination; -}; - -/** - * Environment for Jasmine - * - * @constructor - */ -jasmine.Env = function() { - this.currentSpec = null; - this.currentSuite = null; - this.currentRunner_ = new jasmine.Runner(this); - - this.reporter = new jasmine.MultiReporter(); - - this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; - this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; - this.lastUpdate = 0; - this.specFilter = function() { - return true; +//TODO: expectation result may make more sense as a presentation of an expectation. +jasmine.buildExpectationResult = function(params) { + return { + type: 'expect', + matcherName: params.matcherName, + expected: params.expected, + actual: params.actual, + message: params.passed ? 'Passed.' : params.message, + trace: params.passed ? '' : (params.trace || new Error(this.message)), + passed: params.passed }; - - this.nextSpecId_ = 0; - this.nextSuiteId_ = 0; - this.equalityTesters_ = []; - - // wrap matchers - this.matchersClass = function() { - jasmine.Matchers.apply(this, arguments); - }; - jasmine.util.inherit(this.matchersClass, jasmine.Matchers); - - jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); }; +(function() { + jasmine.Env = function(options) { + options = options || {}; + var self = this; + var global = options.global || jasmine.getGlobal(); + + var catchExceptions = true; + + this.clock = new jasmine.Clock(global, new jasmine.DelayedFunctionScheduler()); + + this.jasmine = jasmine; + this.spies_ = []; + this.currentSpec = null; + + this.undefined = jasmine.undefined; + + this.reporter = new jasmine.ReportDispatcher([ + "jasmineStarted", + "jasmineDone", + "suiteStarted", + "suiteDone", + "specStarted", + "specDone" + ]); + + this.lastUpdate = 0; + this.specFilter = function() { + return true; + }; + + this.nextSpecId_ = 0; + this.nextSuiteId_ = 0; + this.equalityTesters_ = []; + + // wrap matchers + this.matchersClass = function() { + jasmine.Matchers.apply(this, arguments); + }; + jasmine.util.inherit(this.matchersClass, jasmine.Matchers); + + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); + + var expectationFactory = function(actual, spec) { + var expect = new (self.matchersClass)(self, actual, spec); + expect.not = new (self.matchersClass)(self, actual, spec, true); + return expect; + }; + + var specStarted = function(spec) { + self.currentSpec = spec; + self.reporter.specStarted(spec.result); + }; + + var beforeFns = function(currentSuite) { + return function() { + var befores = []; + for (var suite = currentSuite; suite; suite = suite.parentSuite) { + befores = befores.concat(suite.beforeFns) + } + return befores.reverse(); + } + }; + + var afterFns = function(currentSuite) { + return function() { + var afters = []; + for (var suite = currentSuite; suite; suite = suite.parentSuite) { + afters = afters.concat(suite.afterFns) + } + return afters; + } + }; + + var exceptionFormatter = jasmine.exceptionFormatter; + + var specConstructor = jasmine.Spec; + + var getSpecName = function(spec, currentSuite) { + return currentSuite.getFullName() + ' ' + spec.description + '.'; + }; + + var buildExpectationResult = jasmine.buildExpectationResult; + var expectationResultFactory = function(attrs) { + 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 = 100; + var currentSpecCallbackDepth = 0; + + function encourageGarbageCollection(fn) { + currentSpecCallbackDepth++; + if (currentSpecCallbackDepth > maximumSpecCallbackDepth) { + currentSpecCallbackDepth = 0; + global.setTimeout(fn, 0); + } else { + fn(); + } + } + + var queueRunnerFactory = function(options) { + options.catchingExceptions = self.catchingExceptions; + options.encourageGC = options.encourageGarbageCollection || encourageGarbageCollection; + + new jasmine.QueueRunner(options).run(options.fns, 0); + }; -jasmine.Env.prototype.setTimeout = jasmine.setTimeout; -jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; -jasmine.Env.prototype.setInterval = jasmine.setInterval; -jasmine.Env.prototype.clearInterval = jasmine.clearInterval; + var totalSpecsDefined = 0; + this.specFactory = function(description, fn, suite) { + totalSpecsDefined++; -/** - * @returns an object containing jasmine version build info, if set. - */ -jasmine.Env.prototype.version = function () { - if (jasmine.version_) { - return jasmine.version_; - } else { - throw new Error('Version not set'); - } -}; + var spec = new specConstructor({ + id: self.nextSpecId(), + 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, + queueRunner: queueRunnerFactory, + fn: fn + }); -/** - * @returns string containing jasmine version build info, if set. - */ -jasmine.Env.prototype.versionString = function() { - if (!jasmine.version_) { - return "version unknown"; - } + if (!self.specFilter(spec)) { + spec.disable(); + } - var version = this.version(); - var versionString = version.major + "." + version.minor + "." + version.build; - if (version.release_candidate) { - versionString += ".rc" + version.release_candidate; - } - versionString += " revision " + version.revision; - return versionString; -}; + return spec; -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSpecId = function () { - return this.nextSpecId_++; -}; + function specResultCallback(result) { + self.removeAllSpies(); + self.clock.uninstall(); + self.currentSpec = null; + self.reporter.specDone(result); + } + }; -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSuiteId = function () { - return this.nextSuiteId_++; -}; + var suiteStarted = function(suite) { + self.reporter.suiteStarted(suite.result); + }; -/** - * Register a reporter to receive status updates from Jasmine. - * @param {jasmine.Reporter} reporter An object which will receive status updates. - */ -jasmine.Env.prototype.addReporter = function(reporter) { - this.reporter.addReporter(reporter); -}; + var suiteConstructor = jasmine.Suite; -jasmine.Env.prototype.execute = function() { - this.currentRunner_.execute(); -}; - -jasmine.Env.prototype.describe = function(description, specDefinitions) { - var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); - - var parentSuite = this.currentSuite; - if (parentSuite) { - parentSuite.add(suite); - } else { - this.currentRunner_.add(suite); - } - - this.currentSuite = suite; - - var declarationError = null; - try { - specDefinitions.call(suite); - } catch(e) { - declarationError = e; - } - - if (declarationError) { - this.it("encountered a declaration exception", function() { - throw declarationError; + this.topSuite = new jasmine.Suite({ + env: this, + id: this.nextSuiteId(), + description: 'Jasmine__TopLevel__Suite', + queueRunner: queueRunnerFactory, + completeCallback: function() {}, // TODO - hook this up + resultCallback: function() {} // TODO - hook this up }); - } + this.currentSuite = this.topSuite; - this.currentSuite = parentSuite; + this.suiteFactory = function(description) { + return new suiteConstructor({ + env: self, + id: self.nextSuiteId(), + description: description, + parentSuite: self.currentSuite, + queueRunner: queueRunnerFactory, + onStart: suiteStarted, + resultCallback: function(attrs) { + self.reporter.suiteDone(attrs); + } + }); + }; - return suite; -}; - -jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { - if (this.currentSuite) { - this.currentSuite.beforeEach(beforeEachFunction); - } else { - this.currentRunner_.beforeEach(beforeEachFunction); - } -}; - -jasmine.Env.prototype.currentRunner = function () { - return this.currentRunner_; -}; - -jasmine.Env.prototype.afterEach = function(afterEachFunction) { - if (this.currentSuite) { - this.currentSuite.afterEach(afterEachFunction); - } else { - this.currentRunner_.afterEach(afterEachFunction); - } - -}; - -jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { - return { - execute: function() { - } - }; -}; - -jasmine.Env.prototype.it = function(description, func) { - var spec = new jasmine.Spec(this, this.currentSuite, description); - this.currentSuite.add(spec); - this.currentSpec = spec; - - if (func) { - spec.runs(func); - } - - return spec; -}; - -jasmine.Env.prototype.xit = function(desc, func) { - return { - id: this.nextSpecId(), - runs: function() { - } - }; -}; - -jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { - if (a.source != b.source) - mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); - - if (a.ignoreCase != b.ignoreCase) - mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); - - if (a.global != b.global) - mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); - - if (a.multiline != b.multiline) - mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); - - if (a.sticky != b.sticky) - mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); - - return (mismatchValues.length === 0); -}; - -jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { - if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { - return true; - } - - a.__Jasmine_been_here_before__ = b; - b.__Jasmine_been_here_before__ = a; - - var hasKey = function(obj, keyName) { - return obj !== null && obj[keyName] !== jasmine.undefined; + this.execute = function() { + this.reporter.jasmineStarted({ + totalSpecsDefined: totalSpecsDefined + }); + this.topSuite.execute(this.reporter.jasmineDone); + }; }; - for (var property in b) { - if (!hasKey(a, property) && hasKey(b, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + //TODO: shim Spec addMatchers behavior into Env. Should be rewritten to remove globals, etc. + jasmine.Env.prototype.addMatchers = function(matchersPrototype) { + var parent = this.matchersClass; + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); + this.matchersClass = newMatchersClass; + }; + + jasmine.Env.prototype.version = function() { + if (this.jasmine.version_) { + return this.jasmine.version_; + } else { + throw new Error('Version not set'); } - } - for (property in a) { - if (!hasKey(b, property) && hasKey(a, property)) { - mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + }; + + jasmine.Env.prototype.expect = function(actual) { + return this.currentSpec.expect(actual); + }; + + jasmine.Env.prototype.spyOn = function(obj, methodName) { + if (obj == this.undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; } - } - for (property in b) { - if (property == '__Jasmine_been_here_before__') continue; - if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + + if (obj[methodName] === this.undefined) { + throw methodName + '() method does not exist'; } - } - if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { - mismatchValues.push("arrays were not the same length"); - } - - delete a.__Jasmine_been_here_before__; - delete b.__Jasmine_been_here_before__; - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - for (var i = 0; i < this.equalityTesters_.length; i++) { - var equalityTester = this.equalityTesters_[i]; - var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); - if (result !== jasmine.undefined) return result; - } - - if (a === b) return true; - - if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { - return (a == jasmine.undefined && b == jasmine.undefined); - } - - if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { - return a === b; - } - - if (a instanceof Date && b instanceof Date) { - return a.getTime() == b.getTime(); - } - - if (a.jasmineMatches) { - return a.jasmineMatches(b); - } - - if (b.jasmineMatches) { - return b.jasmineMatches(a); - } - - if (a instanceof jasmine.Matchers.ObjectContaining) { - return a.matches(b); - } - - if (b instanceof jasmine.Matchers.ObjectContaining) { - return b.matches(a); - } - - if (jasmine.isString_(a) && jasmine.isString_(b)) { - return (a == b); - } - - if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { - return (a == b); - } - - if (a instanceof RegExp && b instanceof RegExp) { - return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); - } - - if (typeof a === "object" && typeof b === "object") { - return this.compareObjects_(a, b, mismatchKeys, mismatchValues); - } - - //Straight check - return (a === b); -}; - -jasmine.Env.prototype.contains_ = function(haystack, needle) { - if (jasmine.isArray_(haystack)) { - for (var i = 0; i < haystack.length; i++) { - if (this.equals_(haystack[i], needle)) return true; + if (obj[methodName] && obj[methodName].isSpy) { + //TODO?: should this return the current spy? Downside: may cause user confusion about spy state + throw new Error(methodName + ' has already been spied upon'); } - return false; - } - return haystack.indexOf(needle) >= 0; -}; -jasmine.Env.prototype.addEqualityTester = function(equalityTester) { - this.equalityTesters_.push(equalityTester); -}; -/** No-op base class for Jasmine reporters. - * - * @constructor - */ -jasmine.Reporter = function() { -}; + var spyObj = jasmine.createSpy(methodName); -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { -}; + this.spies_.push(spyObj); + spyObj.baseObj = obj; + spyObj.methodName = methodName; + spyObj.originalValue = obj[methodName]; -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerResults = function(runner) { -}; + obj[methodName] = spyObj; -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSuiteResults = function(suite) { -}; + return spyObj; + }; -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecStarting = function(spec) { -}; + // TODO: move this to closure + jasmine.Env.prototype.removeAllSpies = function() { + for (var i = 0; i < this.spies_.length; i++) { + var spy = this.spies_[i]; + spy.baseObj[spy.methodName] = spy.originalValue; + } + this.spies_ = []; + }; -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecResults = function(spec) { -}; + // TODO: move this to closure + jasmine.Env.prototype.versionString = function() { + if (!this.jasmine.version_) { + return "version unknown"; + } -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.log = function(str) { -}; + var version = this.version(); + var versionString = version.major + "." + version.minor + "." + version.build; + if (version.release_candidate) { + versionString += ".rc" + version.release_candidate; + } + versionString += " revision " + version.revision; + return versionString; + }; -/** - * Blocks are functions with executable code that make up a spec. - * - * @constructor - * @param {jasmine.Env} env - * @param {Function} func - * @param {jasmine.Spec} spec - */ -jasmine.Block = function(env, func, spec) { - this.env = env; - this.func = func; - this.spec = spec; -}; + // TODO: move this to closure + jasmine.Env.prototype.nextSpecId = function() { + return this.nextSpecId_++; + }; -jasmine.Block.prototype.execute = function(onComplete) { - if (!jasmine.CATCH_EXCEPTIONS) { - this.func.apply(this.spec); - } - else { + // TODO: move this to closure + jasmine.Env.prototype.nextSuiteId = function() { + return this.nextSuiteId_++; + }; + + // TODO: move this to closure + jasmine.Env.prototype.addReporter = function(reporter) { + this.reporter.addReporter(reporter); + }; + + // TODO: move this to closure + jasmine.Env.prototype.describe = function(description, specDefinitions) { + var suite = this.suiteFactory(description, specDefinitions); + + var parentSuite = this.currentSuite; + parentSuite.addSuite(suite); + this.currentSuite = suite; + + var declarationError = null; try { - this.func.apply(this.spec); + specDefinitions.call(suite); } catch (e) { - this.spec.fail(e); + declarationError = e; } - } - onComplete(); -}; -/** JavaScript API reporter. - * - * @constructor - */ -jasmine.JsApiReporter = function() { + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + this.currentSuite = parentSuite; + + return suite; + }; + + // TODO: move this to closure + jasmine.Env.prototype.xdescribe = function(description, specDefinitions) { + var suite = this.describe(description, specDefinitions); + suite.disable(); + return suite; + }; + + // TODO: move this to closure + jasmine.Env.prototype.it = function(description, fn) { + var spec = this.specFactory(description, fn, this.currentSuite); + this.currentSuite.addSpec(spec); + return spec; + }; + + // TODO: move this to closure + jasmine.Env.prototype.xit = function(description, fn) { + var spec = this.it(description, fn); + spec.disable(); + return spec; + }; + + // TODO: move this to closure + jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { + this.currentSuite.beforeEach(beforeEachFunction); + }; + + // TODO: move this to closure + jasmine.Env.prototype.afterEach = function(afterEachFunction) { + this.currentSuite.afterEach(afterEachFunction); + }; + + // TODO: Still needed? + jasmine.Env.prototype.currentRunner = function() { + return this.topSuite; + }; + + jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.source != b.source) + mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); + + if (a.ignoreCase != b.ignoreCase) + mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); + + if (a.global != b.global) + mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); + + if (a.multiline != b.multiline) + mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); + + if (a.sticky != b.sticky) + mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); + + return (mismatchValues.length === 0); + }; + + jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { + return true; + } + + a.__Jasmine_been_here_before__ = b; + b.__Jasmine_been_here_before__ = a; + + var hasKey = function(obj, keyName) { + return obj !== null && obj[keyName] !== this.undefined; + }; + + for (var property in b) { + if (!hasKey(a, property) && hasKey(b, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + } + for (property in a) { + if (!hasKey(b, property) && hasKey(a, property)) { + mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + } + } + for (property in b) { + if (property == '__Jasmine_been_here_before__') continue; + if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + } + } + + if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { + mismatchValues.push("arrays were not the same length"); + } + + delete a.__Jasmine_been_here_before__; + delete b.__Jasmine_been_here_before__; + return (mismatchKeys.length === 0 && mismatchValues.length === 0); + }; + + jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== this.undefined) return result; + } + + if (a === b) return true; + + if (a === this.undefined || a === null || b === this.undefined || b === null) { + return (a == this.undefined && b == this.undefined); + } + + if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() == b.getTime(); + } + + if (a.jasmineMatches) { + return a.jasmineMatches(b); + } + + if (b.jasmineMatches) { + return b.jasmineMatches(a); + } + + if (a instanceof jasmine.Matchers.ObjectContaining) { + return a.matches(b); + } + + if (b instanceof jasmine.Matchers.ObjectContaining) { + return b.matches(a); + } + + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); + } + + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (a instanceof RegExp && b instanceof RegExp) { + return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + } + + //Straight check + return (a === b); + }; + + jasmine.Env.prototype.contains_ = function(haystack, needle) { + if (jasmine.isArray_(haystack)) { + for (var i = 0; i < haystack.length; i++) { + if (this.equals_(haystack[i], needle)) return true; + } + return false; + } + return haystack.indexOf(needle) >= 0; + }; + + jasmine.Env.prototype.addEqualityTester = function(equalityTester) { + this.equalityTesters_.push(equalityTester); + }; +}()); +jasmine.JsApiReporter = function(jasmine) { + this.jasmine = jasmine || {}; this.started = false; this.finished = false; this.suites_ = []; this.results_ = {}; -}; -jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { - this.started = true; - var suites = runner.topLevelSuites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - this.suites_.push(this.summarize_(suite)); - } -}; + var status = 'loaded'; -jasmine.JsApiReporter.prototype.suites = function() { - return this.suites_; -}; - -jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { - var isSuite = suiteOrSpec instanceof jasmine.Suite; - var summary = { - id: suiteOrSpec.id, - name: suiteOrSpec.description, - type: isSuite ? 'suite' : 'spec', - children: [] + this.jasmineStarted = function() { + this.started = true; + status = 'started'; }; - - if (isSuite) { - var children = suiteOrSpec.children(); - for (var i = 0; i < children.length; i++) { - summary.children.push(this.summarize_(children[i])); - } - } - return summary; -}; -jasmine.JsApiReporter.prototype.results = function() { - return this.results_; -}; - -jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { - return this.results_[specId]; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { - this.finished = true; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { - this.results_[spec.id] = { - messages: spec.results().getItems(), - result: spec.results().failedCount > 0 ? "failed" : "passed" + this.jasmineDone = function() { + this.finished = true; + status = 'done'; }; -}; -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.log = function(str) { -}; - -jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ - var results = {}; - for (var i = 0; i < specIds.length; i++) { - var specId = specIds[i]; - results[specId] = this.summarizeResult_(this.results_[specId]); - } - return results; -}; - -jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ - var summaryMessages = []; - var messagesLength = result.messages.length; - for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { - var resultMessage = result.messages[messageIndex]; - summaryMessages.push({ - text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, - passed: resultMessage.passed ? resultMessage.passed() : true, - type: resultMessage.type, - message: resultMessage.message, - trace: { - stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined - } - }); - } - - return { - result : result.result, - messages : summaryMessages + this.status = function() { + return status; }; -}; -/** + var suites = {}; + + this.suiteStarted = function(result) { + storeSuite(result); + }; + + this.suiteDone = function(result) { + storeSuite(result); + }; + + function storeSuite(result) { + suites[result.id] = result; + } + + this.suites = function() { + return suites; + }; + + var specs = {}; + + this.specStarted = function(result) { + storeSpec(result); + }; + + this.specDone = function(result) { + storeSpec(result); + }; + + function storeSpec(result) { + specs[result.id] = result; + } + + this.specs = function() { + return specs; + }; + +};/** * @constructor * @param {jasmine.Env} env * @param actual * @param {jasmine.Spec} spec */ jasmine.Matchers = function(env, actual, spec, opt_isNot) { + //TODO: true dependency: equals, contains this.env = env; this.actual = actual; this.spec = spec; this.isNot = opt_isNot || false; - this.reportWasCalled_ = false; }; // todo: @deprecated as of Jasmine 0.11, remove soon [xw] @@ -1189,14 +1012,9 @@ jasmine.Matchers.pp = function(str) { throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); }; -// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] -jasmine.Matchers.prototype.report = function(result, failing_message, details) { - throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); -}; jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { for (var methodName in prototype) { - if (methodName == 'report') continue; var orig = prototype[methodName]; matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); } @@ -1211,8 +1029,6 @@ jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { result = !result; } - if (this.reportWasCalled_) return result; - var message; if (!result) { if (this.message) { @@ -1232,14 +1048,14 @@ jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { message += "."; } } - var expectationResult = new jasmine.ExpectationResult({ + var expectationResult = jasmine.buildExpectationResult({ matcherName: matcherName, passed: result, expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], actual: this.actual, message: message }); - this.spec.addMatcherResult(expectationResult); + this.spec.addExpectationResult(result, expectationResult); return jasmine.undefined; }; }; @@ -1574,304 +1390,6 @@ jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mis jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { return ""; }; -// Mock setTimeout, clearTimeout -// Contributed by Pivotal Computer Systems, www.pivotalsf.com - -jasmine.FakeTimer = function() { - this.reset(); - - var self = this; - self.setTimeout = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); - return self.timeoutsMade; - }; - - self.setInterval = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); - return self.timeoutsMade; - }; - - self.clearTimeout = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - - self.clearInterval = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - -}; - -jasmine.FakeTimer.prototype.reset = function() { - this.timeoutsMade = 0; - this.scheduledFunctions = {}; - this.nowMillis = 0; -}; - -jasmine.FakeTimer.prototype.tick = function(millis) { - var oldMillis = this.nowMillis; - var newMillis = oldMillis + millis; - this.runFunctionsWithinRange(oldMillis, newMillis); - this.nowMillis = newMillis; -}; - -jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { - var scheduledFunc; - var funcsToRun = []; - for (var timeoutKey in this.scheduledFunctions) { - scheduledFunc = this.scheduledFunctions[timeoutKey]; - if (scheduledFunc != jasmine.undefined && - scheduledFunc.runAtMillis >= oldMillis && - scheduledFunc.runAtMillis <= nowMillis) { - funcsToRun.push(scheduledFunc); - this.scheduledFunctions[timeoutKey] = jasmine.undefined; - } - } - - if (funcsToRun.length > 0) { - funcsToRun.sort(function(a, b) { - return a.runAtMillis - b.runAtMillis; - }); - for (var i = 0; i < funcsToRun.length; ++i) { - try { - var funcToRun = funcsToRun[i]; - this.nowMillis = funcToRun.runAtMillis; - funcToRun.funcToCall(); - if (funcToRun.recurring) { - this.scheduleFunction(funcToRun.timeoutKey, - funcToRun.funcToCall, - funcToRun.millis, - true); - } - } catch(e) { - } - } - this.runFunctionsWithinRange(oldMillis, nowMillis); - } -}; - -jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { - this.scheduledFunctions[timeoutKey] = { - runAtMillis: this.nowMillis + millis, - funcToCall: funcToCall, - recurring: recurring, - timeoutKey: timeoutKey, - millis: millis - }; -}; - -/** - * @namespace - */ -jasmine.Clock = { - defaultFakeTimer: new jasmine.FakeTimer(), - - reset: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.reset(); - }, - - tick: function(millis) { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.tick(millis); - }, - - runFunctionsWithinRange: function(oldMillis, nowMillis) { - jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); - }, - - scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { - jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); - }, - - useMock: function() { - if (!jasmine.Clock.isInstalled()) { - var spec = jasmine.getEnv().currentSpec; - spec.after(jasmine.Clock.uninstallMock); - - jasmine.Clock.installMock(); - } - }, - - installMock: function() { - jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; - }, - - uninstallMock: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.installed = jasmine.Clock.real; - }, - - real: { - setTimeout: jasmine.getGlobal().setTimeout, - clearTimeout: jasmine.getGlobal().clearTimeout, - setInterval: jasmine.getGlobal().setInterval, - clearInterval: jasmine.getGlobal().clearInterval - }, - - assertInstalled: function() { - if (!jasmine.Clock.isInstalled()) { - throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); - } - }, - - isInstalled: function() { - return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; - }, - - installed: null -}; -jasmine.Clock.installed = jasmine.Clock.real; - -//else for IE support -jasmine.getGlobal().setTimeout = function(funcToCall, millis) { - if (jasmine.Clock.installed.setTimeout.apply) { - return jasmine.Clock.installed.setTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.setTimeout(funcToCall, millis); - } -}; - -jasmine.getGlobal().setInterval = function(funcToCall, millis) { - if (jasmine.Clock.installed.setInterval.apply) { - return jasmine.Clock.installed.setInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.setInterval(funcToCall, millis); - } -}; - -jasmine.getGlobal().clearTimeout = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearTimeout(timeoutKey); - } -}; - -jasmine.getGlobal().clearInterval = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearInterval(timeoutKey); - } -}; - -/** - * @constructor - */ -jasmine.MultiReporter = function() { - this.subReporters_ = []; -}; -jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); - -jasmine.MultiReporter.prototype.addReporter = function(reporter) { - this.subReporters_.push(reporter); -}; - -(function() { - var functionNames = [ - "reportRunnerStarting", - "reportRunnerResults", - "reportSuiteResults", - "reportSpecStarting", - "reportSpecResults", - "log" - ]; - for (var i = 0; i < functionNames.length; i++) { - var functionName = functionNames[i]; - jasmine.MultiReporter.prototype[functionName] = (function(functionName) { - return function() { - for (var j = 0; j < this.subReporters_.length; j++) { - var subReporter = this.subReporters_[j]; - if (subReporter[functionName]) { - subReporter[functionName].apply(subReporter, arguments); - } - } - }; - })(functionName); - } -})(); -/** - * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults - * - * @constructor - */ -jasmine.NestedResults = function() { - /** - * The total count of results - */ - this.totalCount = 0; - /** - * Number of passed results - */ - this.passedCount = 0; - /** - * Number of failed results - */ - this.failedCount = 0; - /** - * Was this suite/spec skipped? - */ - this.skipped = false; - /** - * @ignore - */ - this.items_ = []; -}; - -/** - * Roll up the result counts. - * - * @param result - */ -jasmine.NestedResults.prototype.rollupCounts = function(result) { - this.totalCount += result.totalCount; - this.passedCount += result.passedCount; - this.failedCount += result.failedCount; -}; - -/** - * Adds a log message. - * @param values Array of message parts which will be concatenated later. - */ -jasmine.NestedResults.prototype.log = function(values) { - this.items_.push(new jasmine.MessageResult(values)); -}; - -/** - * Getter for the results: message & results. - */ -jasmine.NestedResults.prototype.getItems = function() { - return this.items_; -}; - -/** - * Adds a result, tracking counts (total, passed, & failed) - * @param {jasmine.ExpectationResult|jasmine.NestedResults} result - */ -jasmine.NestedResults.prototype.addResult = function(result) { - if (result.type != 'log') { - if (result.items_) { - this.rollupCounts(result); - } else { - this.totalCount++; - if (result.passed()) { - this.passedCount++; - } else { - this.failedCount++; - } - } - } - this.items_.push(result); -}; - -/** - * @returns {Boolean} True if everything below passed - */ -jasmine.NestedResults.prototype.passed = function() { - return this.passedCount === this.totalCount; -}; /** * Base class for pretty printing for expectation results. */ @@ -2002,514 +1520,202 @@ jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { jasmine.StringPrettyPrinter.prototype.append = function(value) { this.string += value; }; -jasmine.Queue = function(env) { - this.env = env; - - // parallel to blocks. each true value in this array means the block will - // get executed even if we abort - this.ensured = []; - this.blocks = []; - this.running = false; - this.index = 0; - this.offset = 0; - this.abort = false; +jasmine.QueueRunner = function(attrs) { + this.fns = attrs.fns || []; + this.onComplete = attrs.onComplete || function() {}; + this.encourageGC = attrs.encourageGC || function(fn) {fn()}; + this.onException = attrs.onException || function() {}; + this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; }; -jasmine.Queue.prototype.addBefore = function(block, ensure) { - if (ensure === jasmine.undefined) { - ensure = false; - } - - this.blocks.unshift(block); - this.ensured.unshift(ensure); +jasmine.QueueRunner.prototype.execute = function() { + this.run(this.fns, 0) }; -jasmine.Queue.prototype.add = function(block, ensure) { - if (ensure === jasmine.undefined) { - ensure = false; - } - - this.blocks.push(block); - this.ensured.push(ensure); -}; - -jasmine.Queue.prototype.insertNext = function(block, ensure) { - if (ensure === jasmine.undefined) { - ensure = false; - } - - this.ensured.splice((this.index + this.offset + 1), 0, ensure); - this.blocks.splice((this.index + this.offset + 1), 0, block); - this.offset++; -}; - -jasmine.Queue.prototype.start = function(onComplete) { - this.running = true; - this.onComplete = onComplete; - this.next_(); -}; - -jasmine.Queue.prototype.isRunning = function() { - return this.running; -}; - -jasmine.Queue.LOOP_DONT_RECURSE = true; - -jasmine.Queue.prototype.next_ = function() { - var self = this; - var goAgain = true; - - while (goAgain) { - goAgain = false; - - if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { - var calledSynchronously = true; - var completedSynchronously = false; - - var onComplete = function () { - if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { - completedSynchronously = true; - return; - } - - if (self.blocks[self.index].abort) { - self.abort = true; - } - - self.offset = 0; - self.index++; - - var now = new Date().getTime(); - if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { - self.env.lastUpdate = now; - self.env.setTimeout(function() { - self.next_(); - }, 0); - } else { - if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { - goAgain = true; - } else { - self.next_(); - } - } - }; - self.blocks[self.index].execute(onComplete); - - calledSynchronously = false; - if (completedSynchronously) { - onComplete(); - } - - } else { - self.running = false; - if (self.onComplete) { - self.onComplete(); - } - } - } -}; - -jasmine.Queue.prototype.results = function() { - var results = new jasmine.NestedResults(); - for (var i = 0; i < this.blocks.length; i++) { - if (this.blocks[i].results) { - results.addResult(this.blocks[i].results()); - } - } - return results; -}; - - -/** - * Runner - * - * @constructor - * @param {jasmine.Env} env - */ -jasmine.Runner = function(env) { - var self = this; - self.env = env; - self.queue = new jasmine.Queue(env); - self.before_ = []; - self.after_ = []; - self.suites_ = []; -}; - -jasmine.Runner.prototype.execute = function() { - var self = this; - if (self.env.reporter.reportRunnerStarting) { - self.env.reporter.reportRunnerStarting(this); - } - self.queue.start(function () { - self.finishCallback(); - }); -}; - -jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.splice(0,0,beforeEachFunction); -}; - -jasmine.Runner.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.splice(0,0,afterEachFunction); -}; - - -jasmine.Runner.prototype.finishCallback = function() { - this.env.reporter.reportRunnerResults(this); -}; - -jasmine.Runner.prototype.addSuite = function(suite) { - this.suites_.push(suite); -}; - -jasmine.Runner.prototype.add = function(block) { - if (block instanceof jasmine.Suite) { - this.addSuite(block); - } - this.queue.add(block); -}; - -jasmine.Runner.prototype.specs = function () { - var suites = this.suites(); - var specs = []; - for (var i = 0; i < suites.length; i++) { - specs = specs.concat(suites[i].specs()); - } - return specs; -}; - -jasmine.Runner.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Runner.prototype.topLevelSuites = function() { - var topLevelSuites = []; - for (var i = 0; i < this.suites_.length; i++) { - if (!this.suites_[i].parentSuite) { - topLevelSuites.push(this.suites_[i]); - } - } - return topLevelSuites; -}; - -jasmine.Runner.prototype.results = function() { - return this.queue.results(); -}; -/** - * Internal representation of a Jasmine specification, or test. - * - * @constructor - * @param {jasmine.Env} env - * @param {jasmine.Suite} suite - * @param {String} description - */ -jasmine.Spec = function(env, suite, description) { - if (!env) { - throw new Error('jasmine.Env() required'); - } - if (!suite) { - throw new Error('jasmine.Suite() required'); - } - var spec = this; - spec.id = env.nextSpecId ? env.nextSpecId() : null; - spec.env = env; - spec.suite = suite; - spec.description = description; - spec.queue = new jasmine.Queue(env); - - spec.afterCallbacks = []; - spec.spies_ = []; - - spec.results_ = new jasmine.NestedResults(); - spec.results_.description = description; - spec.matchersClass = null; -}; - -jasmine.Spec.prototype.getFullName = function() { - return this.suite.getFullName() + ' ' + this.description + '.'; -}; - - -jasmine.Spec.prototype.results = function() { - return this.results_; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.Spec.prototype.log = function() { - return this.results_.log(arguments); -}; - -jasmine.Spec.prototype.runs = function (func) { - var block = new jasmine.Block(this.env, func, this); - this.addToQueue(block); - return this; -}; - -jasmine.Spec.prototype.addToQueue = function (block) { - if (this.queue.isRunning()) { - this.queue.insertNext(block); - } else { - this.queue.add(block); - } -}; - -/** - * @param {jasmine.ExpectationResult} result - */ -jasmine.Spec.prototype.addMatcherResult = function(result) { - this.results_.addResult(result); -}; - -jasmine.Spec.prototype.expect = function(actual) { - var positive = new (this.getMatchersClass_())(this.env, actual, this); - positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); - return positive; -}; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -jasmine.Spec.prototype.waits = function(timeout) { - var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); - this.addToQueue(waitsFunc); - return this; -}; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - var latchFunction_ = null; - var optional_timeoutMessage_ = null; - var optional_timeout_ = null; - - for (var i = 0; i < arguments.length; i++) { - var arg = arguments[i]; - switch (typeof arg) { - case 'function': - latchFunction_ = arg; - break; - case 'string': - optional_timeoutMessage_ = arg; - break; - case 'number': - optional_timeout_ = arg; - break; - } - } - - var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); - this.addToQueue(waitsForFunc); - return this; -}; - -jasmine.Spec.prototype.fail = function (e) { - var expectationResult = new jasmine.ExpectationResult({ - passed: false, - message: e ? jasmine.util.formatException(e) : 'Exception', - trace: { stack: e.stack } - }); - this.results_.addResult(expectationResult); -}; - -jasmine.Spec.prototype.getMatchersClass_ = function() { - return this.matchersClass || this.env.matchersClass; -}; - -jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { - var parent = this.getMatchersClass_(); - var newMatchersClass = function() { - parent.apply(this, arguments); - }; - jasmine.util.inherit(newMatchersClass, parent); - jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); - this.matchersClass = newMatchersClass; -}; - -jasmine.Spec.prototype.finishCallback = function() { - this.env.reporter.reportSpecResults(this); -}; - -jasmine.Spec.prototype.finish = function(onComplete) { - this.removeAllSpies(); - this.finishCallback(); - if (onComplete) { - onComplete(); - } -}; - -jasmine.Spec.prototype.after = function(doAfter) { - if (this.queue.isRunning()) { - this.queue.add(new jasmine.Block(this.env, doAfter, this), true); - } else { - this.afterCallbacks.unshift(doAfter); - } -}; - -jasmine.Spec.prototype.execute = function(onComplete) { - var spec = this; - if (!spec.env.specFilter(spec)) { - spec.results_.skipped = true; - spec.finish(onComplete); +jasmine.QueueRunner.prototype.run = function(fns, index) { + if (index >= fns.length) { + this.encourageGC(this.onComplete); return; } - this.env.reporter.reportSpecStarting(this); - - spec.env.currentSpec = spec; - - spec.addBeforesAndAftersToQueue(); - - spec.queue.start(function () { - spec.finish(onComplete); - }); -}; - -jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { - var runner = this.env.currentRunner(); - var i; - - for (var suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); - } - } - for (i = 0; i < runner.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); - } - for (i = 0; i < this.afterCallbacks.length; i++) { - this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true); - } - for (suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true); - } - } - for (i = 0; i < runner.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true); - } -}; - -jasmine.Spec.prototype.explodes = function() { - throw 'explodes function should not have been called'; -}; - -jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { - if (obj == jasmine.undefined) { - throw "spyOn could not find an object to spy upon for " + methodName + "()"; - } - - if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { - throw methodName + '() method does not exist'; - } - - if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { - throw new Error(methodName + ' has already been spied upon'); - } - - var spyObj = jasmine.createSpy(methodName); - - this.spies_.push(spyObj); - spyObj.baseObj = obj; - spyObj.methodName = methodName; - spyObj.originalValue = obj[methodName]; - - obj[methodName] = spyObj; - - return spyObj; -}; - -jasmine.Spec.prototype.removeAllSpies = function() { - for (var i = 0; i < this.spies_.length; i++) { - var spy = this.spies_[i]; - spy.baseObj[spy.methodName] = spy.originalValue; - } - this.spies_ = []; -}; - -/** - * Internal representation of a Jasmine suite. - * - * @constructor - * @param {jasmine.Env} env - * @param {String} description - * @param {Function} specDefinitions - * @param {jasmine.Suite} parentSuite - */ -jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + var fn = fns[index]; var self = this; - self.id = env.nextSuiteId ? env.nextSuiteId() : null; - self.description = description; - self.queue = new jasmine.Queue(env); - self.parentSuite = parentSuite; - self.env = env; - self.before_ = []; - self.after_ = []; - self.children_ = []; - self.suites_ = []; - self.specs_ = []; + if (fn.length > 0) { + attempt(function() { fn.call(self, function() { self.run(fns, index + 1) }) }); + } else { + attempt(function() { fn.call(self); }); + self.run(fns, index + 1); + } + + function attempt(fn) { + try { + fn(); + } catch (e) { + self.onException(e); + if (!self.catchingExceptions()) { + //TODO: set a var when we catch an exception and + //use a finally block to close the loop in a nice way.. + throw e; + } + } + } +}; +jasmine.Spec = function(attrs) { + this.encounteredExpectations = false; + this.expectationFactory = attrs.expectationFactory; + this.resultCallback = attrs.resultCallback || function() {}; + this.id = attrs.id; + this.description = attrs.description || ''; + this.fn = attrs.fn; + this.beforeFns = attrs.beforeFns || function() {}; + this.afterFns = attrs.afterFns || function() {}; + this.catchingExceptions = attrs.catchingExceptions; + this.onStart = attrs.onStart || function() {}; + this.exceptionFormatter = attrs.exceptionFormatter || function() {}; + this.getSpecName = attrs.getSpecName || function() { return ''; }; + this.expectationResultFactory = attrs.expectationResultFactory || function() {}; + this.queueRunner = attrs.queueRunner || { execute: function() {}}; + this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + status: this.status(), + failedExpectations: [] + }; +}; + +jasmine.Spec.prototype.addExpectationResult = function(passed, data) { + this.encounteredExpectations = true; + if (!passed) { + this.result.failedExpectations.push(data); + } +}; + +jasmine.Spec.prototype.expect = function(actual) { + return this.expectationFactory(actual, this); +}; + +jasmine.Spec.prototype.execute = function(onComplete) { + var self = this; + + if (this.disabled) { + complete(); + return; + } + + var befores = this.beforeFns() || [], + afters = this.afterFns() || []; + var allFns = befores.concat(this.fn).concat(afters); + + this.onStart(this); + this.queueRunner({ + fns: allFns, + onException: function(e) { + self.addExpectationResult(false, self.expectationResultFactory({ + matcherName: "", + passed: false, + expected: "", + actual: "", + message: self.exceptionFormatter(e), + trace: e + })); + }, + onComplete: complete + }); + + function complete() { + self.result.status = self.status(); + self.resultCallback(self.result); + + if (onComplete) { + onComplete(); + } + } +}; + +jasmine.Spec.prototype.disable = function() { + this.disabled = true; +}; + +jasmine.Spec.prototype.status = function() { + if (this.disabled) { + return 'disabled'; + } + + if (!this.encounteredExpectations) { + return null; + } + + if (this.result.failedExpectations.length > 0) { + return 'failed'; + } else { + return 'passed'; + } +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.getSpecName(this); +} +jasmine.Suite = function(attrs) { + this.env = attrs.env; + this.id = attrs.id; + this.parentSuite = attrs.parentSuite; + this.description = attrs.description; + this.onStart = attrs.onStart || function() {}; + this.completeCallback = attrs.completeCallback || function() {}; + this.resultCallback = attrs.resultCallback || function() {}; + this.encourageGC = attrs.encourageGC || function(fn) {fn();}; + + this.beforeFns = []; + this.afterFns = []; + this.queueRunner = attrs.queueRunner || function() {}; + this.disabled = false; + + this.children_ = []; // TODO: rename + this.suites = []; // TODO: needed? + this.specs = []; // TODO: needed? + + this.result = { + id: this.id, + status: this.disabled ? 'disabled' : '', + description: this.description, + fullName: this.getFullName() + }; }; jasmine.Suite.prototype.getFullName = function() { var fullName = this.description; for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { - fullName = parentSuite.description + ' ' + fullName; + if (parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } } return fullName; }; -jasmine.Suite.prototype.finish = function(onComplete) { - this.env.reporter.reportSuiteResults(this); - this.finished = true; - if (typeof(onComplete) == 'function') { - onComplete(); - } +jasmine.Suite.prototype.disable = function() { + this.disabled = true; }; -jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.unshift(beforeEachFunction); +jasmine.Suite.prototype.beforeEach = function(fn) { + this.beforeFns.unshift(fn); }; -jasmine.Suite.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.unshift(afterEachFunction); +jasmine.Suite.prototype.afterEach = function(fn) { + this.afterFns.unshift(fn); }; -jasmine.Suite.prototype.results = function() { - return this.queue.results(); +jasmine.Suite.prototype.addSpec = function(spec) { + this.children_.push(spec); + this.specs.push(spec); // TODO: needed? }; -jasmine.Suite.prototype.add = function(suiteOrSpec) { - this.children_.push(suiteOrSpec); - if (suiteOrSpec instanceof jasmine.Suite) { - this.suites_.push(suiteOrSpec); - this.env.currentRunner().addSuite(suiteOrSpec); - } else { - this.specs_.push(suiteOrSpec); - } - this.queue.add(suiteOrSpec); -}; - -jasmine.Suite.prototype.specs = function() { - return this.specs_; -}; - -jasmine.Suite.prototype.suites = function() { - return this.suites_; +jasmine.Suite.prototype.addSuite = function(suite) { + suite.parentSuite = this; + this.children_.push(suite); + this.suites.push(suite); // TODO: needed? }; jasmine.Suite.prototype.children = function() { @@ -2518,80 +1724,264 @@ jasmine.Suite.prototype.children = function() { jasmine.Suite.prototype.execute = function(onComplete) { var self = this; - this.queue.start(function () { - self.finish(onComplete); - }); -}; -jasmine.WaitsBlock = function(env, timeout, spec) { - this.timeout = timeout; - jasmine.Block.call(this, env, null, spec); -}; - -jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); - -jasmine.WaitsBlock.prototype.execute = function (onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); - } - this.env.setTimeout(function () { - onComplete(); - }, this.timeout); -}; -/** - * A block which waits for some condition to become true, with timeout. - * - * @constructor - * @extends jasmine.Block - * @param {jasmine.Env} env The Jasmine environment. - * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. - * @param {Function} latchFunction A function which returns true when the desired condition has been met. - * @param {String} message The message to display if the desired condition hasn't been met within the given time period. - * @param {jasmine.Spec} spec The Jasmine spec. - */ -jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { - this.timeout = timeout || env.defaultTimeoutInterval; - this.latchFunction = latchFunction; - this.message = message; - this.totalTimeSpentWaitingForLatch = 0; - jasmine.Block.call(this, env, null, spec); -}; -jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); - -jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; - -jasmine.WaitsForBlock.prototype.execute = function(onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); - } - var latchFunctionResult; - try { - latchFunctionResult = this.latchFunction.apply(this.spec); - } catch (e) { - this.spec.fail(e); - onComplete(); + if (this.disabled) { + complete(); return; } - if (latchFunctionResult) { - onComplete(); - } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { - var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); - this.spec.fail({ - name: 'timeout', - message: message - }); + var allFns = [], + children = this.children_; - this.abort = true; - onComplete(); - } else { - this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; - var self = this; - this.env.setTimeout(function() { - self.execute(onComplete); - }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + for (var i = 0; i < children.length; i++) { + allFns.push(wrapChild(children[i])); + + function wrapChild(child) { + return function(done) { + child.execute(done); + } + } + } + + this.onStart(this); + + this.queueRunner({ + fns: allFns, + onComplete: complete + }); + + function complete() { + self.resultCallback(self.result); + + if (onComplete) { + onComplete(); + } } }; +jasmine.Clock = function(global, delayedFunctionScheduler) { + var self = this, + realTimingFunctions = { + setTimeout: global.setTimeout, + clearTimeout: global.clearTimeout, + setInterval: global.setInterval, + clearInterval: global.clearInterval + }, + fakeTimingFunctions = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval + }, + timer = realTimingFunctions, + installed = false; + self.install = function() { + installed = true; + timer = fakeTimingFunctions; + }; + + self.uninstall = function() { + delayedFunctionScheduler.reset(); + installed = false; + timer = realTimingFunctions; + }; + + self.setTimeout = function(fn, delay, params) { + if (legacyIE()) { + if (arguments.length > 2) { + throw new Error("IE < 9 cannot support extra params to setTimeout without a polyfill"); + } + return timer.setTimeout(fn, delay); + } + return timer.setTimeout.apply(null, arguments); + }; + + self.setInterval = function(fn, delay, params) { + if (legacyIE()) { + if (arguments.length > 2) { + throw new Error("IE < 9 cannot support extra params to setInterval without a polyfill"); + } + return timer.setInterval(fn, delay); + } + return timer.setInterval.apply(null, arguments); + }; + + self.clearTimeout = function(id) { + return timer.clearTimeout(id); + }; + + self.clearInterval = function(id) { + return timer.clearInterval(id); + }; + + self.tick = function(millis) { + if (installed) { + delayedFunctionScheduler.tick(millis) + } else { + throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); + } + }; + + return self; + + function legacyIE() { + //if these methods are polyfilled, apply will be present + //TODO: it may be difficult to load the polyfill before jasmine loads + //(env should be new-ed inside of onload) + return !(global.setTimeout || global.setInterval).apply; + } + + function setTimeout(fn, delay) { + return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); + } + + function clearTimeout(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function setInterval(fn, interval) { + return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); + } + + function clearInterval(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function argSlice(argsObj, n) { + return Array.prototype.slice.call(argsObj, 2); + } +}; +jasmine.DelayedFunctionScheduler = function() { + var self = this; + var scheduledFunctions = {}; + var currentTime = 0; + var delayedFnCount = 0; + + self.tick = function(millis) { + runFunctionsWithinRange(currentTime, currentTime + millis); + currentTime = currentTime + millis; + }; + + self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) { + timeoutKey = timeoutKey || ++delayedFnCount; + runAtMillis = runAtMillis || (currentTime + millis); + scheduledFunctions[timeoutKey] = { + runAtMillis: runAtMillis, + funcToCall: funcToCall, + recurring: recurring, + params: params, + timeoutKey: timeoutKey, + millis: millis + }; + return timeoutKey; + }; + + self.removeFunctionWithId = function(timeoutKey) { + delete scheduledFunctions[timeoutKey]; + }; + + self.reset = function() { + currentTime = 0; + scheduledFunctions = {}; + delayedFnCount = 0; + }; + + return self; + + + //finds/dupes functions within range and removes them. + function functionsWithinRange(startMillis, endMillis) { + var fnsToRun = []; + for (var timeoutKey in scheduledFunctions) { + var scheduledFunc = scheduledFunctions[timeoutKey]; + if (scheduledFunc && + scheduledFunc.runAtMillis >= startMillis && + scheduledFunc.runAtMillis <= endMillis) { + //remove fn -- we'll reschedule later if it is recurring. + self.removeFunctionWithId(timeoutKey); + if (!scheduledFunc.recurring) { + fnsToRun.push(scheduledFunc); // schedules each function only once + } else { + fnsToRun.push(buildNthInstanceOf(scheduledFunc, 0)); + var additionalTimesFnRunsInRange = + Math.floor((endMillis - scheduledFunc.runAtMillis) / scheduledFunc.millis); + for (var i = 0; i < additionalTimesFnRunsInRange; i++) { + fnsToRun.push(buildNthInstanceOf(scheduledFunc, i + 1)); + } + reschedule(buildNthInstanceOf(scheduledFunc, additionalTimesFnRunsInRange)); + } + } + } + + return fnsToRun; + } + + function buildNthInstanceOf(scheduledFunc, n) { + return { + runAtMillis: scheduledFunc.runAtMillis + (scheduledFunc.millis * n), + funcToCall: scheduledFunc.funcToCall, + params: scheduledFunc.params, + millis: scheduledFunc.millis, + recurring: scheduledFunc.recurring, + timeoutKey: scheduledFunc.timeoutKey + }; + } + + function reschedule(scheduledFn) { + self.scheduleFunction(scheduledFn.funcToCall, + scheduledFn.millis, + scheduledFn.params, + true, + scheduledFn.timeoutKey, + scheduledFn.runAtMillis + scheduledFn.millis); + } + + + function runFunctionsWithinRange(startMillis, endMillis) { + var funcsToRun = functionsWithinRange(startMillis, endMillis); + if (funcsToRun.length === 0) { + return; + } + + funcsToRun.sort(function(a, b) { + return a.runAtMillis - b.runAtMillis; + }); + + for (var i = 0; i < funcsToRun.length; ++i) { + var funcToRun = funcsToRun[i]; + funcToRun.funcToCall.apply(null, funcToRun.params); + } + } +}; +jasmine.ReportDispatcher = function(methods) { + + var dispatchedMethods = methods || []; + + for (var i = 0; i < dispatchedMethods.length; i++) { + var method = dispatchedMethods[i]; + this[method] = function(m) { + return function() { + dispatch(m, arguments); + }; + }(method); + } + + var reporters = []; + + this.addReporter = function(reporter) { + reporters.push(reporter); + }; + + return this; + + function dispatch(method, args) { + for (var i = 0; i < reporters.length; i++) { + var reporter = reporters[i]; + if (reporter[method]) { + reporter[method].apply(reporter, args); + } + } + } +}; jasmine.version_= { "major": 1, "minor": 3, diff --git a/pages b/pages deleted file mode 160000 index dae7b20e..00000000 --- a/pages +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dae7b20e1c018830aead62fec77da88d1affe91f diff --git a/spec/console/ConsoleReporterSpec.js b/spec/console/ConsoleReporterSpec.js index 3dddc413..07ca1427 100644 --- a/spec/console/ConsoleReporterSpec.js +++ b/spec/console/ConsoleReporterSpec.js @@ -1,451 +1,207 @@ describe("ConsoleReporter", function() { - //keep these literal. otherwise the test loses value as a test. - function green(str) { - return '\033[32m' + str + '\033[0m'; - } - - function red(str) { - return '\033[31m' + str + '\033[0m'; - } - - function yellow(str) { - return '\033[33m' + str + '\033[0m'; - } - - function prefixGreen(str) { - return '\033[32m' + str; - } - - function prefixRed(str) { - return '\033[31m' + str; - } - - var newline = "\n"; - - var passingSpec = { - results: function() { - return { - passed: function() { - return true; - } - }; - } - }, - failingSpec = { - results: function() { - return { - passed: function() { - return false; - } - }; - } - }, - skippedSpec = { - results: function() { - return {skipped: true}; - } - }, - passingRun = { - specs: function() { - return [null, null, null]; - }, - results: function() { - return {failedCount: 0, items_: [null, null, null]}; - } - }, - failingRun = { - specs: function() { - return [null, null, null]; - }, - results: function() { - return { - failedCount: 7, items_: [null, null, null]}; - } - }; - - function repeatedlyInvoke(f, times) { - for (var i = 0; i < times; i++) f(times + 1); - } - - function repeat(thing, times) { - var arr = []; - for (var i = 0; i < times; i++) arr.push(thing); - return arr; - } - - function simulateRun(reporter, specResults, suiteResults, finalRunner, startTime, endTime) { - reporter.reportRunnerStarting(); - for (var i = 0; i < specResults.length; i++) { - reporter.reportSpecResults(specResults[i]); - } - for (i = 0; i < suiteResults.length; i++) { - reporter.reportSuiteResults(suiteResults[i]); - } - reporter.runnerStartTime = startTime; - reporter.now = function() { - return endTime; - }; - reporter.reportRunnerResults(finalRunner); - } - - var reporter, out, done; + var out; beforeEach(function() { out = (function() { var output = ""; return { - print:function(str) { + print: function(str) { output += str; }, - getOutput:function() { + getOutput: function() { return output; }, clear: function() { output = ""; } }; - })(); - - done = false; - reporter = new jasmine.ConsoleReporter(out.print, function(runner) { - done = true - }); + }()); }); - - describe('Integration', function() { - it("prints the proper output under a pass scenario - small numbers.", function() { - simulateRun(reporter, - repeat(passingSpec, 3), - [], - { - specs: function() { - return [null, null, null]; - }, - results:function() { - return { - items_: [null, null, null], - totalCount: 7, - failedCount: 0 - }; - } - }, - 1000, - 1777 - ); - - var output = out.getOutput(); - expect(output).toMatch(/^Started/); - expect(output).toMatch(/\.\.\./); - expect(output).toMatch(/3 specs, 0 failures/); + it("reports that the suite has started to the console", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print }); - it("prints the proper output under a pass scenario. large numbers.", function() { - simulateRun(reporter, - repeat(passingSpec, 57), - [], - { - specs: function() { - return [null, null, null]; - }, - results:function() { - return { - items_: [null, null, null], - totalCount: 7, - failedCount: 0 - }; - } - }, - 1000, - 1777); + reporter.jasmineStarted(); - var output = out.getOutput(); - expect(output).toMatch(/^Started/); - expect(output).toMatch(/\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\./); - expect(output).toMatch(/3 specs, 0 failures/); - }); - - it("prints the proper output under a failure scenario.", function() { - simulateRun(reporter, - [failingSpec, passingSpec, failingSpec], - [ - {description:"The oven", - results:function() { - return { - items_:[ - {failedCount:2, - description:"heats up", - items_:[ - {trace:{stack:"stack trace one\n second line"}}, - {trace:{stack:"stack trace two"}} - ]} - ] - }; - }}, - {description:"The washing machine", - results:function() { - return { - items_:[ - {failedCount:2, - description:"washes clothes", - items_:[ - {trace:{stack:"stack trace one"}} - ]} - ] - }; - }} - ], - { - specs: function() { - return [null, null, null]; - }, - results:function() { - return { - items_: [null, null, null], - totalCount: 7, - failedCount: 2 - }; - } - }, - 1000, - 1777); - - var output = out.getOutput(); - expect(output).toMatch(/^Started/); - expect(output).toMatch(/F\.F/); - expect(output).toMatch(/The oven heats up\n stack trace one\n second line\n stack trace two/); - expect(output).toMatch(/The washing machine washes clothes\n stack trace one/); - expect(output).toMatch(/3 specs, 2 failures/); - }); + expect(out.getOutput()).toEqual("Started\n"); }); - describe('When a Jasmine environment executes', function() { - beforeEach(function() { - reporter.reportRunnerStarting(); + it("reports a passing spec as a dot", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print }); - it("should print 'Started' to the console", function() { - expect(out.getOutput()).toEqual("Started" + newline); + reporter.specDone({status: "passed"}); + + expect(out.getOutput()).toEqual("."); + }); + + it("does not report a disabled spec", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print }); - describe('when a spec reports', function() { - beforeEach(function() { - out.clear(); - }); + reporter.specDone({status: "disabled"}); - it("prints a green dot if the spec passes", function() { - reporter.reportSpecResults(passingSpec); + expect(out.getOutput()).toEqual(""); + }); - expect(out.getOutput()).toMatch(/\./); - }); - - it("prints a red dot if the spec fails", function() { - reporter.reportSpecResults(failingSpec); - - expect(out.getOutput()).toMatch(/F/); - }); - - it("prints a yellow star if the spec was skipped", function() { - reporter.reportSpecResults(skippedSpec); - - expect(out.getOutput()).toMatch(/\*/); - }); + it("reports a failing spec as an 'F'", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print }); - describe('when a suite reports', function() { - var emptyResults; - beforeEach(function() { - emptyResults = function() { - return { - items_:[] - }; - }; + reporter.specDone({status: "failed"}); + + expect(out.getOutput()).toEqual("F"); + }); + + it("reports a summary when done (singluar spec and time)", function() { + var fakeNow = jasmine.createSpy('fake Date.now'), + reporter = new jasmine.ConsoleReporter({ + print: out.print, + now: fakeNow }); - it("remembers suite results", function() { - reporter.reportSuiteResults({description: "Oven", results: emptyResults}); - reporter.reportSuiteResults({description: "Mixer", results: emptyResults}); + fakeNow.andReturn(500); - expect(reporter.suiteResults[0].description).toEqual('Oven'); - expect(reporter.suiteResults[1].description).toEqual('Mixer'); + reporter.jasmineStarted(); + reporter.specDone({status: "passed"}); + fakeNow.andReturn(1500); + + out.clear(); + reporter.jasmineDone(); + + expect(out.getOutput()).toMatch(/1 spec, 0 failures/); + expect(out.getOutput()).toMatch("Finished in 1 second\n"); + }); + + it("reports a summary when done (pluralized specs and seconds)", function() { + var fakeNow = jasmine.createSpy('fake Date.now'), + reporter = new jasmine.ConsoleReporter({ + print: out.print, + now: fakeNow }); - it("creates a description out of the current suite and any parent suites", function() { - var grandparentSuite = { - description: "My house", - results: emptyResults - }; - var parentSuite = { - description: "kitchen", - parentSuite: grandparentSuite, - results: emptyResults - }; - reporter.reportSuiteResults({ description: "oven", parentSuite: parentSuite, results: emptyResults }); - - expect(reporter.suiteResults[0].description).toEqual("My house kitchen oven"); - }); - - it("gathers failing spec results from the suite - the spec must have a description.", function() { - reporter.reportSuiteResults({description:"Oven", - results: function() { - return { - items_:[ - { failedCount: 0, description: "specOne" }, - { failedCount: 99, description: "specTwo" }, - { failedCount: 0, description: "specThree" }, - { failedCount: 88, description: "specFour" }, - { failedCount: 3 } - ] - }; - }}); - - expect(reporter.suiteResults[0].failedSpecResults). - toEqual([ - { failedCount: 99, description: "specTwo" }, - { failedCount: 88, description: "specFour" } - ]); - }); - - }); - - describe('and finishes', function() { - - describe('when reporting spec failure information', function() { - - it("prints suite and spec descriptions together as a sentence", function() { - reporter.suiteResults = [ - {description:"The oven", failedSpecResults:[ - {description:"heats up", items_:[]}, - {description:"cleans itself", items_:[]} - ]}, - {description:"The mixer", failedSpecResults:[ - {description:"blends things together", items_:[]} - ]} - ]; - - reporter.reportRunnerResults(failingRun); - - expect(out.getOutput()).toContain("The oven heats up"); - expect(out.getOutput()).toContain("The oven cleans itself"); - expect(out.getOutput()).toContain("The mixer blends things together"); - }); - - it("prints stack trace of spec failure", function() { - reporter.suiteResults = [ - {description:"The oven", failedSpecResults:[ - {description:"heats up", - items_:[ - {trace:{stack:"stack trace one"}}, - {trace:{stack:"stack trace two"}} - ]} - ]} - ]; - - reporter.reportRunnerResults(failingRun); - - expect(out.getOutput()).toContain("The oven heats up"); - expect(out.getOutput()).toContain("stack trace one"); - expect(out.getOutput()).toContain("stack trace two"); - }); - - }); - - describe('when reporting the execution time', function() { - - it("prints the full finished message", function() { - reporter.now = function() { - return 1000; - }; - reporter.reportRunnerStarting(); - reporter.now = function() { - return 1777; - }; - reporter.reportRunnerResults(failingRun); - expect(out.getOutput()).toContain("Finished in 0.777 seconds"); - }); - - it("prints round time numbers correctly", function() { - function run(startTime, endTime) { - out.clear(); - reporter.runnerStartTime = startTime; - reporter.now = function() { - return endTime; - }; - reporter.reportRunnerResults(passingRun); + fakeNow.andReturn(500); + reporter.jasmineStarted(); + reporter.specDone({status: "passed"}); + reporter.specDone({ + status: "failed", + description: "with a failing spec", + fullName: "A suite with a failing spec", + failedExpectations: [ + { + passed: false, + message: "Expected true to be false.", + expected: false, + actual: true, + trace: { + stack: "foo\nbar\nbaz" } + } + ] + }); - run(1000, 11000); - expect(out.getOutput()).toContain("10 seconds"); + out.clear(); - run(1000, 2000); - expect(out.getOutput()).toContain("1 seconds"); + fakeNow.andReturn(600); + reporter.jasmineDone(); - run(1000, 1100); - expect(out.getOutput()).toContain("0.1 seconds"); + expect(out.getOutput()).toMatch(/2 specs, 1 failure/); + expect(out.getOutput()).toMatch("Finished in 0.1 seconds\n"); + }); - run(1000, 1010); - expect(out.getOutput()).toContain("0.01 seconds"); + it("reports a summary when done that includes stack traces for a failing suite", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print + }); - run(1000, 1001); - expect(out.getOutput()).toContain("0.001 seconds"); - }); + reporter.jasmineStarted(); + reporter.specDone({status: "passed"}); + reporter.specDone({ + status: "failed", + description: "with a failing spec", + fullName: "A suite with a failing spec", + failedExpectations: [ + { + passed: false, + message: "Expected true to be false.", + expected: false, + actual: true, + trace: { + stack: "foo bar baz" + } + } + ] + }); + + out.clear(); + + reporter.jasmineDone(); + + expect(out.getOutput()).toMatch(/foo bar baz/); + }); + + it("calls the onComplete callback when the suite is done", function() { + var onComplete = jasmine.createSpy('onComplete'), + reporter = new jasmine.ConsoleReporter({ + print: out.print, + onComplete: onComplete }); - describe("when reporting the results summary", function() { - it("prints statistics in green if there were no failures", function() { - reporter.reportRunnerResults({ - specs: function() { - return [null, null, null]; - }, - results:function() { - return {items_: [null, null, null], totalCount: 7, failedCount: 0}; - } - }); - expect(out.getOutput()). - toContain("3 specs, 0 failures"); - }); + reporter.jasmineDone(); - it("prints statistics in red if there was a failure", function() { - reporter.reportRunnerResults({ - specs: function() { - return [null, null, null]; - }, - results:function() { - return {items_: [null, null, null], totalCount: 7, failedCount: 3}; - } - }); - expect(out.getOutput()). - toContain("3 specs, 3 failures"); - }); + expect(onComplete).toHaveBeenCalled(); + }); - it("handles pluralization with 1's ones appropriately", function() { - reporter.reportRunnerResults({ - specs: function() { - return [null]; - }, - results:function() { - return {items_: [null], totalCount: 1, failedCount: 1}; - } - }); - expect(out.getOutput()). - toContain("1 spec, 1 failure"); - }); + + describe("with color", function() { + + it("reports that the suite has started to the console", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print, + showColors: true }); - describe("done callback", function() { - it("calls back when done", function() { - expect(done).toBeFalsy(); - reporter.reportRunnerResults({ - specs: function() { - return [null, null, null]; - }, - results:function() { - return {items_: [null, null, null], totalCount: 7, failedCount: 0}; - } - }); - expect(done).toBeTruthy(); - }); + reporter.jasmineStarted(); + + expect(out.getOutput()).toEqual("Started\n"); + }); + + it("reports a passing spec as a dot", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print, + showColors: true }); + + reporter.specDone({status: "passed"}); + + expect(out.getOutput()).toEqual("\033[32m.\033[0m"); + }); + + it("does not report a disabled spec", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print, + showColors: true + }); + + reporter.specDone({status: 'disabled'}); + + expect(out.getOutput()).toEqual(""); + }); + + it("reports a failing spec as an 'F'", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print, + showColors: true + }); + + reporter.specDone({status: 'failed'}); + + expect(out.getOutput()).toEqual("\033[31mF\033[0m"); }); }); -}); \ No newline at end of file +}); diff --git a/spec/core/BaseSpec.js b/spec/core/BaseSpec.js deleted file mode 100644 index 121a8815..00000000 --- a/spec/core/BaseSpec.js +++ /dev/null @@ -1,27 +0,0 @@ -describe("base.js", function() { - describe("jasmine.MessageResult", function() { - it("#toString should pretty-print and concatenate each part of the message", function() { - var values = ["log", "message", 123, {key: "value"}, "FTW!"]; - var messageResult = new jasmine.MessageResult(values); - expect(messageResult.toString()).toEqual("log message 123 { key : 'value' } FTW!"); - }); - }); - - describe("jasmine.log", function() { - it("should accept n arguments", function() { - spyOn(jasmine.getEnv().currentSpec, 'log'); - jasmine.log(1, 2, 3); - expect(jasmine.getEnv().currentSpec.log).toHaveBeenCalledWith(1, 2, 3); - }); - }); - - describe("jasmine.getGlobal", function() { - it("should return the global object", function() { - var globalObject = (function() { - return this; - })(); - - expect(jasmine.getGlobal()).toBe(globalObject); - }); - }); -}); diff --git a/spec/core/ClockSpec.js b/spec/core/ClockSpec.js new file mode 100644 index 00000000..84cbf0a3 --- /dev/null +++ b/spec/core/ClockSpec.js @@ -0,0 +1,294 @@ +describe("Clock", function() { + + it("calls the global setTimeout directly if Clock is not installed", function() { + var setTimeout = jasmine.createSpy('setTimeout'), + delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['scheduleFunction']), + global = { setTimeout: setTimeout }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.setTimeout(delayedFn, 0); + + expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled(); + expect(setTimeout).toHaveBeenCalledWith(delayedFn, 0); + }); + + it("schedules the delayed function with the fake timer", function() { + var setTimeout = jasmine.createSpy('setTimeout'), + scheduleFunction = jasmine.createSpy('scheduleFunction'), + delayedFunctionScheduler = {scheduleFunction: scheduleFunction}, + global = { setTimeout: setTimeout }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.install(); + clock.setTimeout(delayedFn, 0, 'a', 'b'); + + expect(setTimeout).not.toHaveBeenCalled(); + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b']); + }); + + it("returns an id for the delayed function", function() { + var setTimeout = jasmine.createSpy('setTimeout'), + scheduleId = 123, + scheduleFunction = jasmine.createSpy('scheduleFunction').andReturn(scheduleId), + delayedFunctionScheduler = {scheduleFunction: scheduleFunction}, + global = { setTimeout: setTimeout }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler), + timeoutId; + + clock.install(); + timeoutId = clock.setTimeout(delayedFn, 0); + + expect(timeoutId).toEqual(123); + }); + + it("calls the global clearTimeout directly if Clock is not installed", function() { + var clearTimeout = jasmine.createSpy('clearTimeout'), + delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['clearTimeout']), + global = { clearTimeout: clearTimeout }, + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.clearTimeout(123); + + expect(clearTimeout).toHaveBeenCalledWith(123); + }); + + it("clears the scheduled function with the scheduler", function() { + var clearTimeout = jasmine.createSpy('clearTimeout'), + delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['removeFunctionWithId']), + global = { setTimeout: clearTimeout }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.install(); + clock.clearTimeout(123); + + expect(clearTimeout).not.toHaveBeenCalled(); + expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith(123); + }); + + it("calls the global setInterval directly if Clock is not installed", function() { + var setInterval = jasmine.createSpy('setInterval'), + delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['scheduleFunction']), + global = { setInterval: setInterval }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.setInterval(delayedFn, 0); + + expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled(); + expect(setInterval).toHaveBeenCalledWith(delayedFn, 0); + }); + + it("schedules the delayed function with the fake timer", function() { + var setInterval = jasmine.createSpy('setInterval'), + scheduleFunction = jasmine.createSpy('scheduleFunction'), + delayedFunctionScheduler = {scheduleFunction: scheduleFunction}, + global = { setInterval: setInterval }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.install(); + clock.setInterval(delayedFn, 0, 'a', 'b'); + + expect(setInterval).not.toHaveBeenCalled(); + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b'], true); + }); + + it("returns an id for the delayed function", function() { + var setInterval = jasmine.createSpy('setInterval'), + scheduleId = 123, + scheduleFunction = jasmine.createSpy('scheduleFunction').andReturn(scheduleId), + delayedFunctionScheduler = {scheduleFunction: scheduleFunction}, + global = { setInterval: setInterval }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler), + intervalId; + + clock.install(); + intervalId = clock.setInterval(delayedFn, 0); + + expect(intervalId).toEqual(123); + }); + + it("calls the global clearInterval directly if Clock is not installed", function() { + var clearInterval = jasmine.createSpy('clearInterval'), + delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['clearInterval']), + global = { clearInterval: clearInterval }, + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.clearInterval(123); + + expect(clearInterval).toHaveBeenCalledWith(123); + }); + + it("clears the scheduled function with the scheduler", function() { + var clearInterval = jasmine.createSpy('clearInterval'), + delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['removeFunctionWithId']), + global = { setInterval: clearInterval }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.install(); + clock.clearInterval(123); + + expect(clearInterval).not.toHaveBeenCalled(); + expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith(123); + }); + + it("gives you a friendly reminder if the Clock is not installed and you tick", function() { + var clock = new jasmine.Clock({}, jasmine.createSpyObj('delayedFunctionScheduler', ['tick'])); + expect(function() { + clock.tick(50); + }).toThrow(); + }); + + it("can be uninstalled", function() { + var setTimeout = jasmine.createSpy('setTimeout'), + setInterval = jasmine.createSpy('setInterval'), + delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['scheduleFunction', 'tick', 'reset']), + global = { setTimeout: setTimeout, setInterval: setInterval }, + delayedFn = jasmine.createSpy('delayedFn'), + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + clock.install(); + clock.setTimeout(delayedFn, 0); + expect(setTimeout).not.toHaveBeenCalled(); + + clock.setInterval(delayedFn, 0); + expect(setInterval).not.toHaveBeenCalled(); + + expect(function() { + clock.tick(0); + }).not.toThrow(); + + clock.uninstall(); + + expect(delayedFunctionScheduler.reset).toHaveBeenCalled(); + + clock.setTimeout(delayedFn, 0); + + expect(setTimeout).toHaveBeenCalled(); + + clock.setInterval(delayedFn, 0); + expect(setInterval).toHaveBeenCalled(); + + expect(function() { + clock.tick(0); + }).toThrow(); + }); + + + it("on IE < 9, fails if extra args are passed to fake clock", function() { + //fail, because this would break in IE9. + var setTimeout = jasmine.createSpy('setTimeout'), + setInterval = jasmine.createSpy('setInterval'), + delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['scheduleFunction']), + fn = jasmine.createSpy('fn'), + global = { setTimeout: setTimeout, setInterval: setInterval }, + clock = new jasmine.Clock(global, delayedFunctionScheduler); + + setTimeout.apply = null; + setInterval.apply = null; + + clock.install(); + + clock.setTimeout(fn, 0); + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, []); + expect(function() { + clock.setTimeout(fn, 0, 'extra'); + }).toThrow(); + + clock.setInterval(fn, 0); + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], true); + expect(function() { + clock.setInterval(fn, 0, 'extra'); + }).toThrow(); + }); + +}); + +describe("Clock (acceptance)", function() { + it("can run setTimeouts/setIntervals synchronously", function() { + var delayedFn1 = jasmine.createSpy('delayedFn1'), + delayedFn2 = jasmine.createSpy('delayedFn2'), + delayedFn3 = jasmine.createSpy('delayedFn3'), + recurring1 = jasmine.createSpy('recurring1'), + delayedFunctionScheduler = new jasmine.DelayedFunctionScheduler(), + clock = new jasmine.Clock({setTimeout: setTimeout}, delayedFunctionScheduler); + + clock.install(); + + clock.setTimeout(delayedFn1, 0, 'some', 'arg'); + var intervalId = clock.setInterval(recurring1, 50, 'some', 'other', 'args'); + clock.setTimeout(delayedFn2, 100); + clock.setTimeout(delayedFn3, 200); + + expect(delayedFn1).not.toHaveBeenCalled(); + expect(delayedFn2).not.toHaveBeenCalled(); + expect(delayedFn3).not.toHaveBeenCalled(); + + clock.tick(0); + + expect(delayedFn1).toHaveBeenCalledWith('some', 'arg'); + expect(delayedFn2).not.toHaveBeenCalled(); + expect(delayedFn3).not.toHaveBeenCalled(); + + clock.tick(50); + + expect(recurring1).toHaveBeenCalledWith('some', 'other', 'args'); + expect(recurring1.callCount).toBe(1); + expect(delayedFn2).not.toHaveBeenCalled(); + expect(delayedFn3).not.toHaveBeenCalled(); + + clock.tick(50); + + expect(recurring1.callCount).toBe(2); + expect(delayedFn2).toHaveBeenCalled(); + expect(delayedFn3).not.toHaveBeenCalled(); + + clock.tick(100); + + expect(recurring1.callCount).toBe(4); + expect(delayedFn3).toHaveBeenCalled(); + + clock.clearInterval(intervalId); + clock.tick(50); + + expect(recurring1.callCount).toBe(4); + }); + + it("can clear a previously set timeout", function() { + var clearedFn = jasmine.createSpy('clearedFn'), + delayedFunctionScheduler = new jasmine.DelayedFunctionScheduler(), + clock = new jasmine.Clock({setTimeout: function() {}}, delayedFunctionScheduler), + timeoutId; + + clock.install(); + + timeoutId = clock.setTimeout(clearedFn, 100); + expect(clearedFn).not.toHaveBeenCalled(); + + clock.clearTimeout(timeoutId); + clock.tick(100); + + expect(clearedFn).not.toHaveBeenCalled(); + }); + + it("correctly schedules functions after the Clock has advanced", function() { + var delayedFn1 = jasmine.createSpy('delayedFn1'), + delayedFunctionScheduler = new jasmine.DelayedFunctionScheduler(), + clock = new jasmine.Clock({setTimeout: function(){}}, delayedFunctionScheduler); + + clock.install(); + + clock.tick(100); + clock.setTimeout(delayedFn1, 10, ['some', 'arg']); + clock.tick(5); + expect(delayedFn1).not.toHaveBeenCalled(); + clock.tick(5); + expect(delayedFn1).toHaveBeenCalled(); + }); +}); diff --git a/spec/core/CustomMatchersSpec.js b/spec/core/CustomMatchersSpec.js index 7a724b57..21214b90 100644 --- a/spec/core/CustomMatchersSpec.js +++ b/spec/core/CustomMatchersSpec.js @@ -1,97 +1,96 @@ -describe("Custom Matchers", function() { - var env; - var fakeTimer; - - beforeEach(function() { - env = new jasmine.Env(); - env.updateInterval = 0; - }); - - it("should be easy to add more matchers local to a spec, suite, etc.", function() { - var spec1, spec2, spec1Matcher, spec2Matcher; - var suite = env.describe('some suite', function() { - env.beforeEach(function() { - this.addMatchers({ - matcherForSuite: function(expected) { - this.message = "matcherForSuite: actual: " + this.actual + "; expected: " + expected; - return true; - } - }); - }); - - spec1 = env.it('spec with an expectation').runs(function () { - this.addMatchers({ - matcherForSpec: function(expected) { - this.message = "matcherForSpec: actual: " + this.actual + "; expected: " + expected; - return true; - } - }); - spec1Matcher = this.expect("xxx"); - }); - - spec2 = env.it('spec with failing expectation').runs(function () { - spec2Matcher = this.expect("yyy"); - }); - }); - - suite.execute(); - - spec1Matcher.matcherForSuite("expected"); - expect(spec1Matcher.message).toEqual("matcherForSuite: actual: xxx; expected: expected"); - spec1Matcher.matcherForSpec("expected"); - expect(spec1Matcher.message).toEqual("matcherForSpec: actual: xxx; expected: expected"); - - spec2Matcher.matcherForSuite("expected"); - expect(spec2Matcher.message).toEqual("matcherForSuite: actual: yyy; expected: expected"); - expect(spec2Matcher.matcherForSpec).toBe(jasmine.undefined); - }); - - it("should generate messages with the same rules as for regular matchers when this.report() is not called", function() { - var spec; - var suite = env.describe('some suite', function() { - spec = env.it('spec with an expectation').runs(function () { - this.addMatchers({ - toBeTrue: function() { - return this.actual === true; - } - }); - this.expect(true).toBeTrue(); - this.expect(false).toBeTrue(); - }); - }); - - suite.execute(); - var passResult = new jasmine.ExpectationResult({passed: true, matcherName: 'toBeTrue', - actual: true, expected: jasmine.undefined, message: "Passed." }); - var failResult = new jasmine.ExpectationResult({passed: false, matcherName: 'toBeTrue', - actual: false, expected: jasmine.undefined, message: "Expected false to be true." }); - failResult.trace = jasmine.any(Object); - expect(spec.results().getItems()).toEqual([passResult, failResult]); - }); - - it("should pass args", function() { - var matcherCallArgs = []; - var spec; - var suite = env.describe('some suite', function() { - spec = env.it('spec with an expectation').runs(function () { - this.addMatchers({ - toBeTrue: function() { - matcherCallArgs.push(jasmine.util.argsToArray(arguments)); - return this.actual === true; - } - }); - this.expect(true).toBeTrue(); - this.expect(false).toBeTrue('arg'); - this.expect(true).toBeTrue('arg1', 'arg2'); - }); - }); - - suite.execute(); - var results = spec.results().getItems(); - expect(results[0].expected).toEqual(jasmine.undefined); - expect(results[1].expected).toEqual('arg'); - expect(results[2].expected).toEqual(['arg1', 'arg2']); - - expect(matcherCallArgs).toEqual([[], ['arg'], ['arg1', 'arg2']]); - }); -}); \ No newline at end of file +////TODO: matchers should be add-able to the env, not to the spec. +//describe("Custom Matchers", function() { +// var env; +// var fakeTimer; +// +// beforeEach(function() { +// env = new jasmine.Env(); +// env.updateInterval = 0; +// }); +// +// it("should be easy to add more matchers local to a spec, suite, etc.", function() { +// var spec1, spec2, spec1Matcher, spec2Matcher; +// var suite = env.describe('some suite', function() { +// env.beforeEach(function() { +// this.addMatchers({ +// matcherForSuite: function(expected) { +// this.message = "matcherForSuite: actual: " + this.actual + "; expected: " + expected; +// return true; +// } +// }); +// }); +// +// spec1 = env.it('spec with an expectation').runs(function () { +// this.addMatchers({ +// matcherForSpec: function(expected) { +// this.message = "matcherForSpec: actual: " + this.actual + "; expected: " + expected; +// return true; +// } +// }); +// spec1Matcher = this.expect("xxx"); +// }); +// +// spec2 = env.it('spec with failing expectation').runs(function () { +// spec2Matcher = this.expect("yyy"); +// }); +// }); +// +// suite.execute(); +// +// spec1Matcher.matcherForSuite("expected"); +// expect(spec1Matcher.message).toEqual("matcherForSuite: actual: xxx; expected: expected"); +// spec1Matcher.matcherForSpec("expected"); +// expect(spec1Matcher.message).toEqual("matcherForSpec: actual: xxx; expected: expected"); +// +// spec2Matcher.matcherForSuite("expected"); +// expect(spec2Matcher.message).toEqual("matcherForSuite: actual: yyy; expected: expected"); +// expect(spec2Matcher.matcherForSpec).toBe(jasmine.undefined); +// }); +// +// it("should generate messages with the same rules as for regular matchers when this.report() is not called", function() { +// var spec; +// var suite = env.describe('some suite', function() { +// spec = env.it('spec with an expectation').runs(function () { +// this.addMatchers({ +// toBeTrue: function() { +// return this.actual === true; +// } +// }); +// this.expect(true).toBeTrue(); +// this.expect(false).toBeTrue(); +// }); +// }); +// +// suite.execute(); +// +// var results = spec.results().getItems(); +// expect(results[0].message).toEqual("Passed."); +// expect(results[1].message).toEqual("Expected false to be true."); +// }); +// +// it("should pass args", function() { +// var matcherCallArgs = []; +// var spec; +// var suite = env.describe('some suite', function() { +// spec = env.it('spec with an expectation').runs(function () { +// this.addMatchers({ +// toBeTrue: function() { +// matcherCallArgs.push(jasmine.util.argsToArray(arguments)); +// return this.actual === true; +// } +// }); +// this.expect(true).toBeTrue(); +// this.expect(false).toBeTrue('arg'); +// this.expect(true).toBeTrue('arg1', 'arg2'); +// }); +// }); +// +// suite.execute(); +// var results = spec.results().getItems(); +// expect(results[0].expected).toEqual(jasmine.undefined); +// expect(results[1].expected).toEqual('arg'); +// expect(results[2].expected).toEqual(['arg1', 'arg2']); +// +// expect(matcherCallArgs).toEqual([[], ['arg'], ['arg1', 'arg2']]); +// }); +//}); diff --git a/spec/core/DelayedFunctionSchedulerSpec.js b/spec/core/DelayedFunctionSchedulerSpec.js new file mode 100644 index 00000000..0615f493 --- /dev/null +++ b/spec/core/DelayedFunctionSchedulerSpec.js @@ -0,0 +1,142 @@ +describe("DelayedFunctionScheduler", function() { + it("schedules a function for later execution", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(), + fn = jasmine.createSpy('fn'); + + scheduler.scheduleFunction(fn, 0); + + expect(fn).not.toHaveBeenCalled(); + + scheduler.tick(0); + + expect(fn).toHaveBeenCalled(); + }); + + it("optionally passes params to scheduled functions", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(), + fn = jasmine.createSpy('fn'); + + scheduler.scheduleFunction(fn, 0, ['foo', 'bar']); + + expect(fn).not.toHaveBeenCalled(); + + scheduler.tick(0); + + expect(fn).toHaveBeenCalledWith('foo', 'bar'); + }); + + it("scheduled fns can optionally reoccur", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(), + fn = jasmine.createSpy('fn'); + + scheduler.scheduleFunction(fn, 20, [], true); + + expect(fn).not.toHaveBeenCalled(); + + scheduler.tick(20); + + expect(fn.callCount).toBe(1); + + scheduler.tick(40); + + expect(fn.callCount).toBe(3); + + scheduler.tick(21); + + expect(fn.callCount).toBe(4); + + }); + + it("increments scheduled fns ids unless one is passed", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(); + + expect(scheduler.scheduleFunction(function() { + }, 0)).toBe(1); + expect(scheduler.scheduleFunction(function() { + }, 0)).toBe(2); + expect(scheduler.scheduleFunction(function() { + }, 0, [], false, 123)).toBe(123); + expect(scheduler.scheduleFunction(function() { + }, 0)).toBe(3); + }); + + it("#removeFunctionWithId removes a previously scheduled function with a given id", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(), + fn = jasmine.createSpy('fn'), + timeoutKey; + + timeoutKey = scheduler.scheduleFunction(fn, 0); + + expect(fn).not.toHaveBeenCalled(); + + scheduler.removeFunctionWithId(timeoutKey); + + scheduler.tick(0); + + expect(fn).not.toHaveBeenCalled(); + }); + + it("reset removes scheduled functions", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(), + fn = jasmine.createSpy('fn'); + + scheduler.scheduleFunction(fn, 0); + + expect(fn).not.toHaveBeenCalled(); + + scheduler.reset(); + + scheduler.tick(0); + + expect(fn).not.toHaveBeenCalled(); + }); + + it("reset resets the returned ids", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(); + expect(scheduler.scheduleFunction(function() { }, 0)).toBe(1); + expect(scheduler.scheduleFunction(function() { }, 0, [], false, 123)).toBe(123); + + scheduler.reset(); + expect(scheduler.scheduleFunction(function() { }, 0)).toBe(1); + expect(scheduler.scheduleFunction(function() { }, 0, [], false, 123)).toBe(123); + }); + + it("reset resets the current tick time", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(), + fn = jasmine.createSpy('fn'); + + expect(fn).not.toHaveBeenCalled(); + + scheduler.tick(15); + scheduler.reset(); + + scheduler.scheduleFunction(fn, 20, [], false, 1, 20); + + scheduler.tick(5); + + expect(fn).not.toHaveBeenCalled(); + }); + + it("executes recurring functions interleaved with regular functions in the correct order", function() { + var scheduler = new jasmine.DelayedFunctionScheduler(), + fn = jasmine.createSpy('fn'), + recurringCallCount = 0, + recurring = jasmine.createSpy('recurring').andCallFake(function() { + recurringCallCount++; + if (recurringCallCount < 5) { + expect(fn).not.toHaveBeenCalled(); + } + }); + + scheduler.scheduleFunction(recurring, 10, [], true); + scheduler.scheduleFunction(fn, 50); + + scheduler.tick(60); + + expect(recurring).toHaveBeenCalled(); + expect(recurring.callCount).toBe(6); + expect(fn).toHaveBeenCalled(); + }); + +}); + diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 3bd67031..b9611489 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -1,12 +1,13 @@ -describe("jasmine.Env", function() { +// TODO: Fix these unit tests! +describe("Env", function() { var env; beforeEach(function() { env = new jasmine.Env(); env.updateInterval = 0; }); - describe('ids', function () { - it('nextSpecId should return consecutive integers, starting at 0', function () { + describe('ids', function() { + it('nextSpecId should return consecutive integers, starting at 0', function() { expect(env.nextSpecId()).toEqual(0); expect(env.nextSpecId()).toEqual(1); expect(env.nextSpecId()).toEqual(2); @@ -17,21 +18,21 @@ describe("jasmine.Env", function() { var fakeReporter; beforeEach(function() { - fakeReporter = jasmine.createSpyObj("fakeReporter", ["log"]); + fakeReporter = originalJasmine.createSpyObj("fakeReporter", ["jasmineStarted"]); }); - describe('version', function () { + describe('version', function() { var oldVersion; - beforeEach(function () { + beforeEach(function() { oldVersion = jasmine.version_; }); - afterEach(function () { + afterEach(function() { jasmine.version_ = oldVersion; }); - it('should raise an error if version is not set', function () { + it('should raise an error if version is not set', function() { jasmine.version_ = null; var exception; try { @@ -79,8 +80,8 @@ describe("jasmine.Env", function() { it("should allow reporters to be registered", function() { env.addReporter(fakeReporter); - env.reporter.log("message"); - expect(fakeReporter.log).toHaveBeenCalledWith("message"); + env.reporter.jasmineStarted(); + expect(fakeReporter.jasmineStarted).toHaveBeenCalled(); }); }); @@ -138,8 +139,12 @@ describe("jasmine.Env", function() { describe("even if there are several", function() { beforeEach(function() { - env.addEqualityTester(function(a, b) { return jasmine.undefined; }); - env.addEqualityTester(function(a, b) { return jasmine.undefined; }); + env.addEqualityTester(function(a, b) { + return jasmine.undefined; + }); + env.addEqualityTester(function(a, b) { + return jasmine.undefined; + }); }); it("should use normal equality rules", function() { @@ -151,9 +156,182 @@ describe("jasmine.Env", function() { it("should evaluate custom equality testers in the order they are declared", function() { isEqual = false; - env.addEqualityTester(function(a, b) { return true; }); + env.addEqualityTester(function(a, b) { + return true; + }); expect(env.equals_('abc', 'abc')).toBeFalsy(); }); }); }); }); + +describe("Env (integration)", function() { + + it("Suites execute as expected (no nesting)", function() { + var env = new jasmine.Env(), + calls = []; + + env.describe("A Suite", function() { + env.it("with a spec", function() { + calls.push("with a spec"); + }); + env.it("and another spec", function() { + calls.push("and another spec"); + }); + }); + + env.execute(); + + expect(calls).toEqual([ + "with a spec", + "and another spec" + ]); + }); + + it("Nested Suites execute as expected", function() { + var env = new jasmine.Env(), + calls = []; + + env.describe("Outer suite", function() { + env.it("an outer spec", function() { + calls.push('an outer spec') + }); + env.describe("Inner suite", function() { + env.it("an inner spec", function() { + calls.push('an inner spec'); + }); + env.it("another inner spec", function() { + calls.push('another inner spec'); + }); + }); + }); + + env.execute(); + + expect(calls).toEqual([ + 'an outer spec', + 'an inner spec', + 'another inner spec' + ]); + + }); + + it("Multiple top-level Suites execute as expected", function() { + var env = new jasmine.Env(), + calls = []; + + env.describe("Outer suite", function() { + env.it("an outer spec", function() { + calls.push('an outer spec') + }); + env.describe("Inner suite", function() { + env.it("an inner spec", function() { + calls.push('an inner spec'); + }); + env.it("another inner spec", function() { + calls.push('another inner spec'); + }); + }); + }); + + env.describe("Another outer suite", function() { + env.it("a 2nd outer spec", function() { + calls.push('a 2nd outer spec') + }); + }); + + env.execute(); + + expect(calls).toEqual([ + 'an outer spec', + 'an inner spec', + 'another inner spec', + 'a 2nd outer spec' + ]); + }); + + it("Mock clock can be installed and used in tests", function() { + var globalSetTimeout = jasmine.createSpy('globalSetTimeout'), + delayedFunctionForGlobalClock = jasmine.createSpy('delayedFunctionForGlobalClock'), + delayedFunctionForMockClock = jasmine.createSpy('delayedFunctionForMockClock'), + env = new jasmine.Env({global: { setTimeout: globalSetTimeout }}); + + env.describe("tests", function() { + env.it("test with mock clock", function() { + env.clock.install(); + env.clock.setTimeout(delayedFunctionForMockClock, 100); + env.clock.tick(100); + }); + env.it("test without mock clock", function() { + env.clock.setTimeout(delayedFunctionForGlobalClock, 100); + }); + }); + + expect(globalSetTimeout).not.toHaveBeenCalled(); + expect(delayedFunctionForMockClock).not.toHaveBeenCalled(); + + env.execute(); + + expect(delayedFunctionForMockClock).toHaveBeenCalled(); + expect(globalSetTimeout).toHaveBeenCalledWith(delayedFunctionForGlobalClock, 100); + }); + + it("should report as expected", function() { + var env = new jasmine.Env(), + reporter = jasmine.createSpyObj('fakeReproter', [ + "jasmineStarted", + "jasmineDone", + "suiteStarted", + "suiteDone", + "specStarted", + "specDone" + ]); + + env.addReporter(reporter); + + env.describe("A Suite", function() { + env.it("with a top level spec", function() { + env.expect(true).toBe(true); + }); + env.describe("with a nested suite", function() { + env.xit("with a disabled spec", function() { + env.expect(true).toBe(true); + }); + env.it("with a spec", function() { + env.expect(true).toBe(false); + }); + }); + }); + + env.execute(); + + expect(reporter.jasmineStarted).toHaveBeenCalledWith({ + totalSpecsDefined: 3 + }); + var suiteResult = reporter.suiteStarted.calls[0].args[0]; + expect(suiteResult.description).toEqual("A Suite"); + expect(reporter.jasmineDone).toHaveBeenCalled(); + }); + + it("should be possible to get full name from a spec", function() { + var env = new jasmine.Env({global: { setTimeout: setTimeout }}), + topLevelSpec, nestedSpec, doublyNestedSpec; + + env.describe("my tests", function() { + topLevelSpec = env.it("are sometimes top level", function() { + }); + env.describe("are sometimes", function() { + nestedSpec = env.it("singly nested", function() { + }); + env.describe("even", function() { + doublyNestedSpec = env.it("doubly nested", function() { + }); + }); + }); + }); + + expect(topLevelSpec.getFullName()).toBe("my tests are sometimes top level."); + expect(nestedSpec.getFullName()).toBe("my tests are sometimes singly nested."); + expect(doublyNestedSpec.getFullName()).toBe("my tests are sometimes even doubly nested."); + }); +}); diff --git a/spec/core/ExceptionFormatterSpec.js b/spec/core/ExceptionFormatterSpec.js new file mode 100644 index 00000000..c3052c8c --- /dev/null +++ b/spec/core/ExceptionFormatterSpec.js @@ -0,0 +1,26 @@ +describe("ExceptionFormatter", function() { + + it('formats Firefox exception messages', function() { + var sampleFirefoxException = { + fileName: 'foo.js', + line: '1978', + message: 'you got your foo in my bar', + name: 'A Classic Mistake' + }, + message = jasmine.exceptionMessageFor(sampleFirefoxException); + + expect(message).toEqual('A Classic Mistake: you got your foo in my bar in foo.js (line 1978)'); + }); + + it('formats Webkit exception messages', function() { + var sampleWebkitException = { + sourceURL: 'foo.js', + lineNumber: '1978', + message: 'you got your foo in my bar', + name: 'A Classic Mistake' + }, + message = jasmine.exceptionMessageFor(sampleWebkitException); + + expect(message).toEqual('A Classic Mistake: you got your foo in my bar in foo.js (line 1978)'); + }); +}); \ No newline at end of file diff --git a/spec/core/ExceptionsSpec.js b/spec/core/ExceptionsSpec.js index 315aa1bf..e06b1275 100644 --- a/spec/core/ExceptionsSpec.js +++ b/spec/core/ExceptionsSpec.js @@ -6,34 +6,9 @@ describe('Exceptions:', function() { env.updateInterval = 0; }); - it('jasmine.formatException formats Firefox exception messages as expected', function() { - var sampleFirefoxException = { - fileName: 'foo.js', - line: '1978', - message: 'you got your foo in my bar', - name: 'A Classic Mistake' - }; - - var expected = 'A Classic Mistake: you got your foo in my bar in foo.js (line 1978)'; - - expect(jasmine.util.formatException(sampleFirefoxException)).toEqual(expected); - }); - - it('jasmine.formatException formats Webkit exception messages as expected', function() { - var sampleWebkitException = { - sourceURL: 'foo.js', - lineNumber: '1978', - message: 'you got your foo in my bar', - name: 'A Classic Mistake' - }; - - var expected = 'A Classic Mistake: you got your foo in my bar in foo.js (line 1978)'; - - expect(jasmine.util.formatException(sampleWebkitException)).toEqual(expected); - }); - describe('with break on exception', function() { it('should not catch the exception', function() { + env.catchExceptions(false); var suite = env.describe('suite for break on exceptions', function() { env.it('should break when an exception is thrown', function() { throw new Error('I should hit a breakpoint!'); @@ -42,16 +17,11 @@ describe('Exceptions:', function() { var runner = env.currentRunner(); var dont_change = 'I will never change!'; - var oldCatch = jasmine.CATCH_EXCEPTIONS; - jasmine.CATCH_EXCEPTIONS = false; try { suite.execute(); dont_change = 'oops I changed'; } catch (e) {} - finally { - jasmine.CATCH_EXCEPTIONS = oldCatch; - } expect(dont_change).toEqual('I will never change!'); }); @@ -59,117 +29,36 @@ describe('Exceptions:', function() { describe("with catch on exception", function() { it('should handle exceptions thrown, but continue', function() { - var fakeTimer = new jasmine.FakeTimer(); - env.setTimeout = fakeTimer.setTimeout; - env.clearTimeout = fakeTimer.clearTimeout; - env.setInterval = fakeTimer.setInterval; - env.clearInterval = fakeTimer.clearInterval; - - //we run two exception tests to make sure we continue after throwing an exception - var suite = env.describe('Suite for handles exceptions', function () { + var ranSecondTest = false, + suite = env.describe('Suite for handles exceptions', function () { env.it('should be a test that fails because it throws an exception', function() { - throw new Error('fake error 1'); + throw new Error(); }); - - env.it('should be another test that fails because it throws an exception', function() { - this.runs(function () { - throw new Error('fake error 2'); - }); - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - - env.it('should be a passing test that runs after exceptions are thrown', function() { - this.expect(true).toEqual(true); - }); - - env.it('should be another test that fails because it throws an exception after a wait', function() { - this.runs(function () { - var foo = 'foo'; - }); - this.waits(250); - this.runs(function () { - throw new Error('fake error 3'); - }); - }); - env.it('should be a passing test that runs after exceptions are thrown from a async test', function() { - this.expect(true).toEqual(true); + ranSecondTest = true; }); }); - var runner = env.currentRunner(); suite.execute(); - fakeTimer.tick(2500); - - var suiteResults = suite.results(); - var specResults = suiteResults.getItems(); - - expect(suiteResults.passed()).toEqual(false); - // - expect(specResults.length).toEqual(5); - expect(specResults[0].passed()).toMatch(false); - var blockResults = specResults[0].getItems(); - expect(blockResults[0].passed()).toEqual(false); - expect(blockResults[0].message).toMatch(/fake error 1/); - - expect(specResults[1].passed()).toEqual(false); - blockResults = specResults[1].getItems(); - expect(blockResults[0].passed()).toEqual(false); - expect(blockResults[0].message).toMatch(/fake error 2/); - expect(blockResults[1].passed()).toEqual(true); - - expect(specResults[2].passed()).toEqual(true); - - expect(specResults[3].passed()).toEqual(false); - blockResults = specResults[3].getItems(); - expect(blockResults[0].message).toMatch(/fake error 3/); - - expect(specResults[4].passed()).toEqual(true); + expect(ranSecondTest).toBe(true); }); it("should handle exceptions thrown directly in top-level describe blocks and continue", function () { - var suite = env.describe("a top level describe block that throws an exception", function () { + var ranSecondDescribe = false, suite, suite2, runner = env.currentRunner(); + suite = env.describe("a suite that throws an exception", function () { env.it("is a test that should pass", function () { this.expect(true).toEqual(true); }); throw new Error("top level error"); }); - - suite.execute(); - var suiteResults = suite.results(); - var specResults = suiteResults.getItems(); - - expect(suiteResults.passed()).toEqual(false); - expect(specResults.length).toEqual(2); - - expect(specResults[1].description).toMatch(/encountered a declaration exception/); - }); - - it("should handle exceptions thrown directly in nested describe blocks and continue", function () { - var suite = env.describe("a top level describe", function () { - env.describe("a mid-level describe that throws an exception", function () { - env.it("is a test that should pass", function () { - this.expect(true).toEqual(true); - }); - - throw new Error("a mid-level error"); - }); + suite2 = env.describe("a suite that doesn't throw an exception", function () { + ranSecondDescribe = true; }); - suite.execute(); - var suiteResults = suite.results(); - var specResults = suiteResults.getItems(); - - expect(suiteResults.passed()).toEqual(false); - expect(specResults.length).toEqual(1); - - var nestedSpecResults = specResults[0].getItems(); - - expect(nestedSpecResults.length).toEqual(2); - expect(nestedSpecResults[1].description).toMatch(/encountered a declaration exception/); + runner.execute(); + expect(ranSecondDescribe).toBe(true); }); }); + }); diff --git a/spec/core/ExpectationResultSpec.js b/spec/core/ExpectationResultSpec.js new file mode 100644 index 00000000..36c4e49c --- /dev/null +++ b/spec/core/ExpectationResultSpec.js @@ -0,0 +1,48 @@ +describe("buildExpectationResult", function() { + + it("defaults to passed", function() { + var result = jasmine.buildExpectationResult({passed: 'some-value'}); + expect(result.passed).toBe('some-value'); + }); + + it("has a type of expect", function() { + var result = jasmine.buildExpectationResult({}); + expect(result.type).toBe('expect'); + }); + + it("message defaults to Passed for passing specs", function() { + var result = jasmine.buildExpectationResult({passed: true, message: 'some-value'}); + expect(result.message).toBe('Passed.'); + }); + + it("message returns the message for failing specs", function() { + var result = jasmine.buildExpectationResult({passed: false, message: 'some-value'}); + expect(result.message).toBe('some-value'); + }); + + it("trace passes trace if exists", function() { + var result = jasmine.buildExpectationResult({trace: 'some-value'}); + expect(result.trace).toBe('some-value'); + }); + + it("trace returns a new error if trace is falsy", function() { + var result = jasmine.buildExpectationResult({trace: false}); + expect(result.trace).toEqual(jasmine.any(Error)); + }); + + it("matcherName returns passed matcherName", function() { + var result = jasmine.buildExpectationResult({matcherName: 'some-value'}); + expect(result.matcherName).toBe('some-value'); + }); + + it("expected returns passed expected", function() { + var result = jasmine.buildExpectationResult({expected: 'some-value'}); + expect(result.expected).toBe('some-value'); + }); + + it("actual returns passed actual", function() { + var result = jasmine.buildExpectationResult({actual: 'some-value'}); + expect(result.actual).toBe('some-value'); + }); + +}); diff --git a/spec/core/JsApiReporterSpec.js b/spec/core/JsApiReporterSpec.js index cca70674..bf7f9f5a 100644 --- a/spec/core/JsApiReporterSpec.js +++ b/spec/core/JsApiReporterSpec.js @@ -1,6 +1,6 @@ -describe('jasmine.jsApiReporter', function() { - describe('results', function () { - var reporter, spec1, spec2, spec3, expectedSpec1Results, expectedSpec2Results; +xdescribe('JsApiReporter (integration specs)', function() { + describe('results', function() { + var reporter, spec1, spec2; var env; var suite, nestedSuite, nestedSpec; @@ -24,34 +24,24 @@ describe('jasmine.jsApiReporter', function() { }); }); - spec3 = env.it("spec 3", function() { - this.log('some debug message'); - }); }); - reporter = new jasmine.JsApiReporter(); + reporter = new jasmine.JsApiReporter(jasmine); env.addReporter(reporter); env.execute(); - expectedSpec1Results = { - messages: spec1.results().getItems(), - result: "passed" - }; - expectedSpec2Results = { - messages: spec2.results().getItems(), - result: "failed" - }; }); - it('resultForSpec() should return the result for the given spec', function () { - expect(reporter.resultsForSpec(spec1.id)).toEqual(expectedSpec1Results); - expect(reporter.resultsForSpec(spec2.id)).toEqual(expectedSpec2Results); - }); - - it('results() should return a hash of all results, indexed by spec id', function () { - expect(reporter.results()[spec1.id]).toEqual(expectedSpec1Results); - expect(reporter.results()[spec2.id]).toEqual(expectedSpec2Results); + it('results() should return a hash of all results, indexed by spec id', function() { + var expectedSpec1Results = { + result: "passed" + }, + expectedSpec2Results = { + result: "failed" + }; + expect(reporter.results()[spec1.id].result).toEqual('passed'); + expect(reporter.results()[spec2.id].result).toEqual('failed'); }); it("should return nested suites as children of their parents", function() { @@ -65,7 +55,6 @@ describe('jasmine.jsApiReporter', function() { { id: 2, name: 'nested spec', type: 'spec', children: [ ] } ] }, - { id: 3, name: 'spec 3', type: 'spec', children: [ ] } ] } ]); @@ -76,12 +65,7 @@ describe('jasmine.jsApiReporter', function() { var result = reporter.results()[spec1.id]; var summarizedResult = reporter.summarizeResult_(result); expect(summarizedResult.result).toEqual('passed'); - expect(summarizedResult.messages.length).toEqual(1); - expect(summarizedResult.messages[0].message).toEqual(result.messages[0].message); - expect(summarizedResult.messages[0].passed).toBeTruthy(); - expect(summarizedResult.messages[0].type).toEqual('expect'); - expect(summarizedResult.messages[0].text).toBeUndefined(); - expect(summarizedResult.messages[0].trace.stack).toBeUndefined(); + expect(summarizedResult.messages.length).toEqual(0); }); it("should have a stack trace for failing specs", function() { @@ -91,13 +75,98 @@ describe('jasmine.jsApiReporter', function() { expect(summarizedResult.messages[0].trace.stack).toEqual(result.messages[0].trace.stack); }); - it("should have messages for specs with messages", function() { - var result = reporter.results()[spec3.id]; - var summarizedResult = reporter.summarizeResult_(result); - expect(summarizedResult.result).toEqual('passed'); - expect(summarizedResult.messages[0].type).toEqual('log'); - expect(summarizedResult.messages[0].text).toEqual('some debug message'); - }); }); }); -}); \ No newline at end of file +}); + + +describe("JsApiReporter", function() { + + it("knows when a full environment is started", function() { + var reporter = new jasmine.JsApiReporter(); + + expect(reporter.started).toBe(false); + expect(reporter.finished).toBe(false); + + reporter.jasmineStarted(); + + expect(reporter.started).toBe(true); + expect(reporter.finished).toBe(false); + }); + + it("knows when a full environment is done", function() { + var reporter = new jasmine.JsApiReporter(); + + expect(reporter.started).toBe(false); + expect(reporter.finished).toBe(false); + + reporter.jasmineStarted(); + reporter.jasmineDone(); + + expect(reporter.finished).toBe(true); + }); + + it("defaults to 'loaded' status", function() { + var reporter = new jasmine.JsApiReporter(); + + expect(reporter.status()).toEqual('loaded'); + }); + + it("reports 'started' when Jasmine has started", function() { + var reporter = new jasmine.JsApiReporter(); + + reporter.jasmineStarted(); + + expect(reporter.status()).toEqual('started'); + }); + + it("reports 'done' when Jasmine is done", function() { + var reporter = new jasmine.JsApiReporter(); + + reporter.jasmineDone(); + + expect(reporter.status()).toEqual('done'); + }); + + it("tracks a suite", function() { + var reporter = new jasmine.JsApiReporter(); + + reporter.suiteStarted({ + id: 123, + description: "A suite" + }); + + var suites = reporter.suites(); + + expect(suites).toEqual({123: {id: 123, description: "A suite"}}); + + reporter.suiteDone({ + id: 123, + description: "A suite", + status: 'passed' + }); + + expect(suites).toEqual({123: {id: 123, description: "A suite", status: 'passed'}}); + }); + + it("tracks a spec", function() { + var reporter = new jasmine.JsApiReporter(); + + reporter.specStarted({ + id: 123, + description: "A spec" + }); + + var specs = reporter.specs(); + + expect(specs).toEqual({123: {id: 123, description: "A spec"}}); + + reporter.specDone({ + id: 123, + description: "A spec", + status: 'passed' + }); + + expect(specs).toEqual({123: {id: 123, description: "A spec", status: 'passed'}}); + }); +}); diff --git a/spec/core/MatchersSpec.js b/spec/core/MatchersSpec.js index 97c3f60d..fe501f7a 100644 --- a/spec/core/MatchersSpec.js +++ b/spec/core/MatchersSpec.js @@ -9,14 +9,14 @@ describe("jasmine.Matchers", function() { spec = env.it("spec", function() { }); }); - spyOn(spec, 'addMatcherResult'); + spyOn(spec, 'addExpectationResult'); - this.addMatchers({ + addMatchers({ toPass: function() { - return lastResult().passed(); + return lastResult().passed; }, toFail: function() { - return !lastResult().passed(); + return !lastResult().passed; } }); }); @@ -26,7 +26,7 @@ describe("jasmine.Matchers", function() { } function lastResult() { - return spec.addMatcherResult.mostRecentCall.args[0]; + return spec.addExpectationResult.mostRecentCall.args[1]; } function catchException(fn) { @@ -82,7 +82,7 @@ describe("jasmine.Matchers", function() { expect((match(/[abc]/gm).toNotEqual(/1/i))).toPass(); // only test if the browser supports the sticky option on a regExp see pull #234 - if (typeof RegExp.prototype.sticky !== 'undefined') { + if (RegExp.prototype.sticky !== undefined) { var sticky_regexp = new RegExp("[abc]", "y"); expect((match(sticky_regexp).toEqual(/1/i))).toFail(); expect((match(sticky_regexp).toNotEqual(/1/i))).toPass(); @@ -98,7 +98,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toEqual"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual)); expect(result.message).toMatch(jasmine.pp(expected)); expect(result.expected).toEqual(expected); @@ -113,7 +113,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toNotEqual"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(str)); expect(result.message).toMatch('not'); expect(result.expected).toEqual(str); @@ -142,7 +142,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toBe"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual)); expect(result.message).toMatch(jasmine.pp(expected)); expect(result.expected).toEqual(expected); @@ -157,7 +157,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toNotBe"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(str); expect(result.expected).toEqual(str); expect(result.actual).toEqual(str); @@ -186,7 +186,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toMatch"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual)); expect(result.message).toMatch(expected.toString()); expect(result.expected).toEqual(expected); @@ -202,7 +202,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toMatch"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toEqual("Expected 'a' to match 'b'."); expect(result.expected).toEqual(expected); expect(result.actual).toEqual(actual); @@ -217,7 +217,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toNotMatch"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toEqual("Expected 'a' to not match /a/."); expect(result.expected).toEqual(expected); expect(result.actual).toEqual(actual); @@ -231,7 +231,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toNotMatch"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toEqual("Expected 'a' to not match 'a'."); expect(result.expected).toEqual(str); expect(result.actual).toEqual(str); @@ -249,7 +249,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toBeDefined"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toEqual('Expected undefined to be defined.'); expect(result.actual).toEqual(jasmine.undefined); }); @@ -273,7 +273,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toBeNull"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual)); expect(result.message).toMatch('null'); expect(result.actual).toEqual(actual); @@ -287,34 +287,34 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toBeNull"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual)); expect(result.message).toMatch('null'); expect(result.actual).toEqual(actual); }); - it("toBeNaN", function() { - expect(match(Number.NaN).toBeNaN()).toPass(); - expect(match(0).toBeNaN()).toFail(); - expect(match(1).toBeNaN()).toFail(); - expect(match(null).toBeNaN()).toFail(); - expect(match(Number.POSITIVE_INFINITY).toBeNaN()).toFail(); - expect(match(Number.NEGATIVE_INFINITY).toBeNaN()).toFail(); - expect(match('NaN').toBeNaN()).toFail(); - }); + it("toBeNaN", function() { + expect(match(Number.NaN).toBeNaN()).toPass(); + expect(match(0).toBeNaN()).toFail(); + expect(match(1).toBeNaN()).toFail(); + expect(match(null).toBeNaN()).toFail(); + expect(match(Number.POSITIVE_INFINITY).toBeNaN()).toFail(); + expect(match(Number.NEGATIVE_INFINITY).toBeNaN()).toFail(); + expect(match('NaN').toBeNaN()).toFail(); + }); - it("toBeNaN to build an ExpectationResult", function() { - var actual = 'a'; - var matcher = match(actual); - matcher.toBeNaN(); + it("toBeNaN to build an ExpectationResult", function() { + var actual = 'a'; + var matcher = match(actual); + matcher.toBeNaN(); - var result = lastResult(); + var result = lastResult(); - expect(result.matcherName).toEqual("toBeNaN"); - expect(result.passed()).toFail(); - expect(result.message).toMatch("Expected 'a' to be NaN."); - expect(result.actual).toMatch(actual); - }); + expect(result.matcherName).toEqual("toBeNaN"); + expect(result.passed).toBe(false); + expect(result.message).toMatch("Expected 'a' to be NaN."); + expect(result.actual).toMatch(actual); + }); it("toBeFalsy", function() { expect(match(false).toBeFalsy()).toPass(); @@ -332,7 +332,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toBeFalsy"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual)); expect(result.message).toMatch('falsy'); expect(result.actual).toEqual(actual); @@ -356,7 +356,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toBeTruthy"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toEqual("Expected false to be truthy."); expect(result.actual).toFail(); }); @@ -378,11 +378,11 @@ describe("jasmine.Matchers", function() { expect(match({someObj:'foo'}).toEqual(jasmine.any(Function))).toFail(); expect(match( function() { - }).toEqual(jasmine.any(Object))).toFail(); + }).toEqual(jasmine.any(Object))).toFail(); expect(match(["foo", "goo"]).toEqual(["foo", jasmine.any(String)])).toPass(); expect(match( function() { - }).toEqual(jasmine.any(Function))).toPass(); + }).toEqual(jasmine.any(Function))).toPass(); expect(match(["a", function() { }]).toEqual(["a", jasmine.any(Function)])).toPass(); }); @@ -391,7 +391,7 @@ describe("jasmine.Matchers", function() { var matcher; beforeEach(function () { matcher = { - jasmineMatches: jasmine.createSpy("jasmineMatches") + jasmineMatches: originalJasmine.createSpy("jasmineMatches") }; }); @@ -459,7 +459,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toContain"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual)); expect(result.message).toMatch('contain'); expect(result.message).toMatch(jasmine.pp(expected)); @@ -476,7 +476,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toNotContain"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual)); expect(result.message).toMatch('not contain'); expect(result.message).toMatch(jasmine.pp(expected)); @@ -499,7 +499,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toBeLessThan"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual) + ' to be less than'); expect(result.message).toMatch(jasmine.pp(expected)); expect(result.actual).toEqual(actual); @@ -521,7 +521,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toBeGreaterThan"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toMatch(jasmine.pp(actual) + ' to be greater than'); expect(result.message).toMatch(jasmine.pp(expected)); expect(result.actual).toEqual(actual); @@ -608,13 +608,13 @@ describe("jasmine.Matchers", function() { it("should match exceptions specified by message", function() { expect(match(throwingFn).not.toThrow("Fake Error")).toFail(); -// expect(lastResult().message).toMatch(/Expected function not to throw Fake Error./); + // expect(lastResult().message).toMatch(/Expected function not to throw Fake Error./); expect(match(throwingFn).not.toThrow("Other Error")).toPass(); }); it("should match exceptions specified by Error", function() { expect(match(throwingFn).not.toThrow(new Error("Fake Error"))).toFail(); -// expect(lastResult().message).toMatch("Other Error"); + // expect(lastResult().message).toMatch("Other Error"); expect(match(throwingFn).not.toThrow(new Error("Other Error"))).toPass(); }); }); @@ -645,7 +645,7 @@ describe("jasmine.Matchers", function() { it("should fail (or pass when inverted with .not)", function() { expect(match( function() { - }).toThrow()).toFail(); + }).toThrow()).toFail(); expect(lastResult().message).toEqual('Expected function to throw an exception.'); }); }); @@ -668,7 +668,7 @@ describe("jasmine.Matchers", function() { }); it("should use the second message when the matcher sets an array of custom messages", function() { - spec.addMatchers({ + env.addMatchers({ custom: function() { this.message = function() { return ['Expected it was called.', 'Expected it wasn\'t called.']; @@ -694,7 +694,7 @@ describe("jasmine.Matchers", function() { TestClass = { normalFunction: function() { }, - spyFunction: jasmine.createSpy("My spy") + spyFunction: originalJasmine.createSpy("My spy") }; }); @@ -702,23 +702,23 @@ describe("jasmine.Matchers", function() { return function() { expect( function() { - match(TestClass.normalFunction)[methodName](); - }).toThrow('Expected a spy, but got Function.'); + match(TestClass.normalFunction)[methodName](); + }).toThrow('Expected a spy, but got Function.'); expect( function() { - match(jasmine.undefined)[methodName](); - }).toThrow('Expected a spy, but got undefined.'); + match(jasmine.undefined)[methodName](); + }).toThrow('Expected a spy, but got undefined.'); expect( function() { - match({some:'object'})[methodName](); - }).toThrow('Expected a spy, but got { some : \'object\' }.'); + match({some:'object'})[methodName](); + }).toThrow('Expected a spy, but got { some : \'object\' }.'); expect( function() { - match("")[methodName](); - }).toThrow('Expected a spy, but got \'\'.'); + match("")[methodName](); + }).toThrow('Expected a spy, but got \'\'.'); }; } @@ -733,8 +733,8 @@ describe("jasmine.Matchers", function() { it("should throw an exception when invoked with any arguments", function() { expect( function() { - match(TestClass.normalFunction).toHaveBeenCalled("unwanted argument"); - }).toThrow('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + match(TestClass.normalFunction).toHaveBeenCalled("unwanted argument"); + }).toThrow('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); }); it('should throw an exception when invoked on a non-spy', shouldThrowAnExceptionWhenInvokedOnANonSpy('toHaveBeenCalled')); @@ -762,8 +762,8 @@ describe("jasmine.Matchers", function() { it("should throw an exception when invoked with any arguments", function() { expect( function() { - match(TestClass.normalFunction).wasNotCalled("unwanted argument"); - }).toThrow('wasNotCalled does not take arguments'); + match(TestClass.normalFunction).wasNotCalled("unwanted argument"); + }).toThrow('wasNotCalled does not take arguments'); }); it('should throw an exception when invoked on a non-spy', shouldThrowAnExceptionWhenInvokedOnANonSpy('wasNotCalled')); @@ -780,7 +780,7 @@ describe("jasmine.Matchers", function() { var expected = match(TestClass.spyFunction); expect(expected.toHaveBeenCalledWith('c', 'b', 'a')).toFail(); var result = lastResult(); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.expected).toEqual(['c', 'b', 'a']); expect(result.actual.mostRecentCall.args).toEqual(['a', 'b', 'c']); expect(result.message).toContain(jasmine.pp(result.expected)); @@ -791,7 +791,7 @@ describe("jasmine.Matchers", function() { var expected = match(TestClass.spyFunction); expect(expected.toHaveBeenCalledWith('c', 'b', 'a')).toFail(); var result = lastResult(); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.expected).toEqual(['c', 'b', 'a']); expect(result.actual.argsForCall).toEqual([]); expect(result.message).toContain(jasmine.pp(result.expected)); @@ -839,7 +839,7 @@ describe("jasmine.Matchers", function() { }, spec); TestClass = { someFunction: function(a, b) { } }; - spec.spyOn(TestClass, 'someFunction'); + env.spyOn(TestClass, 'someFunction'); }); it("should should handle the case of a spy", function() { @@ -849,7 +849,7 @@ describe("jasmine.Matchers", function() { var result = lastResult(); expect(result.matcherName).toEqual("toHaveBeenCalledWith"); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.message).toContain(jasmine.pp(['a', 'b'])); expect(result.message).toContain(jasmine.pp(['a', 'c'])); expect(result.actual).toEqual(TestClass.someFunction); @@ -879,7 +879,7 @@ describe("jasmine.Matchers", function() { var expected = match(TestClass.spyFunction); expect(expected.wasNotCalledWith('a', 'b', 'c')).toFail(); var result = lastResult(); - expect(result.passed()).toFail(); + expect(result.passed).toBe(false); expect(result.expected).toEqual(['a', 'b', 'c']); expect(result.actual.mostRecentCall.args).toEqual(['a', 'b', 'c']); expect(result.message).toContain(jasmine.pp(result.expected)); @@ -910,7 +910,7 @@ describe("jasmine.Matchers", function() { containing = new jasmine.Matchers.ObjectContaining({}); }); it("matches everything", function () { - expect(containing.jasmineMatches("foo", [], [])).toBe(true); + expect(containing.jasmineMatches("foo", [], [])).toBe(true); }); it("says it didn't expect to contain anything", function () { @@ -974,7 +974,7 @@ describe("jasmine.Matchers", function() { describe("in real life", function () { var method; beforeEach(function () { - method = jasmine.createSpy("method"); + method = originalJasmine.createSpy("method"); method({a:"b", c:"d"}); }); it("works correctly for positive matches", function () { diff --git a/spec/core/MockClockSpec.js b/spec/core/MockClockSpec.js deleted file mode 100644 index be683440..00000000 --- a/spec/core/MockClockSpec.js +++ /dev/null @@ -1,38 +0,0 @@ -describe("MockClock", function () { - - beforeEach(function() { - jasmine.Clock.useMock(); - }); - - describe("setTimeout", function () { - it("should mock the clock when useMock is in a beforeEach", function() { - var expected = false; - setTimeout(function() { - expected = true; - }, 30000); - expect(expected).toBe(false); - jasmine.Clock.tick(30001); - expect(expected).toBe(true); - }); - }); - - describe("setInterval", function () { - it("should mock the clock when useMock is in a beforeEach", function() { - var interval = 0; - setInterval(function() { - interval++; - }, 30000); - expect(interval).toEqual(0); - jasmine.Clock.tick(30001); - expect(interval).toEqual(1); - jasmine.Clock.tick(30001); - expect(interval).toEqual(2); - jasmine.Clock.tick(1); - expect(interval).toEqual(2); - }); - }); - - it("shouldn't complain if you call jasmine.Clock.useMock() more than once", function() { - jasmine.Clock.useMock(); - }); -}); diff --git a/spec/core/MultiReporterSpec.js b/spec/core/MultiReporterSpec.js deleted file mode 100644 index f269d682..00000000 --- a/spec/core/MultiReporterSpec.js +++ /dev/null @@ -1,45 +0,0 @@ -describe("jasmine.MultiReporter", function() { - var multiReporter, fakeReporter1, fakeReporter2; - - beforeEach(function() { - multiReporter = new jasmine.MultiReporter(); - fakeReporter1 = jasmine.createSpyObj("fakeReporter1", ["reportSpecResults"]); - fakeReporter2 = jasmine.createSpyObj("fakeReporter2", ["reportSpecResults", "reportRunnerStarting"]); - multiReporter.addReporter(fakeReporter1); - multiReporter.addReporter(fakeReporter2); - }); - - it("should support all the method calls that jasmine.Reporter supports", function() { - var delegate = {}; - multiReporter.addReporter(delegate); - - this.addMatchers({ - toDelegateMethod: function(methodName) { - delegate[methodName] = jasmine.createSpy(methodName); - this.actual[methodName]("whatever argument"); - - return delegate[methodName].wasCalled && - delegate[methodName].mostRecentCall.args.length == 1 && - delegate[methodName].mostRecentCall.args[0] == "whatever argument"; - } - }); - - expect(multiReporter).toDelegateMethod('reportRunnerStarting'); - expect(multiReporter).toDelegateMethod('reportRunnerResults'); - expect(multiReporter).toDelegateMethod('reportSuiteResults'); - expect(multiReporter).toDelegateMethod('reportSpecStarting'); - expect(multiReporter).toDelegateMethod('reportSpecResults'); - expect(multiReporter).toDelegateMethod('log'); - }); - - it("should delegate to any and all subreporters", function() { - multiReporter.reportSpecResults('blah', 'foo'); - expect(fakeReporter1.reportSpecResults).toHaveBeenCalledWith('blah', 'foo'); - expect(fakeReporter2.reportSpecResults).toHaveBeenCalledWith('blah', 'foo'); - }); - - it("should quietly skip delegating to any subreporters which lack the given method", function() { - multiReporter.reportRunnerStarting('blah', 'foo'); - expect(fakeReporter2.reportRunnerStarting).toHaveBeenCalledWith('blah', 'foo'); - }); -}); \ No newline at end of file diff --git a/spec/core/NestedResultsSpec.js b/spec/core/NestedResultsSpec.js deleted file mode 100644 index e4bc9190..00000000 --- a/spec/core/NestedResultsSpec.js +++ /dev/null @@ -1,54 +0,0 @@ -describe('jasmine.NestedResults', function() { - it('#addResult increments counters', function() { - // Leaf case - var results = new jasmine.NestedResults(); - - results.addResult(new jasmine.ExpectationResult({ - matcherName: "foo", passed: true, message: 'Passed.', actual: 'bar', expected: 'bar'} - )); - - expect(results.getItems().length).toEqual(1); - expect(results.totalCount).toEqual(1); - expect(results.passedCount).toEqual(1); - expect(results.failedCount).toEqual(0); - - results.addResult(new jasmine.ExpectationResult({ - matcherName: "baz", passed: false, message: 'FAIL.', actual: "corge", expected: "quux" - })); - - expect(results.getItems().length).toEqual(2); - expect(results.totalCount).toEqual(2); - expect(results.passedCount).toEqual(1); - expect(results.failedCount).toEqual(1); - }); - - it('should roll up counts for nested results', function() { - // Branch case - var leafResultsOne = new jasmine.NestedResults(); - leafResultsOne.addResult(new jasmine.ExpectationResult({ - matcherName: "toSomething", passed: true, message: 'message', actual: '', expected:'' - })); - - leafResultsOne.addResult(new jasmine.ExpectationResult({ - matcherName: "toSomethingElse", passed: false, message: 'message', actual: 'a', expected: 'b' - })); - - var leafResultsTwo = new jasmine.NestedResults(); - leafResultsTwo.addResult(new jasmine.ExpectationResult({ - matcherName: "toSomething", passed: true, message: 'message', actual: '', expected: '' - })); - leafResultsTwo.addResult(new jasmine.ExpectationResult({ - matcherName: "toSomethineElse", passed: false, message: 'message', actual: 'c', expected: 'd' - })); - - var branchResults = new jasmine.NestedResults(); - branchResults.addResult(leafResultsOne); - branchResults.addResult(leafResultsTwo); - - expect(branchResults.getItems().length).toEqual(2); - expect(branchResults.totalCount).toEqual(4); - expect(branchResults.passedCount).toEqual(2); - expect(branchResults.failedCount).toEqual(2); - }); - -}); diff --git a/spec/core/QueueRunnerSpec.js b/spec/core/QueueRunnerSpec.js new file mode 100644 index 00000000..933f3e67 --- /dev/null +++ b/spec/core/QueueRunnerSpec.js @@ -0,0 +1,132 @@ +describe("QueueRunner", function() { + + it("runs all the functions it's passed", function() { + var calls = [], + fn1 = jasmine.createSpy('fn1'), + fn2 = jasmine.createSpy('fn2'), + queueRunner = new jasmine.QueueRunner({ + fns: [fn1, fn2] + }); + fn1.andCallFake(function() { + calls.push('fn1'); + }); + fn2.andCallFake(function() { + calls.push('fn2'); + }); + + queueRunner.execute(); + + expect(calls).toEqual(['fn1', 'fn2']); + }); + + it("supports asynchronous functions, only advancing to next function after a done() callback", function() { + //TODO: it would be nice if spy arity could match the fake, so we could do something like: + //createSpy('asyncfn').andCallFake(function(done) {}); + + var onComplete = originalJasmine.createSpy('onComplete'), + beforeCallback = originalJasmine.createSpy('beforeCallback'), + fnCallback = originalJasmine.createSpy('fnCallback'), + afterCallback = originalJasmine.createSpy('afterCallback'), + fn1 = function(done) { + beforeCallback(); + setTimeout(function() { + done() + }, 100); + }, + fn2 = function(done) { + fnCallback(); + setTimeout(function() { + done() + }, 100); + }, + fn3 = function(done) { + afterCallback(); + setTimeout(function() { + done() + }, 100); + }, + queueRunner = new jasmine.QueueRunner({ + fns: [fn1, fn2, fn3], + onComplete: onComplete + }); + + clock.install(); + + queueRunner.execute(); + + expect(beforeCallback).toHaveBeenCalled(); + expect(fnCallback).not.toHaveBeenCalled(); + expect(afterCallback).not.toHaveBeenCalled(); + expect(onComplete).not.toHaveBeenCalled(); + + clock.tick(100); + + expect(fnCallback).toHaveBeenCalled(); + expect(afterCallback).not.toHaveBeenCalled(); + expect(onComplete).not.toHaveBeenCalled(); + + clock.tick(100); + + expect(afterCallback).toHaveBeenCalled(); + expect(onComplete).not.toHaveBeenCalled(); + + clock.tick(100); + + expect(onComplete).toHaveBeenCalled(); + }); + + it("calls an exception handler when an exception is thrown in a fn", function() { + var fn = function() { + throw new Error('fake error'); + }, + exceptionCallback = jasmine.createSpy('exception callback'), + queueRunner = new jasmine.QueueRunner({ + fns: [fn], + onException: exceptionCallback + }); + + queueRunner.execute(); + + expect(exceptionCallback).toHaveBeenCalledWith(jasmine.any(Error)); + }); + + it("rethrows an exception if told to", function() { + var fn = function() { + throw new Error('fake error'); + }, + queueRunner = new jasmine.QueueRunner({ + fns: [fn], + catchingExceptions: function() { return false; } + }); + + expect(function() { queueRunner.execute(); }).toThrow(); + }); + + it("calls a provided complete callback when done", function() { + var fn = jasmine.createSpy('fn'), + completeCallback = jasmine.createSpy('completeCallback'), + queueRunner = new jasmine.QueueRunner({ + fns: [fn], + onComplete: completeCallback + }); + + queueRunner.execute(); + + expect(completeCallback).toHaveBeenCalled(); + }); + + it("calls a provided garbage collection function with the complete callback when done", function() { + var fn = jasmine.createSpy('fn'), + completeCallback = jasmine.createSpy('completeCallback'), + encourageGC = jasmine.createSpy('encourageGC'), + queueRunner = new jasmine.QueueRunner({ + fns: [fn], + encourageGC: encourageGC, + onComplete: completeCallback + }); + + queueRunner.execute(); + + expect(encourageGC).toHaveBeenCalledWith(completeCallback); + }); +}); diff --git a/spec/core/QueueSpec.js b/spec/core/QueueSpec.js deleted file mode 100644 index 59a70f39..00000000 --- a/spec/core/QueueSpec.js +++ /dev/null @@ -1,23 +0,0 @@ -describe("jasmine.Queue", function() { - it("should not call itself recursively, so we don't get stack overflow errors", function() { - var queue = new jasmine.Queue(new jasmine.Env()); - queue.add(new jasmine.Block(null, function() {})); - queue.add(new jasmine.Block(null, function() {})); - queue.add(new jasmine.Block(null, function() {})); - queue.add(new jasmine.Block(null, function() {})); - - var nestCount = 0; - var maxNestCount = 0; - var nextCallCount = 0; - queue.next_ = function() { - nestCount++; - if (nestCount > maxNestCount) maxNestCount = nestCount; - - jasmine.Queue.prototype.next_.apply(queue, arguments); - nestCount--; - }; - - queue.start(); - expect(maxNestCount).toEqual(1); - }); -}); \ No newline at end of file diff --git a/spec/core/ReportDispatcherSpec.js b/spec/core/ReportDispatcherSpec.js new file mode 100644 index 00000000..62b0d463 --- /dev/null +++ b/spec/core/ReportDispatcherSpec.js @@ -0,0 +1,40 @@ +describe("ReportDispatcher", function() { + + it("builds an interface of requested methods", function() { + var dispatcher = new jasmine.ReportDispatcher(['foo', 'bar', 'baz']); + + expect(dispatcher.foo).toBeDefined(); + expect(dispatcher.bar).toBeDefined(); + expect(dispatcher.baz).toBeDefined(); + }); + + it("dispatches requested methods to added reporters", function() { + var dispatcher = new jasmine.ReportDispatcher(['foo', 'bar']), + reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']), + anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']); + + dispatcher.addReporter(reporter); + dispatcher.addReporter(anotherReporter); + + dispatcher.foo(123, 456); + + expect(reporter.foo).toHaveBeenCalledWith(123, 456); + expect(anotherReporter.foo).toHaveBeenCalledWith(123, 456); + + dispatcher.bar('a', 'b'); + + expect(reporter.bar).toHaveBeenCalledWith('a', 'b'); + expect(anotherReporter.bar).toHaveBeenCalledWith('a', 'b'); + }); + + it("does not dispatch to a reporter if the reporter doesn't accept the method", function() { + var dispatcher = new jasmine.ReportDispatcher(['foo']), + reporter = jasmine.createSpyObj('reporter', ['baz']); + + dispatcher.addReporter(reporter); + + expect(function() { + dispatcher.foo(123, 456); + }).not.toThrow(); + }); +}); \ No newline at end of file diff --git a/spec/core/ReporterSpec.js b/spec/core/ReporterSpec.js deleted file mode 100644 index 26cdd17c..00000000 --- a/spec/core/ReporterSpec.js +++ /dev/null @@ -1,56 +0,0 @@ -describe('jasmine.Reporter', function() { - var env; - - - beforeEach(function() { - env = new jasmine.Env(); - env.updateInterval = 0; - }); - - it('should get called from the test runner', function() { - env.describe('Suite for JSON Reporter with Callbacks', function () { - env.it('should be a test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - env.it('should be a failing test', function() { - this.runs(function () { - this.expect(false).toEqual(true); - }); - }); - }); - env.describe('Suite for JSON Reporter with Callbacks 2', function () { - env.it('should be a test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - - }); - - var foo = 0; - var bar = 0; - var baz = 0; - - env.addReporter({ - reportSpecResults: function() { - foo++; - }, - reportSuiteResults: function() { - bar++; - }, - reportRunnerResults: function() { - baz++; - } - }); - - var runner = env.currentRunner(); - runner.execute(); - - expect(foo).toEqual(3); // 'foo was expected to be 3, was ' + foo); - expect(bar).toEqual(2); // 'bar was expected to be 2, was ' + bar); - expect(baz).toEqual(1); // 'baz was expected to be 1, was ' + baz); - }); - -}); \ No newline at end of file diff --git a/spec/core/RunnerSpec.js b/spec/core/RunnerSpec.js deleted file mode 100644 index 090a15ea..00000000 --- a/spec/core/RunnerSpec.js +++ /dev/null @@ -1,280 +0,0 @@ -describe('RunnerTest', function() { - var fakeTimer; - var env; - - beforeEach(function() { - env = new jasmine.Env(); - env.updateInterval = 0; - - fakeTimer = new jasmine.FakeTimer(); - env.setTimeout = fakeTimer.setTimeout; - env.clearTimeout = fakeTimer.clearTimeout; - env.setInterval = fakeTimer.setInterval; - env.clearInterval = fakeTimer.clearInterval; - }); - - describe('beforeEach', function() { - it('should run before each spec for all suites', function () { - var foo; - env.beforeEach(function () { - foo = 0; - }); - - env.describe('suite 1', function () { - env.it('test 1-1', function() { - foo++; - this.expect(foo).toEqual(1); - }); - env.it('test 1-2', function() { - foo++; - this.expect(foo).toEqual(1); - }); - }); - - env.describe('suite 2', function () { - env.it('test 2-1', function() { - foo++; - this.expect(foo).toEqual(1); - }); - }); - - env.currentRunner().execute(); - - var runnerResults = env.currentRunner().results(); - expect(runnerResults.totalCount).toEqual(3); - expect(runnerResults.passedCount).toEqual(3); - }); - - - it('should provide all specs', function () { - var foo; - env.beforeEach(function () { - foo = 0; - }); - - env.describe('suite 1', function () { - env.it('test 1-1', function() { - foo++; - this.expect(foo).toEqual(1); - }); - env.it('test 1-2', function() { - foo++; - this.expect(foo).toEqual(1); - }); - }); - - env.describe('suite 2', function () { - env.it('test 2-1', function() { - foo++; - this.expect(foo).toEqual(1); - }); - }); - - env.currentRunner().execute(); - - - expect(env.currentRunner().specs().length).toEqual(3); - }); - }); - - describe('afterEach', function() { - it('should run after each spec for all suites', function () { - var foo = 3; - env.afterEach(function () { - foo = foo - 1; - }); - - env.describe('suite 1', function () { - env.it('test 1-1', function() { - this.expect(foo).toEqual(3); - }); - env.it('test 1-2', function() { - this.expect(foo).toEqual(2); - }); - }); - - env.describe('suite 2', function () { - env.it('test 2-1', function() { - this.expect(foo).toEqual(1); - }); - }); - - env.currentRunner().execute(); - - var runnerResults = env.currentRunner().results(); - expect(runnerResults.totalCount).toEqual(3); - expect(runnerResults.passedCount).toEqual(3); - }); - - it('should run after a failing spec', function () { - var afterEach = jasmine.createSpy(); - env.afterEach(afterEach); - - env.describe('suite', function () { - env.it('fails', function () { - this.explodes(); - }); - }).execute(); - - expect(afterEach).toHaveBeenCalled(); - }); - }); - - - it('should run child suites and specs and generate results when execute is called', function() { - env.describe('one suite description', function () { - env.it('should be a test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - }); - - env.describe('another suite description', function () { - env.it('should be another test', function() { - this.runs(function () { - this.expect(true).toEqual(false); - }); - }); - }); - - env.currentRunner().execute(); - - var runnerResults = env.currentRunner().results(); - expect(runnerResults.totalCount).toEqual(2); - expect(runnerResults.passedCount).toEqual(1); - expect(runnerResults.failedCount).toEqual(1); - }); - - - it('should ignore suites that have been x\'d', function() { - env.xdescribe('one suite description', function () { - env.it('should be a test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - }); - - env.describe('another suite description', function () { - env.it('should be another test', function() { - this.runs(function () { - this.expect(true).toEqual(false); - }); - }); - }); - - env.currentRunner().execute(); - - var runnerResults = env.currentRunner().results(); - expect(runnerResults.totalCount).toEqual(1); - expect(runnerResults.passedCount).toEqual(0); - expect(runnerResults.failedCount).toEqual(1); - }); - - it('should roll up results from all specs', function() { - env.describe('one suite description', function () { - env.it('should be a test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - }); - - env.describe('another suite description', function () { - env.it('should be another test', function() { - this.runs(function () { - this.expect(true).toEqual(false); - }); - }); - }); - - env.currentRunner().execute(); - - var results = env.currentRunner().results(); - expect(results.totalCount).toEqual(2); - expect(results.passedCount).toEqual(1); - expect(results.failedCount).toEqual(1); - }); - - describe('reporting', function () { - var fakeReporter; - beforeEach(function () { - fakeReporter = jasmine.createSpyObj("fakeReporter", ["log", "reportRunnerStarting", "reportRunnerResults"]); - env.addReporter(fakeReporter); - }); - - it('should report runner results when the runner has completed running', function() { - env.describe('one suite description', function () { - env.it('should be a test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - }); - - env.describe('another suite description', function () { - env.it('should be another test', function() { - this.waits(200); - this.runs(function () { - this.expect(true).toEqual(false); - }); - }); - }); - - env.currentRunner().execute(); - expect(fakeReporter.reportRunnerResults).not.toHaveBeenCalled(); - fakeTimer.tick(200); - //This blows up the JSApiReporter. - //expect(fakeReporter.reportRunnerResults).toHaveBeenCalledWith(env.currentRunner); - expect(fakeReporter.reportRunnerResults).toHaveBeenCalled(); - expect(fakeReporter.reportRunnerResults.mostRecentCall.args[0].results()).toEqual(env.currentRunner().results()); - }); - }); - - it("should report when the tests start running", function() { - var fakeReporter = jasmine.createSpyObj("fakeReporter", ["log", "reportRunnerStarting"]); - env.addReporter(fakeReporter); - - - var runner = new jasmine.Runner(env); - runner.arbitraryVariable = 'foo'; - spyOn(runner.queue, 'start'); - expect(fakeReporter.reportRunnerStarting).not.toHaveBeenCalled(); - runner.execute(); - expect(fakeReporter.reportRunnerStarting).toHaveBeenCalled(); - var reportedRunner = fakeReporter.reportRunnerStarting.mostRecentCall.args[0]; - expect(reportedRunner.arbitraryVariable).toEqual('foo'); - expect(runner.queue.start).toHaveBeenCalled(); - }); - - describe("when suites are nested", function() { - var suite1, suite2, suite3; - - function suiteNames(suites) { - var suiteDescriptions = []; - for (var i = 0; i < suites.length; i++) { - suiteDescriptions.push(suites[i].getFullName()); - } - return suiteDescriptions; - } - - beforeEach(function() { - suite1 = env.describe("suite 1", function() { - suite2 = env.describe("suite 2", function() { - }); - }); - suite3 = env.describe("suite 3", function() {}); - }); - - it("#suites should return a flat array of all suites, including nested suites", function() { - var suites = env.currentRunner().suites(); - expect(suiteNames(suites)).toEqual([suite1.getFullName(), suite2.getFullName(), suite3.getFullName()]); - }); - - it("#topLevelSuites should return a flat array of all top-level suites only", function() { - var suites = env.currentRunner().topLevelSuites(); - expect(suiteNames(suites)).toEqual([suite1.getFullName(), suite3.getFullName()]); - }); - }); -}); \ No newline at end of file diff --git a/spec/core/SpecRunningSpec.js b/spec/core/SpecRunningSpec.js index a0268224..32e69f7c 100644 --- a/spec/core/SpecRunningSpec.js +++ b/spec/core/SpecRunningSpec.js @@ -1,3 +1,4 @@ +// TODO: This should really be part of the Env Integration Spec describe("jasmine spec running", function () { var env; var fakeTimer; @@ -5,12 +6,6 @@ describe("jasmine spec running", function () { beforeEach(function() { env = new jasmine.Env(); env.updateInterval = 0; - - fakeTimer = new jasmine.FakeTimer(); - env.setTimeout = fakeTimer.setTimeout; - env.clearTimeout = fakeTimer.clearTimeout; - env.setInterval = fakeTimer.setInterval; - env.clearInterval = fakeTimer.clearInterval; }); it('should assign spec ids sequentially', function() { @@ -37,626 +32,6 @@ describe("jasmine spec running", function () { expect(it4.id).toEqual(4); }); - it("should build up some objects with results we can inspect", function() { - - var specWithNoBody, specWithExpectation, specWithFailingExpectations, specWithMultipleExpectations; - - var suite = env.describe('default current suite', function() { - specWithNoBody = env.it('new spec'); - - specWithExpectation = env.it('spec with an expectation').runs(function () { - var foo = 'bar'; - this.expect(foo).toEqual('bar'); - }); - - specWithFailingExpectations = env.it('spec with failing expectation').runs(function () { - var foo = 'bar'; - this.expect(foo).toEqual('baz'); - }); - - specWithMultipleExpectations = env.it('spec with multiple expectations').runs(function () { - var foo = 'bar'; - var baz = 'quux'; - - this.expect(foo).toEqual('bar'); - this.expect(baz).toEqual('quux'); - }); - }); - - suite.execute(); - - expect(specWithNoBody.description).toEqual('new spec'); - - expect(specWithExpectation.results().getItems().length).toEqual(1); // "Results aren't there after a spec was executed" - expect(specWithExpectation.results().getItems()[0].passed()).toEqual(true); // "Results has a result, but it's true" - expect(specWithExpectation.results().description).toEqual('spec with an expectation'); // "Spec's results did not get the spec's description" - - expect(specWithFailingExpectations.results().getItems()[0].passed()).toEqual(false); // "Expectation that failed, passed" - - expect(specWithMultipleExpectations.results().getItems().length).toEqual(2); // "Spec doesn't support multiple expectations" - }); - - it("should work without a runs block", function() { - var another_spec; - env.describe('default current suite', function() { - another_spec = env.it('spec with an expectation', function () { - var foo = 'bar'; - this.expect(foo).toEqual('bar'); - this.expect(foo).toEqual('baz'); - }); - }); - - another_spec.execute(); - another_spec.done = true; - - expect(another_spec.results().getItems().length).toEqual(2); - expect(another_spec.results().getItems()[0].passed()).toEqual(true); // "In a spec without a run block, expected first expectation result to be true but was false" - expect(another_spec.results().getItems()[1].passed()).toEqual(false); // "In a spec without a run block, expected second expectation result to be false but was true"; - expect(another_spec.results().description).toEqual('spec with an expectation'); // "In a spec without a run block, results did not include the spec's description"; - }); - - it('should queue waits and runs that it encounters while executing specs', function() { - var specWithRunsAndWaits; - var foo = 0; - env.describe('test async spec', function() { - specWithRunsAndWaits = env.it('spec w/ queued statments', function () { - this.runs(function () { - foo++; - }); - this.waits(500); - this.runs(function () { - foo++; - }); - this.waits(500); - this.runs(function () { - foo++; - }); - }); - }); - - expect(foo).toEqual(0); - specWithRunsAndWaits.execute(); - - expect(foo).toEqual(1); - fakeTimer.tick(500); - expect(foo).toEqual(2); - fakeTimer.tick(500); - expect(foo).toEqual(3); - }); - - it("should run asynchronous tests", function () { - var foo = 0; - - var a_spec; - env.describe('test async spec', function() { - a_spec = env.it('spec w/ queued statments', function () { - this.runs(function () { - foo++; - }); - this.runs(function () { - this.expect(foo).toEqual(1); - }); - }); - }); - - a_spec.execute(); - - expect(a_spec.results().getItems().length).toEqual(1); // 'No call to waits(): Spec queue did not run all functions'; - expect(a_spec.results().getItems()[0].passed()).toEqual(true); // 'No call to waits(): Queued expectation failed'; - - foo = 0; - env.describe('test async spec', function() { - a_spec = env.it('spec w/ queued statments', function () { - this.runs(function () { - fakeTimer.setTimeout(function() { - foo++; - }, 500); - }); - this.waits(1000); - this.runs(function() { - this.expect(foo).toEqual(1); - }); - }); - }); - - a_spec.execute(); - - expect(a_spec.results().getItems().length).toEqual(0); - - fakeTimer.tick(500); - expect(a_spec.results().getItems().length).toEqual(0); - - fakeTimer.tick(500); - expect(a_spec.results().getItems().length).toEqual(1); // 'Calling waits(): Spec queue did not run all functions'; - - expect(a_spec.results().getItems()[0].passed()).toEqual(true); // 'Calling waits(): Queued expectation failed'; - - var bar = 0; - var another_spec; - env.describe('test async spec', function() { - another_spec = env.it('spec w/ queued statments', function () { - this.runs(function () { - fakeTimer.setTimeout(function() { - bar++; - }, 250); - - }); - this.waits(500); - this.runs(function () { - fakeTimer.setTimeout(function() { - bar++; - }, 250); - }); - this.waits(500); - this.runs(function () { - this.expect(bar).toEqual(2); - }); - }); - }); - - - another_spec.execute(); - - fakeTimer.tick(1000); - - expect(another_spec.results().getItems().length).toEqual(1); - expect(another_spec.results().getItems()[0].passed()).toEqual(true); - - var baz = 0; - var yet_another_spec; - env.describe('test async spec', function() { - yet_another_spec = env.it('spec w/ async fail', function () { - this.runs(function () { - fakeTimer.setTimeout(function() { - baz++; - }, 250); - }); - this.waits(100); - this.runs(function() { - this.expect(baz).toEqual(1); - }); - }); - }); - - - yet_another_spec.execute(); - //tick twice so that second runs gets eval'd first: mockClock bug? - fakeTimer.tick(100); - fakeTimer.tick(150); - - - expect(yet_another_spec.results().getItems().length).toEqual(1); - expect(yet_another_spec.results().getItems()[0].passed()).toEqual(false); - }); - - it("testAsyncSpecsWithMockSuite", function () { - var bar = 0; - var another_spec; - env.describe('test async spec', function() { - another_spec = env.it('spec w/ queued statments', function () { - this.runs(function () { - fakeTimer.setTimeout(function() { - bar++; - }, 250); - }); - this.waits(500); - this.runs(function () { - fakeTimer.setTimeout(function() { - bar++; - }, 250); - }); - this.waits(1500); - this.runs(function() { - this.expect(bar).toEqual(2); - }); - }); - }); - - another_spec.execute(); - fakeTimer.tick(2000); - expect(another_spec.results().getItems().length).toEqual(1); - expect(another_spec.results().getItems()[0].passed()).toEqual(true); - }); - - describe("waitsFor", function() { - var latchFunction = function() { - return true; - }; - var spec; - - function makeWaitsForSpec() { - var args = jasmine.util.argsToArray(arguments); - env.describe('suite', function() { - spec = env.it('spec', function() { - this.waitsFor.apply(this, args); - }); - }); - env.execute(); - } - - it("should accept args (latchFunction, timeoutMessage, timeout)", function() { - makeWaitsForSpec(latchFunction, "message", 123); - var block = spec.queue.blocks[1]; - expect(block.latchFunction).toBe(latchFunction); - expect(block.timeout).toEqual(123); - expect(block.message).toEqual('message'); - }); - - it("should accept args (latchFunction, timeout)", function() { - makeWaitsForSpec(latchFunction, 123); - var block = spec.queue.blocks[1]; - expect(block.latchFunction).toBe(latchFunction); - expect(block.timeout).toEqual(123); - expect(block.message).toEqual(null); - }); - - it("should accept args (latchFunction, timeoutMessage)", function() { - env.defaultTimeoutInterval = 4321; - makeWaitsForSpec(latchFunction, "message"); - var block = spec.queue.blocks[1]; - expect(block.latchFunction).toBe(latchFunction); - expect(block.timeout).toEqual(4321); - expect(block.message).toEqual('message'); - }); - - it("should accept args (latchFunction)", function() { - env.defaultTimeoutInterval = 4321; - makeWaitsForSpec(latchFunction); - var block = spec.queue.blocks[1]; - expect(block.latchFunction).toBe(latchFunction); - expect(block.timeout).toEqual(4321); - expect(block.message).toEqual(null); - }); - - it("should accept deprecated args order (timeout, latchFunction, timeoutMessage)", function() { - makeWaitsForSpec(123, latchFunction, "message"); - var block = spec.queue.blocks[1]; - expect(block.latchFunction).toBe(latchFunction); - expect(block.timeout).toEqual(123); - expect(block.message).toEqual('message'); - }); - - it("testWaitsFor", function() { - var doneWaiting = false; - var runsBlockExecuted = false; - - var spec; - env.describe('foo', function() { - spec = env.it('has a waits for', function() { - this.runs(function() { - }); - - this.waitsFor(500, function() { - return doneWaiting; - }); - - this.runs(function() { - runsBlockExecuted = true; - }); - }); - }); - - spec.execute(); - expect(runsBlockExecuted).toEqual(false); //, 'should not have executed runs block yet'); - fakeTimer.tick(100); - doneWaiting = true; - fakeTimer.tick(100); - expect(runsBlockExecuted).toEqual(true); //, 'should have executed runs block'); - }); - - it("fails with message", function() { - var spec; - env.describe('foo', function() { - spec = env.it('has a waits for', function() { - this.runs(function() { - }); - - this.waitsFor(500, function() { - return false; // force a timeout - }, 'my awesome condition'); - - this.runs(function() { - }); - }); - }); - - spec.execute(); - fakeTimer.tick(1000); - expect(spec.results().getItems()[0].message).toEqual('timeout: timed out after 500 msec waiting for my awesome condition'); - }); - - it("fails and skips the rest of the spec if timeout is reached and the latch function hasn't returned true", function() { - var runsBlockExecuted = false; - var subsequentSpecRan = false; - - var timeoutSpec, subsequentSpec; - var suite = env.describe('foo', function() { - timeoutSpec = env.it('has a waits for', function() { - this.runs(function() { - }); - - this.waitsFor(500, function() { - return false; - }); - - this.runs(function() { - runsBlockExecuted = true; - }); - }); - - subsequentSpec = env.it('then carries on to the next test', function() { - subsequentSpecRan = true; - }); - }); - - env.execute(); - expect(runsBlockExecuted).toEqual(false); - fakeTimer.tick(100); - expect(runsBlockExecuted).toEqual(false); - fakeTimer.tick(400); - expect(runsBlockExecuted).toEqual(false); - expect(timeoutSpec.results().getItems()[0].message).toEqual('timeout: timed out after 500 msec waiting for something to happen'); - expect(subsequentSpecRan).toEqual(true); - }); - - it("runs afterEach after timing out", function() { - var afterEach = jasmine.createSpy('afterEach'); - - env.describe('foo', function () { - env.afterEach(afterEach); - - env.it('waitsFor', function () { - this.waitsFor(100, function() { - return false; - }); - }); - }).execute(); - - fakeTimer.tick(500); - expect(afterEach).toHaveBeenCalled(); - }); - - it("runs single-spec after functions after timing out", function() { - var after = jasmine.createSpy('after'); - - env.describe('foo', function () { - env.it('waitsFor', function () { - this.after(after); - this.waitsFor(100, function() { - return false; - }); - }); - }).execute(); - - fakeTimer.tick(500); - expect(after).toHaveBeenCalled(); - }); - - describe('with consecutive calls', function () { - var foo; - beforeEach(function () { - foo = 0; - }); - - it('exits immediately (does not stack) if the latchFunction times out', function () { - var reachedFirstWaitsFor = false; - var reachedSecondWaitsFor = false; - env.describe('suite that waits', function () { - env.it('should stack timeouts', function() { - this.waitsFor(500, function () { - reachedFirstWaitsFor = true; - return false; - }); - this.waitsFor(500, function () { - reachedSecondWaitsFor = true; - }); - this.runs(function () { - foo++; - }); - }); - }); - - expect(reachedFirstWaitsFor).toEqual(false); - env.execute(); - - expect(reachedFirstWaitsFor).toEqual(true); - expect(foo).toEqual(0); - expect(reachedSecondWaitsFor).toEqual(false); - fakeTimer.tick(500); - expect(reachedSecondWaitsFor).toEqual(false); - expect(foo).toEqual(0); - fakeTimer.tick(500); - expect(reachedSecondWaitsFor).toEqual(false); - expect(foo).toEqual(0); - }); - - it('stacks latchFunctions', function () { - var firstWaitsResult = false; - var secondWaitsResult = false; - var waitsSuite = env.describe('suite that waits', function () { - env.it('spec with waitsFors', function() { - this.waitsFor(600, function () { - fakeTimer.setTimeout(function () { - firstWaitsResult = true; - }, 300); - return firstWaitsResult; - }); - this.waitsFor(600, function () { - fakeTimer.setTimeout(function () { - secondWaitsResult = true; - }, 300); - return secondWaitsResult; - }); - this.runs(function () { - foo++; - }); - }); - }); - - expect(firstWaitsResult).toEqual(false); - expect(secondWaitsResult).toEqual(false); - waitsSuite.execute(); - - expect(firstWaitsResult).toEqual(false); - expect(secondWaitsResult).toEqual(false); - expect(foo).toEqual(0); - - fakeTimer.tick(300); - - expect(firstWaitsResult).toEqual(true); - expect(secondWaitsResult).toEqual(false); - expect(foo).toEqual(0); - - fakeTimer.tick(300); - - expect(firstWaitsResult).toEqual(true); - expect(secondWaitsResult).toEqual(true); - expect(foo).toEqual(1); - - }); - }); - }); - - it("testSpecAfter", function() { - var log = ""; - var spec; - var suite = env.describe("has after", function() { - spec = env.it('spec with after', function() { - this.runs(function() { - log += "spec"; - }); - }); - }); - spec.after(function() { - log += "after1"; - }); - spec.after(function() { - log += "after2"; - }); - - suite.execute(); - - expect(log).toEqual("specafter2after1"); - }); - - describe('test suite declaration', function() { - var suite; - var dummyFunction = function() { - }; - - it('should give the suite a description', function() { - suite = env.describe('one suite description', dummyFunction); - expect(suite.description).toEqual('one suite description'); - }); - - it('should enqueue functions for multipart tests and support waits, and run any ready runs() blocks', function() { - var foo = 0; - var bar = 0; - - suite = env.describe('one suite description', function () { - env.it('should be a test with queuedFunctions', function() { - this.runs(function() { - foo++; - }); - this.waits(100); - this.runs(function() { - bar++; - }); - }); - }); - - suite.execute(); - - expect(foo).toEqual(1); - expect(bar).toEqual(0); - - fakeTimer.tick(100); - expect(bar).toEqual(1); - }); - - }); - - it("testBeforeAndAfterCallbacks", function () { - var suiteWithBefore = env.describe('one suite with a before', function () { - - this.beforeEach(function () { - this.foo = 1; - }); - - env.it('should be a spec', function () { - this.runs(function() { - this.foo++; - this.expect(this.foo).toEqual(2); - }); - }); - - env.it('should be another spec', function () { - this.runs(function() { - this.foo++; - this.expect(this.foo).toEqual(2); - }); - }); - }); - - suiteWithBefore.execute(); - - var suite = suiteWithBefore; - - expect(suite.results().getItems()[0].passed()).toEqual(true); // "testBeforeAndAfterCallbacks: the first spec's foo should have been 2"); - expect(suite.results().getItems()[1].passed()).toEqual(true); // "testBeforeAndAfterCallbacks: the second spec's this.foo should have been 2"); - - - var foo = 1; - var suiteWithAfter = env.describe('one suite with an after_each', function () { - - env.it('should be a spec with an after_each', function () { - this.expect(foo).toEqual(1); - foo++; - this.expect(foo).toEqual(2); - }); - - env.it('should be another spec with an after_each', function () { - this.expect(foo).toEqual(0); - foo++; - this.expect(foo).toEqual(1); - }); - - this.afterEach(function () { - foo = 0; - }); - }); - - suiteWithAfter.execute(); - - suite = suiteWithAfter; - expect(suite.afterEach.length).toEqual(1); - expect(suite.results().getItems()[0].passed()).toEqual(true); - expect(suite.results().getItems()[1].passed()).toEqual(true); - expect(foo).toEqual(0); - - }); - - it('#waits should allow consecutive waits calls', function () { - var foo = 0; - var waitsSuite = env.describe('suite that waits', function () { - env.it('should wait', function() { - this.waits(500); - this.waits(500); - this.runs(function () { - foo++; - }); - }); - }); - - waitsSuite.execute(); - expect(foo).toEqual(0); - fakeTimer.tick(500); - expect(foo).toEqual(0); - fakeTimer.tick(500); - - expect(foo).toEqual(1); - }); - it('nested suites', function () { var foo = 0; @@ -696,271 +71,15 @@ describe("jasmine spec running", function () { expect(quux).toEqual(1); }); - it("#beforeEach should be able to eval runs and waits blocks", function () { - var foo = 0; - var bar = 0; - var suiteWithBefore = env.describe('one suite with a before', function () { - this.beforeEach(function () { - this.runs(function () { - foo++; - }); - this.waits(500); - this.runs(function () { - foo++; - }); - this.waits(500); - }); - - env.it('should be a spec', function () { - bar = 1; - foo++; - }); - - }); - - expect(foo).toEqual(0); - expect(bar).toEqual(0); - suiteWithBefore.execute(); - - expect(bar).toEqual(0); - expect(foo).toEqual(1); - fakeTimer.tick(500); - - expect(bar).toEqual(0); - expect(foo).toEqual(2); - fakeTimer.tick(500); - expect(bar).toEqual(1); - expect(foo).toEqual(3); - }); - - it("#afterEach should be able to eval runs and waits blocks", function () { - var foo = 0; - var firstSpecHasRun = false; - var secondSpecHasRun = false; - var suiteWithAfter = env.describe('one suite with a before', function () { - this.afterEach(function () { - this.waits(500); - this.runs(function () { - foo++; - }); - this.waits(500); - }); - - env.it('should be the first spec', function () { - firstSpecHasRun = true; - }); - - env.it('should be a spec', function () { - secondSpecHasRun = true; - foo++; - }); - - }); - - expect(firstSpecHasRun).toEqual(false); - expect(secondSpecHasRun).toEqual(false); - expect(foo).toEqual(0); - - suiteWithAfter.execute(); - - - expect(firstSpecHasRun).toEqual(true); - expect(secondSpecHasRun).toEqual(false); - expect(foo).toEqual(0); - - fakeTimer.tick(500); - - expect(foo).toEqual(1); - expect(secondSpecHasRun).toEqual(false); - fakeTimer.tick(500); - - expect(foo).toEqual(2); - expect(secondSpecHasRun).toEqual(true); - - }); - - it("Spec#after should be able to eval runs and waits blocks", function () { - var runsBeforeAfter = false; - var firstSpecHasRun = false; - var secondSpecHasRun = false; - var afterHasRun = false; - var suiteWithAfter = env.describe('one suite with a before', function () { - - env.it('should be the first spec', function () { - firstSpecHasRun = true; - this.after(function () { - this.waits(500); - this.runs(function () { - afterHasRun = true; - }); - this.waits(500); - }, true); - this.waits(500); - this.runs(function () { - runsBeforeAfter = true; - }); - }); - - env.it('should be a spec', function () { - secondSpecHasRun = true; - }); - - }); - - expect(firstSpecHasRun).toEqual(false); - expect(runsBeforeAfter).toEqual(false); - expect(afterHasRun).toEqual(false); - expect(secondSpecHasRun).toEqual(false); - - suiteWithAfter.execute(); - - expect(firstSpecHasRun).toEqual(true); - expect(runsBeforeAfter).toEqual(false); - expect(afterHasRun).toEqual(false); - expect(secondSpecHasRun).toEqual(false); - - fakeTimer.tick(500); - - expect(firstSpecHasRun).toEqual(true); - expect(runsBeforeAfter).toEqual(true); - expect(afterHasRun).toEqual(false); - expect(secondSpecHasRun).toEqual(false); - - fakeTimer.tick(500); - - expect(firstSpecHasRun).toEqual(true); - expect(runsBeforeAfter).toEqual(true); - expect(afterHasRun).toEqual(true); - expect(secondSpecHasRun).toEqual(false); - - fakeTimer.tick(500); - - expect(firstSpecHasRun).toEqual(true); - expect(runsBeforeAfter).toEqual(true); - expect(afterHasRun).toEqual(true); - expect(secondSpecHasRun).toEqual(true); - }); - - it("handles waits", function () { - var firstSpecHasRun = false; - var secondSpecHasRun = false; - var suiteWithAfter = env.describe('one suite with a before', function () { - - env.it('should be the first spec', function () { - this.waits(500); - this.runs(function () { - firstSpecHasRun = true; - }); - }); - - env.it('should be a spec', function () { - secondSpecHasRun = true; - }); - - }); - - expect(firstSpecHasRun).toEqual(false); - expect(secondSpecHasRun).toEqual(false); - - suiteWithAfter.execute(); - - expect(firstSpecHasRun).toEqual(false); - expect(secondSpecHasRun).toEqual(false); - - fakeTimer.tick(500); - - expect(firstSpecHasRun).toEqual(true); - expect(secondSpecHasRun).toEqual(true); - }); - - it("testBeforeExecutesSafely", function() { - var report = ""; - var suite = env.describe('before fails on first test, passes on second', function() { - var counter = 0; - this.beforeEach(function() { - counter++; - if (counter == 1) { - throw "before failure"; - } - }); - env.it("first should not run because before fails", function() { - this.runs(function() { - report += "first"; - this.expect(true).toEqual(true); - }); - }); - env.it("second should run and pass because before passes", function() { - this.runs(function() { - report += "second"; - this.expect(true).toEqual(true); - }); - }); - }); - - suite.execute(); - - expect(report).toEqual("firstsecond"); - var suiteResults = suite.results(); - expect(suiteResults.getItems()[0].getItems()[0].passed()).toEqual(false); - expect(suiteResults.getItems()[1].getItems()[0].passed()).toEqual(true); - }); - - it("testAfterExecutesSafely", function() { - var report = ""; - var suite = env.describe('after fails on first test, then passes', function() { - var counter = 0; - this.afterEach(function() { - counter++; - if (counter == 1) { - throw "after failure"; - } - }); - env.it("first should run, expectation passes, but spec fails because after fails", function() { - this.runs(function() { - report += "first"; - this.expect(true).toEqual(true); - }); - }); - env.it("second should run and pass because after passes", function() { - this.runs(function() { - report += "second"; - this.expect(true).toEqual(true); - }); - }); - env.it("third should run and pass because after passes", function() { - this.runs(function() { - report += "third"; - this.expect(true).toEqual(true); - }); - }); - }); - - suite.execute(); - - expect(report).toEqual("firstsecondthird"); // "all tests should run"); - //After each errors should not go in spec results because it confuses the count. - var suiteResults = suite.results(); - expect(suiteResults.getItems().length).toEqual(3, 'testAfterExecutesSafely should have results for three specs'); - - expect(suiteResults.getItems()[0].getItems()[0].passed()).toEqual(true, "testAfterExecutesSafely 1st spec should pass"); - expect(suiteResults.getItems()[1].getItems()[0].passed()).toEqual(true, "testAfterExecutesSafely 2nd spec should pass"); - expect(suiteResults.getItems()[2].getItems()[0].passed()).toEqual(true, "testAfterExecutesSafely 3rd spec should pass"); - - expect(suiteResults.getItems()[0].getItems()[0].passed()).toEqual(true, "testAfterExecutesSafely 1st result for 1st suite spec should pass"); - expect(suiteResults.getItems()[0].getItems()[1].passed()).toEqual(false, "testAfterExecutesSafely 2nd result for 1st suite spec should fail because afterEach failed"); - expect(suiteResults.getItems()[1].getItems()[0].passed()).toEqual(true, "testAfterExecutesSafely 2nd suite spec should pass"); - expect(suiteResults.getItems()[2].getItems()[0].passed()).toEqual(true, "testAfterExecutesSafely 3rd suite spec should pass"); - }); - it("should permit nested describes", function() { var actions = []; env.beforeEach(function () { - actions.push('runner beforeEach'); + actions.push('topSuite beforeEach'); }); env.afterEach(function () { - actions.push('runner afterEach'); + actions.push('topSuite afterEach'); }); env.describe('Something', function() { @@ -1013,33 +132,33 @@ describe("jasmine spec running", function () { var expected = [ - "runner beforeEach", + "topSuite beforeEach", "outer beforeEach", "outer it 1", "outer afterEach", - "runner afterEach", + "topSuite afterEach", - "runner beforeEach", + "topSuite beforeEach", "outer beforeEach", "inner 1 beforeEach", "inner 1 it", "inner 1 afterEach", "outer afterEach", - "runner afterEach", + "topSuite afterEach", - "runner beforeEach", + "topSuite beforeEach", "outer beforeEach", "outer it 2", "outer afterEach", - "runner afterEach", + "topSuite afterEach", - "runner beforeEach", + "topSuite beforeEach", "outer beforeEach", "inner 2 beforeEach", "inner 2 it", "inner 2 afterEach", "outer afterEach", - "runner afterEach" + "topSuite afterEach" ]; expect(actions).toEqual(expected); }); @@ -1101,146 +220,34 @@ describe("jasmine spec running", function () { expect(actions).toEqual(expected); }); - it("builds up nested names", function() { - var nestedSpec; - env.describe('Test Subject', function() { - env.describe('when under circumstance A', function() { - env.describe('and circumstance B', function() { - nestedSpec = env.it('behaves thusly', function() { - }); - }); + it("shouldn't run disabled suites", function() { + var specInADisabledSuite = originalJasmine.createSpy("specInADisabledSuite"), + suite = env.describe('A Suite', function() { + env.xdescribe('with a disabled suite', function(){ + env.it('disabled spec', specInADisabledSuite); }); }); - expect(nestedSpec.getFullName()).toEqual('Test Subject when under circumstance A and circumstance B behaves thusly.'); //, "Spec.fullName was incorrect: " + nestedSpec.getFullName()); - }); - - it("should skip empty suites", function () { - env.describe('NonEmptySuite1', function() { - env.it('should pass', function() { - this.expect(true).toEqual(true); - }); - env.describe('NestedEmptySuite', function() { - }); - env.it('should pass', function() { - this.expect(true).toEqual(true); - }); - }); - - env.describe('EmptySuite', function() { - }); - - env.describe('NonEmptySuite2', function() { - env.it('should pass', function() { - this.expect(true).toEqual(true); - }); - }); - - env.execute(); - - var runnerResults = env.currentRunner_.results(); - expect(runnerResults.totalCount).toEqual(3); - expect(runnerResults.passedCount).toEqual(3); - expect(runnerResults.failedCount).toEqual(0); - }); - - it("should bind 'this' to the running spec within the spec body", function() { - var spec; - var suite = env.describe('one suite description', function () { - env.it('should be a test with queuedFunctions', function() { - spec = this.runs(function() { - this.foo = 0; - this.foo++; - }); - - this.runs(function() { - var that = this; - fakeTimer.setTimeout(function() { - that.foo++; - }, 250); - }); - - this.runs(function() { - this.expect(this.foo).toEqual(2); - }); - - this.waits(300); - - this.runs(function() { - this.expect(this.foo).toEqual(2); - }); - }); - - }); - suite.execute(); - fakeTimer.tick(600); - expect(spec.foo).toEqual(2); - var suiteResults = suite.results(); - expect(suiteResults.getItems()[0].getItems().length).toEqual(2); - expect(suiteResults.getItems()[0].getItems()[0].passed()).toEqual(false); - expect(suiteResults.getItems()[0].getItems()[1].passed()).toEqual(true); + + expect(specInADisabledSuite).not.toHaveBeenCalled(); }); it("shouldn't run disabled tests", function() { - var xitSpecWasRun = false; - var suite = env.describe('default current suite', function() { - env.xit('disabled spec').runs(function () { - xitSpecWasRun = true; - }); - - env.it('enabled spec').runs(function () { - var foo = 'bar'; - expect(foo).toEqual('bar'); - }); + var disabledSpec = originalJasmine.createSpy('disabledSpec'), + suite = env.describe('default current suite', function() { + env.xit('disabled spec', disabledSpec); }); - suite.execute(); - expect(xitSpecWasRun).toEqual(false); + expect(disabledSpec).not.toHaveBeenCalled(); }); - it('shouldn\'t execute specs in disabled suites', function() { - var spy = jasmine.createSpy(); - var disabledSuite = env.xdescribe('a disabled suite', function() { - env.it('enabled spec, but should not be run', function() { - spy(); - }); - }); - - disabledSuite.execute(); - - expect(spy).not.toHaveBeenCalled(); - }); - - it('#explodes should throw an exception when it is called inside a spec', function() { - var exceptionMessage = false; - var anotherSuite = env.describe('Spec', function () { - env.it('plodes', function() { - try { - this.explodes(); - } - catch (e) { - exceptionMessage = e; - } - expect(exceptionMessage).toNotEqual(false); - }); - }); - - anotherSuite.execute(); - - expect(exceptionMessage).toEqual('explodes function should not have been called'); - }); - - it("should recover gracefully when there are errors in describe functions", function() { + // TODO: is this useful? It doesn't catch syntax errors + xit("should recover gracefully when there are errors in describe functions", function() { var specs = []; var superSimpleReporter = new jasmine.Reporter(); - superSimpleReporter.reportSpecResults = function(spec) { - specs.push("Spec: " + spec.getFullName()); - var results = spec.results().getItems(); - for (var i = 0; i < results.length; i++) { - var result = results[i]; - specs.push("Result: " + result); - } + superSimpleReporter.reportSpecResults = function(result) { + specs.push("Spec: " + result.fullName); }; try { @@ -1276,16 +283,11 @@ describe("jasmine spec running", function () { expect(specs.join('')).toMatch(new RegExp( 'Spec: outer1 inner1 should thingy.' + - 'Result: Passed.' + - 'Spec: outer1 inner1 encountered a declaration exception.' + - 'Result: Error: fake error.*' + - 'Spec: outer1 inner2 should other thingy.' + - 'Result: Passed.' + - 'Spec: outer1 encountered a declaration exception.' + - 'Result: Error: fake error.*' + - 'Spec: outer2 should xxx.' + - 'Result: Passed.' - )); - }); + 'Spec: outer1 inner1 encountered a declaration exception.' + + 'Spec: outer1 inner2 should other thingy.' + + 'Spec: outer1 encountered a declaration exception.' + + 'Spec: outer2 should xxx.' + )); -}); + }); +}); \ No newline at end of file diff --git a/spec/core/SpecSpec.js b/spec/core/SpecSpec.js index 1e4a22d4..949b81cb 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -1,124 +1,184 @@ -describe('Spec', function () { - var env, suite; - beforeEach(function() { - env = new jasmine.Env(); - env.updateInterval = 0; - suite = new jasmine.Suite(env, 'suite 1'); +describe("Spec", function() { + + it("delegates execution to a QueueRunner", function() { + var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), + spec = new jasmine.Spec({ + description: 'my test', + id: 'some-id', + fn: function() {}, + queueRunner: fakeQueueRunner + }); + + spec.execute(); + + expect(fakeQueueRunner).toHaveBeenCalled(); }); - describe('initialization', function () { + it("should call the start callback on execution", function() { + var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), + beforesWereCalled = false, + startCallback = originalJasmine.createSpy('startCallback'), + spec = new jasmine.Spec({ + id: 123, + description: 'foo bar', + fn: function() {}, + onStart: startCallback, + queueRunner: fakeQueueRunner + }); - it('should raise an error if an env is not passed', function () { - try { - new jasmine.Spec(); - } - catch (e) { - expect(e.message).toEqual('jasmine.Env() required'); - } - }); + spec.execute(); - it('should raise an error if a suite is not passed', function () { - try { - new jasmine.Spec(env); - } - catch (e) { - expect(e.message).toEqual('jasmine.Suite() required'); - } - }); + expect(startCallback).toHaveBeenCalledWith(spec); + }); - it('should assign sequential ids for specs belonging to the same env', function () { - var spec1 = new jasmine.Spec(env, suite); - var spec2 = new jasmine.Spec(env, suite); - var spec3 = new jasmine.Spec(env, suite); - expect(spec1.id).toEqual(0); - expect(spec2.id).toEqual(1); - expect(spec3.id).toEqual(2); + it("should call the start callback on execution but before any befores are called", function() { + var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), + beforesWereCalled = false, + startCallback = originalJasmine.createSpy('start-callback').andCallFake(function() { + expect(beforesWereCalled).toBe(false); + }), + spec = new jasmine.Spec({ + fn: function() {}, + beforeFns: function() { + return [function() { + beforesWereCalled = true + }] + }, + onStart: startCallback, + queueRunner: fakeQueueRunner + }); + + spec.execute(); + + expect(startCallback).toHaveBeenCalled(); + }); + + it("provides all before fns and after fns to be run", function() { + var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), + before = originalJasmine.createSpy('before'), + after = originalJasmine.createSpy('after'), + fn = originalJasmine.createSpy('test body').andCallFake(function() { + expect(before).toHaveBeenCalled(); + expect(after).not.toHaveBeenCalled(); + }), + spec = new jasmine.Spec({ + fn: fn, + beforeFns: function() { + return [before] + }, + afterFns: function() { + return [after] + }, + queueRunner: fakeQueueRunner + }); + + spec.execute(); + + var allSpecFns = fakeQueueRunner.mostRecentCall.args[0].fns; + expect(allSpecFns).toEqual([before, fn, after]); + }); + + it("can be disabled, but still calls callbacks", function() { + var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), + + startCallback = originalJasmine.createSpy('startCallback'), + specBody = originalJasmine.createSpy('specBody'), + resultCallback = originalJasmine.createSpy('resultCallback'), + spec = new jasmine.Spec({ + onStart:startCallback, + fn: specBody, + resultCallback: resultCallback, + queueRunner: fakeQueueRunner + }); + + spec.disable(); + + expect(spec.status()).toBe('disabled'); + + spec.execute(); + + expect(startCallback).not.toHaveBeenCalled(); + expect(fakeQueueRunner).not.toHaveBeenCalled(); + expect(specBody).not.toHaveBeenCalled(); + + expect(resultCallback).toHaveBeenCalled(); + }); + + it("should call the results callback on execution complete", function() { + var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), + + startCallback = originalJasmine.createSpy('startCallback'), + specBody = originalJasmine.createSpy('specBody'), + resultCallback = originalJasmine.createSpy('resultCallback'), + spec = new jasmine.Spec({ + onStart:startCallback, + fn: specBody, + resultCallback: resultCallback, + description: "with a spec", + getSpecName: function() { return "a suite with a spec"}, + queueRunner: fakeQueueRunner + }); + + spec.disable(); + + expect(spec.status()).toBe('disabled'); + + spec.execute(); + + expect(startCallback).not.toHaveBeenCalled(); + expect(fakeQueueRunner).not.toHaveBeenCalled(); + expect(specBody).not.toHaveBeenCalled(); + + expect(resultCallback).toHaveBeenCalledWith({ + id: spec.id, + status: 'disabled', + description: 'with a spec', + fullName: 'a suite with a spec', + failedExpectations: [] }); }); - it('getFullName returns suite & spec description', function () { - var spec = new jasmine.Spec(env, suite, 'spec 1'); - expect(spec.getFullName()).toEqual('suite 1 spec 1.'); + it("should call the done callback on execution complete", function() { + var done = originalJasmine.createSpy('done callback'), + spec = new jasmine.Spec({ + fn: function() {}, + catchExceptions: function() { return false; }, + resultCallback: function() {}, + queueRunner: function(attrs) { attrs.onComplete(); } + }); + + spec.execute(done); + + expect(done).toHaveBeenCalled(); }); - describe('results', function () { - var spec, results; - beforeEach(function () { - spec = new jasmine.Spec(env, suite); - results = spec.results(); - expect(results.totalCount).toEqual(0); - spec.runs(function () { - this.expect(true).toEqual(true); - this.expect(true).toEqual(true); - }); - }); - - - it('results shows the total number of expectations for each spec after execution', function () { - expect(results.totalCount).toEqual(0); - spec.execute(); - expect(results.totalCount).toEqual(2); - }); - - it('results shows the number of passed expectations for each spec after execution', function () { - expect(results.passedCount).toEqual(0); - spec.execute(); - expect(results.passedCount).toEqual(2); - }); - - it('results shows the number of failed expectations for each spec after execution', function () { - spec.runs(function () { - this.expect(true).toEqual(false); - }); - expect(results.failedCount).toEqual(0); - spec.execute(); - expect(results.failedCount).toEqual(1); - }); - - describe('results.passed', function () { - it('is true if all spec expectations pass', function () { - spec.runs(function () { - this.expect(true).toEqual(true); - }); - spec.execute(); - expect(results.passed()).toEqual(true); - }); - - it('is false if one spec expectation fails', function () { - spec.runs(function () { - this.expect(true).toEqual(false); - }); - spec.execute(); - expect(results.passed()).toEqual(false); - }); - - it('a spec with no expectations will return true', function () { - var specWithoutExpectations = new jasmine.Spec(env, suite); - specWithoutExpectations.runs(function() { - - }); - specWithoutExpectations.execute(); - expect(results.passed()).toEqual(true); - }); - - it('an unexecuted spec will return true', function () { - expect(results.passed()).toEqual(true); - }); - }); - - it("includes log messages, which may contain arbitary objects", function() { - spec.runs(function() { - this.log("here's some log message", {key: 'value'}, 123); - }); - spec.execute(); - var items = results.getItems(); - expect(items).toEqual([ - jasmine.any(jasmine.ExpectationResult), - jasmine.any(jasmine.ExpectationResult), - jasmine.any(jasmine.MessageResult) - ]); - var logResult = items[2]; - expect(logResult.values).toEqual(["here's some log message", {key: 'value'}, 123]); - }); + it("#status returns null by default", function() { + var spec = new jasmine.Spec({}); + expect(spec.status()).toBeNull(); }); -}); \ No newline at end of file + + it("#status returns passed if all expectations in the spec have passed", function() { + var spec = new jasmine.Spec({}); + spec.addExpectationResult(true); + expect(spec.status()).toBe('passed'); + }); + + it("#status returns failed if any expectations in the spec have failed", function() { + var spec = new jasmine.Spec({}); + spec.addExpectationResult(true); + spec.addExpectationResult(false); + expect(spec.status()).toBe('failed'); + }); + + it("can return its full name", function() { + var spec; + spec = new jasmine.Spec({ + getSpecName: function(passedVal) { +// expect(passedVal).toBe(spec); TODO: a exec time, spec is undefined WTF? + return 'expected val'; + } + }); + + expect(spec.getFullName()).toBe('expected val'); + }); +}); diff --git a/spec/core/SpySpec.js b/spec/core/SpySpec.js index 90439c4e..8d477895 100644 --- a/spec/core/SpySpec.js +++ b/spec/core/SpySpec.js @@ -1,4 +1,9 @@ describe('Spies', function () { + var env; + beforeEach(function() { + env = new jasmine.Env(); + }); + it('should replace the specified function with a spy object', function() { var originalFunctionWasCalled = false; var TestClass = { @@ -6,11 +11,13 @@ describe('Spies', function () { originalFunctionWasCalled = true; } }; - this.spyOn(TestClass, 'someFunction'); + env.spyOn(TestClass, 'someFunction'); expect(TestClass.someFunction.wasCalled).toEqual(false); expect(TestClass.someFunction.callCount).toEqual(0); + TestClass.someFunction('foo'); + expect(TestClass.someFunction.wasCalled).toEqual(true); expect(TestClass.someFunction.callCount).toEqual(1); expect(TestClass.someFunction.mostRecentCall.args).toEqual(['foo']); @@ -29,7 +36,7 @@ describe('Spies', function () { originalFunctionWasCalled = true; } }; - this.spyOn(TestClass, 'someFunction'); + env.spyOn(TestClass, 'someFunction'); TestClass.someFunction('foo'); TestClass.someFunction('bar'); @@ -51,7 +58,7 @@ describe('Spies', function () { } }; - this.spyOn(TestClass, 'someFunction').andCallThrough(); + env.spyOn(TestClass, 'someFunction').andCallThrough(); var result = TestClass.someFunction('arg1', 'arg2'); expect(result).toEqual("return value from original function"); expect(originalFunctionWasCalled).toEqual(true); @@ -69,7 +76,7 @@ describe('Spies', function () { } }; - this.spyOn(TestClass, 'someFunction').andReturn("some value"); + env.spyOn(TestClass, 'someFunction').andReturn("some value"); originalFunctionWasCalled = false; var result = TestClass.someFunction('arg1', 'arg2'); expect(result).toEqual("some value"); @@ -85,7 +92,7 @@ describe('Spies', function () { } }; - this.spyOn(TestClass, 'someFunction').andThrow(new Error('fake error')); + env.spyOn(TestClass, 'someFunction').andThrow(new Error('fake error')); var exception; try { TestClass.someFunction('arg1', 'arg2'); @@ -108,7 +115,7 @@ describe('Spies', function () { } }; - this.spyOn(TestClass, 'someFunction').andCallFake(function() { + env.spyOn(TestClass, 'someFunction').andCallFake(function() { fakeFunctionWasCalled = true; passedArgs = arguments; passedObj = this; @@ -124,65 +131,78 @@ describe('Spies', function () { expect(TestClass.someFunction.wasCalled).toEqual(true); }); - it('is torn down when this.removeAllSpies is called', function() { - var originalFunctionWasCalled = false; - var TestClass = { + it('is torn down when env.removeAllSpies is called', function() { + var originalFunctionWasCalled = false, + env = new jasmine.Env(), + TestClass = { someFunction: function() { originalFunctionWasCalled = true; } }; - this.spyOn(TestClass, 'someFunction'); + env.spyOn(TestClass, 'someFunction'); TestClass.someFunction('foo'); expect(originalFunctionWasCalled).toEqual(false); - this.removeAllSpies(); + env.removeAllSpies(); TestClass.someFunction('foo'); expect(originalFunctionWasCalled).toEqual(true); }); it('calls removeAllSpies during spec finish', function() { - var test = new jasmine.Spec(new jasmine.Env(), {}, 'sample test'); + var env = new jasmine.Env(), + originalFoo = function() {}, + testObj = { + foo: originalFoo + }, + firstSpec = originalJasmine.createSpy('firstSpec').andCallFake(function() { + env.spyOn(testObj, 'foo'); + }), + secondSpec = originalJasmine.createSpy('secondSpec').andCallFake(function() { + expect(testObj.foo).toBe(originalFoo); + }); + env.describe('test suite', function() { + env.it('spec 0', firstSpec); + env.it('spec 1', secondSpec); + }); - this.spyOn(test, 'removeAllSpies'); - - test.finish(); - - expect(test.removeAllSpies).wasCalled(); + env.execute(); + expect(firstSpec).toHaveBeenCalled(); + expect(secondSpec).toHaveBeenCalled(); }); it('throws an exception when some method is spied on twice', function() { var TestClass = { someFunction: function() { } }; - this.spyOn(TestClass, 'someFunction'); + env.spyOn(TestClass, 'someFunction'); var exception; try { - this.spyOn(TestClass, 'someFunction'); + env.spyOn(TestClass, 'someFunction'); } catch (e) { exception = e; } expect(exception).toBeDefined(); }); - - it('to spy on an undefined method throws exception', function() { - var TestClass = { - someFunction : function() { - } - }; - function efunc() { - this.spyOn(TestClass, 'someOtherFunction'); - }; - expect(function() { - efunc(); - }).toThrow('someOtherFunction() method does not exist'); - - }); + + it('to spy on an undefined method throws exception', function() { + var TestClass = { + someFunction : function() { + } + }; + function efunc() { + env.spyOn(TestClass, 'someOtherFunction'); + }; + expect(function() { + efunc(); + }).toThrow('someOtherFunction() method does not exist'); + + }); it('should be able to reset a spy', function() { var TestClass = { someFunction: function() {} }; - this.spyOn(TestClass, 'someFunction'); + env.spyOn(TestClass, 'someFunction'); expect(TestClass.someFunction).not.toHaveBeenCalled(); TestClass.someFunction(); @@ -195,7 +215,7 @@ describe('Spies', function () { describe("createSpyObj", function() { it("should create an object with a bunch of spy methods when you call jasmine.createSpyObj()", function() { var spyObj = jasmine.createSpyObj('BaseName', ['method1', 'method2']); - expect(spyObj).toEqual({ method1: jasmine.any(Function), method2: jasmine.any(Function)}); + expect(spyObj).toEqual({ method1: originalJasmine.any(Function), method2: originalJasmine.any(Function)}); expect(spyObj.method1.identity).toEqual('BaseName.method1'); expect(spyObj.method2.identity).toEqual('BaseName.method2'); }); diff --git a/spec/core/SuiteSpec.js b/spec/core/SuiteSpec.js index d48ef39c..b09e4f62 100644 --- a/spec/core/SuiteSpec.js +++ b/spec/core/SuiteSpec.js @@ -1,120 +1,230 @@ -describe('Suite', function() { - var fakeTimer; - var env; +describe("Suite", function() { - beforeEach(function() { - env = new jasmine.Env(); - env.updateInterval = 0; - - fakeTimer = new jasmine.FakeTimer(); - env.setTimeout = fakeTimer.setTimeout; - env.clearTimeout = fakeTimer.clearTimeout; - env.setInterval = fakeTimer.setInterval; - env.clearInterval = fakeTimer.clearInterval; - }); - - describe('Specs', function () { - var suite; - - beforeEach(function() { - suite = env.describe('Suite 1', function () { - env.it('Spec 1', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - env.it('Spec 2', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - env.describe('Suite 2', function () { - env.it('Spec 3', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - }); - env.it('Spec 4', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - }); - }); - - it('#specs should return all immediate children that are specs.', function () { - var suiteSpecs = suite.specs(); - expect(suiteSpecs.length).toEqual(3); - expect(suiteSpecs[0].description).toEqual('Spec 1'); - expect(suiteSpecs[1].description).toEqual('Spec 2'); - expect(suiteSpecs[2].description).toEqual('Spec 4'); - }); - - it("#suites should return all immediate children that are suites.", function() { - var nestedSuites = suite.suites(); - expect(nestedSuites.length).toEqual(1); - expect(nestedSuites[0].description).toEqual('Suite 2'); - }); - - it("#children should return all immediate children including suites and specs.", function() { - var children = suite.children(); - expect(children.length).toEqual(4); - expect(children[0].description).toEqual('Spec 1'); - expect(children[1].description).toEqual('Spec 2'); - expect(children[2].description).toEqual('Suite 2'); - expect(children[3].description).toEqual('Spec 4'); - }); - }); - - describe('SpecCount', function () { - - it('should keep a count of the number of specs that are run', function() { - var suite = env.describe('one suite description', function () { - env.it('should be a test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - env.it('should be another test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - env.it('should be a third test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); + it("keeps its id", function() { + var env = new jasmine.Env(), + suite = new jasmine.Suite({ + env: env, + id: 456, + description: "I am a suite" }); - expect(suite.specs().length).toEqual(3); - }); + expect(suite.id).toEqual(456); + }); - it('specCount should be correct even with runs/waits blocks', function() { - var suite = env.describe('one suite description', function () { - env.it('should be a test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - env.it('should be another test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - this.waits(10); - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); - env.it('should be a third test', function() { - this.runs(function () { - this.expect(true).toEqual(true); - }); - }); + it("returns its full name", function() { + var env = new jasmine.Env(), + suite = new jasmine.Suite({ + env: env, + description: "I am a suite" }); - expect(suite.specs().length).toEqual(3); + expect(suite.getFullName()).toEqual("I am a suite"); + }); + + it("returns its full name when it has parent suites", function() { + var env = new jasmine.Env(), + parentSuite = new jasmine.Suite({ + env: env, + description: "I am a parent suite", + parentSuite: jasmine.createSpy('pretend top level suite') + }), + suite = new jasmine.Suite({ + env: env, + description: "I am a suite", + parentSuite: parentSuite + }); + + expect(suite.getFullName()).toEqual("I am a parent suite I am a suite"); + }); + + it("adds before functions in order of needed execution", function() { + var env = new jasmine.Env(), + suite = new jasmine.Suite({ + env: env, + description: "I am a suite" + }), + outerBefore = jasmine.createSpy('outerBeforeEach'), + innerBefore = jasmine.createSpy('insideBeforeEach'); + + suite.beforeEach(outerBefore); + suite.beforeEach(innerBefore); + + expect(suite.beforeFns).toEqual([innerBefore, outerBefore]); + }); + + it("adds after functions in order of needed execution", function() { + var env = new jasmine.Env(), + suite = new jasmine.Suite({ + env: env, + description: "I am a suite" + }), + outerAfter = jasmine.createSpy('outerAfterEach'), + innerAfter = jasmine.createSpy('insideAfterEach'); + + suite.afterEach(outerAfter); + suite.afterEach(innerAfter); + + expect(suite.afterFns).toEqual([innerAfter, outerAfter]); + }); + + it("adds specs", function() { + var env = new jasmine.Env(), + fakeQueue = { + add: jasmine.createSpy() + }, + suite = new jasmine.Suite({ + env: env, + description: "I am a suite", + queueFactory: function() { + return fakeQueue + } + }), + fakeSpec = {}; + + expect(suite.specs.length).toEqual(0); + + suite.addSpec(fakeSpec); + + expect(suite.specs.length).toEqual(1); + }); + + it("adds suites", function() { + var env = new jasmine.Env(), + fakeQueue = { + add: jasmine.createSpy() + }, + suite = new jasmine.Suite({ + env: env, + description: "I am a suite", + queueFactory: function() { + return fakeQueue + } + }), + anotherSuite = new jasmine.Suite({ + env: env, + description: "I am another suite", + queueFactory: function() { + return fakeQueue + } + }); + + expect(suite.suites.length).toEqual(0); + + suite.addSuite(anotherSuite); + + expect(suite.suites.length).toEqual(1); + }); + + it("can be disabled", function() { + var env = new jasmine.Env(), + fakeQueueRunner = jasmine.createSpy('fake queue runner'), + suite = new jasmine.Suite({ + env: env, + description: "with a child suite", + queueRunner: fakeQueueRunner + }); + + suite.disable(); + + expect(suite.disabled).toBe(true); + + suite.execute(); + + expect(fakeQueueRunner).not.toHaveBeenCalled(); + }); + + it("delegates execution of its specs and suites", function() { + var env = new jasmine.Env(), + parentSuiteDone = jasmine.createSpy('parent suite done'), + fakeQueueRunnerForParent = jasmine.createSpy('fake parent queue runner'), + parentSuite = new jasmine.Suite({ + env: env, + description: "I am a parent suite", + queueRunner: fakeQueueRunnerForParent + }), + fakeQueueRunner = jasmine.createSpy('fake queue runner'), + suite = new jasmine.Suite({ + env: env, + description: "with a child suite", + queueRunner: fakeQueueRunner + }), + fakeSpec1 = { + execute: jasmine.createSpy('fakeSpec1') + }; + + spyOn(suite, "execute"); + + parentSuite.addSpec(fakeSpec1); + parentSuite.addSuite(suite); + + parentSuite.execute(parentSuiteDone); + + var parentSuiteFns = fakeQueueRunnerForParent.mostRecentCall.args[0].fns; + + parentSuiteFns[0](); + expect(fakeSpec1.execute).toHaveBeenCalled(); + parentSuiteFns[1](); + expect(suite.execute).toHaveBeenCalled(); + }); + + it("calls a provided onStart callback when starting", function() { + var env = new jasmine.Env(), + suiteStarted = jasmine.createSpy('suiteStarted'), + fakeQueueRunner = function(attrs) { attrs.onComplete(); }, + suite = new jasmine.Suite({ + env: env, + description: "with a child suite", + onStart: suiteStarted, + queueRunner: fakeQueueRunner + }), + fakeSpec1 = { + execute: jasmine.createSpy('fakeSpec1') + }; + + suite.execute(); + + expect(suiteStarted).toHaveBeenCalledWith(suite); + }); + + it("calls a provided onComplete callback when done", function() { + var env = new jasmine.Env(), + suiteCompleted = jasmine.createSpy('parent suite done'), + fakeQueueRunner = function(attrs) { attrs.onComplete(); }, + suite = new jasmine.Suite({ + env: env, + description: "with a child suite", + queueRunner: fakeQueueRunner + }), + fakeSpec1 = { + execute: jasmine.createSpy('fakeSpec1') + }; + + suite.execute(suiteCompleted); + + expect(suiteCompleted).toHaveBeenCalled(); + }); + + it("calls a provided result callback when done", function() { + var env = new jasmine.Env(), + suiteResultsCallback = jasmine.createSpy('suite result callback'), + fakeQueueRunner = function(attrs) { attrs.onComplete(); }, + suite = new jasmine.Suite({ + env: env, + description: "with a child suite", + queueRunner: fakeQueueRunner, + resultCallback: suiteResultsCallback + }), + fakeSpec1 = { + execute: jasmine.createSpy('fakeSpec1') + }; + + suite.execute(); + + expect(suiteResultsCallback).toHaveBeenCalledWith({ + id: suite.id, + status: '', + description: "with a child suite", + fullName: "with a child suite" }); }); -}); \ No newline at end of file +}); diff --git a/spec/core/UtilSpec.js b/spec/core/UtilSpec.js index 551c2872..17694168 100644 --- a/spec/core/UtilSpec.js +++ b/spec/core/UtilSpec.js @@ -1,26 +1,4 @@ describe("jasmine.util", function() { - describe("extend", function () { - it("should add properies to a destination object ", function() { - var destination = {baz: 'baz'}; - jasmine.util.extend(destination, { - foo: 'foo', bar: 'bar' - }); - expect(destination).toEqual({foo: 'foo', bar: 'bar', baz: 'baz'}); - }); - - it("should replace properies that already exist on a destination object", function() { - var destination = {foo: 'foo'}; - jasmine.util.extend(destination, { - foo: 'bar' - }); - expect(destination).toEqual({foo: 'bar'}); - jasmine.util.extend(destination, { - foo: null - }); - expect(destination).toEqual({foo: null}); - }); - }); - describe("isArray_", function() { it("should return true if the argument is an array", function() { expect(jasmine.isArray_([])).toBe(true); diff --git a/spec/core/WaitsForBlockSpec.js b/spec/core/WaitsForBlockSpec.js deleted file mode 100644 index e1807212..00000000 --- a/spec/core/WaitsForBlockSpec.js +++ /dev/null @@ -1,118 +0,0 @@ -describe('WaitsForBlock', function () { - var env, suite, timeout, spec, message, onComplete, fakeTimer; - beforeEach(function() { - env = new jasmine.Env(); - env.updateInterval = 0; - suite = new jasmine.Suite(env, 'suite 1'); - timeout = 1000; - spec = new jasmine.Spec(env, suite); - message = "some error message"; - onComplete = jasmine.createSpy("onComplete"); - }); - - describe("jasmine.VERBOSE", function() { - var jasmineVerboseOriginal; - beforeEach(function() { - jasmineVerboseOriginal = jasmine.VERBOSE; - spyOn(env.reporter, 'log'); - - }); - it('do not show information if jasmine.VERBOSE is set to false', function () { - jasmine.VERBOSE = false; - var latchFunction = function() { - return true; - }; - var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec); - expect(env.reporter.log).not.toHaveBeenCalled(); - block.execute(onComplete); - expect(env.reporter.log).not.toHaveBeenCalled(); - jasmine.VERBOSE = jasmineVerboseOriginal; - }); - it('show information if jasmine.VERBOSE is set to true', function () { - jasmine.VERBOSE = true; - var latchFunction = function() { - return true; - }; - var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec); - expect(env.reporter.log).not.toHaveBeenCalled(); - block.execute(onComplete); - expect(env.reporter.log).toHaveBeenCalled(); - jasmine.VERBOSE = jasmineVerboseOriginal; - }); - }); - - it('onComplete should be called if the latchFunction returns true', function () { - var latchFunction = function() { - return true; - }; - var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec); - expect(onComplete).not.toHaveBeenCalled(); - block.execute(onComplete); - expect(onComplete).toHaveBeenCalled(); - }); - - it('latchFunction should run in same scope as spec', function () { - var result; - var latchFunction = function() { - result = this.scopedValue; - }; - spec.scopedValue = 'foo'; - var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec); - block.execute(onComplete); - expect(result).toEqual('foo'); - }); - - it('should fail spec and call onComplete if there is an error in the latchFunction', function() { - var latchFunction = jasmine.createSpy('latchFunction').andThrow('some error'); - spyOn(spec, 'fail'); - var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec); - block.execute(onComplete); - expect(spec.fail).toHaveBeenCalledWith('some error'); - expect(onComplete).toHaveBeenCalled(); - }); - - describe("if latchFunction returns false", function() { - var latchFunction, fakeTimer; - beforeEach(function() { - latchFunction = jasmine.createSpy('latchFunction').andReturn(false); - fakeTimer = new jasmine.FakeTimer(); - env.setTimeout = fakeTimer.setTimeout; - env.clearTimeout = fakeTimer.clearTimeout; - env.setInterval = fakeTimer.setInterval; - env.clearInterval = fakeTimer.clearInterval; - }); - - it('latchFunction should be retried after 10 ms', function () { - var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec); - expect(latchFunction).not.toHaveBeenCalled(); - block.execute(onComplete); - expect(latchFunction.callCount).toEqual(1); - fakeTimer.tick(5); - expect(latchFunction.callCount).toEqual(1); - fakeTimer.tick(5); - expect(latchFunction.callCount).toEqual(2); - }); - - it('onComplete should be called if latchFunction returns true before timeout', function () { - var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec); - expect(onComplete).not.toHaveBeenCalled(); - block.execute(onComplete); - expect(onComplete).not.toHaveBeenCalled(); - latchFunction.andReturn(true); - fakeTimer.tick(100); - expect(onComplete).toHaveBeenCalled(); - }); - - it('spec should fail with the passed message if the timeout is reached (and not call onComplete)', function () { - spyOn(spec, 'fail'); - var block = new jasmine.WaitsForBlock(env, timeout, latchFunction, message, spec); - block.execute(onComplete); - expect(spec.fail).not.toHaveBeenCalled(); - fakeTimer.tick(timeout); - expect(spec.fail).toHaveBeenCalled(); - var failMessage = spec.fail.mostRecentCall.args[0].message; - expect(failMessage).toMatch(message); - expect(onComplete).toHaveBeenCalled(); - }); - }); -}); diff --git a/spec/html/HTMLReporterSpec.js b/spec/html/HTMLReporterSpec.js deleted file mode 100644 index 3545527b..00000000 --- a/spec/html/HTMLReporterSpec.js +++ /dev/null @@ -1,209 +0,0 @@ -describe("HtmlReporter", function() { - var env; - var htmlReporter; - var body; - var fakeDocument; - - beforeEach(function() { - env = new jasmine.Env(); - env.updateInterval = 0; - - body = document.createElement("body"); - fakeDocument = { body: body, location: { search: "" } }; - htmlReporter = new jasmine.HtmlReporter(fakeDocument); - }); - - function fakeSpec(name) { - return { - getFullName: function() { - return name; - } - }; - } - - function findElements(divs, withClass) { - var els = []; - for (var i = 0; i < divs.length; i++) { - if (divs[i].className == withClass) els.push(divs[i]); - } - return els; - } - - function findElement(divs, withClass) { - var els = findElements(divs, withClass); - if (els.length > 0) { - return els[0]; - } - throw new Error("couldn't find div with class " + withClass); - } - - it("should run only specs beginning with spec parameter", function() { - fakeDocument.location.search = "?spec=run%20this"; - expect(htmlReporter.specFilter(fakeSpec("run this"))).toBeTruthy(); - expect(htmlReporter.specFilter(fakeSpec("not the right spec"))).toBeFalsy(); - expect(htmlReporter.specFilter(fakeSpec("not run this"))).toBeFalsy(); - }); - - describe("running without any specs", function() { - var runner; - beforeEach(function() { - runner = env.currentRunner(); - env.addReporter(htmlReporter); - }); - - it("should not error", function() { - var exec = function() { - runner.execute(); - }; - expect(exec).not.toThrow(); - }); - }); - - describe('Matcher reporting', function() { - var getResultMessageDiv = function(body) { - var divs = body.getElementsByTagName("div"); - for (var i = 0; i < divs.length; i++) { - if (divs[i].className.match(/resultMessage/)) { - return divs[i]; - } - } - }; - - var runner, spec, fakeTimer; - beforeEach(function() { - fakeTimer = new jasmine.FakeTimer(); - env.setTimeout = fakeTimer.setTimeout; - env.clearTimeout = fakeTimer.clearTimeout; - env.setInterval = fakeTimer.setInterval; - env.clearInterval = fakeTimer.clearInterval; - runner = env.currentRunner(); - var suite = new jasmine.Suite(env, 'some suite'); - runner.add(suite); - spec = new jasmine.Spec(env, suite, 'some spec'); - suite.add(spec); - fakeDocument.location.search = "?"; - env.addReporter(htmlReporter); - }); - - describe('toContain', function() { - it('should show actual and expected', function() { - spec.runs(function() { - this.expect('foo').toContain('bar'); - }); - runner.execute(); - fakeTimer.tick(0); - - var resultEl = getResultMessageDiv(body); - expect(resultEl.innerHTML).toMatch(/foo/); - expect(resultEl.innerHTML).toMatch(/bar/); - }); - }); - }); - - describe("failure messages (integration)", function() { - var spec, results, expectationResult; - - it("should add the failure message to the DOM (non-toEquals matchers)", function() { - env.describe("suite", function() { - env.it("will have log messages", function() { - this.expect('a').toBeNull(); - }); - }); - - env.addReporter(htmlReporter); - env.execute(); - - var divs = body.getElementsByTagName("div"); - var errorDiv = findElement(divs, 'resultMessage fail'); - expect(errorDiv.innerHTML).toMatch(/Expected 'a' to be null/); - }); - - it("should add the failure message to the DOM (non-toEquals matchers) html escaping", function() { - env.describe("suite", function() { - env.it("will have log messages", function() { - this.expect('1 < 2').toBeNull(); - }); - }); - - env.addReporter(htmlReporter); - env.execute(); - - var divs = body.getElementsByTagName("div"); - var errorDiv = findElement(divs, 'resultMessage fail'); - expect(errorDiv.innerHTML).toMatch(/Expected '1 < 2' to be null/); - }); - }); - - describe("log messages", function() { - it("should appear in the report of a failed spec", function() { - env.describe("suite", function() { - env.it("will have log messages", function() { - this.log("this is a", "multipart log message"); - this.expect(true).toBeFalsy(); - }); - }); - - env.addReporter(htmlReporter); - env.execute(); - - var divs = body.getElementsByTagName("div"); - var errorDiv = findElement(divs, 'specDetail failed'); - expect(errorDiv.innerHTML).toMatch("this is a multipart log message"); - }); - - xit("should work on IE without console.log.apply", function() { - }); - }); - - describe("duplicate example names", function() { - it("should report failures correctly", function() { - var suite1 = env.describe("suite", function() { - env.it("will have log messages", function() { - this.log("this one fails!"); - this.expect(true).toBeFalsy(); - }); - }); - - var suite2 = env.describe("suite", function() { - env.it("will have log messages", function() { - this.log("this one passes!"); - this.expect(true).toBeTruthy(); - }); - }); - - env.addReporter(htmlReporter); - env.execute(); - - var divs = body.getElementsByTagName("div"); - var failedSpecDiv = findElement(divs, 'specDetail failed'); - expect(failedSpecDiv.className).toEqual('specDetail failed'); - expect(failedSpecDiv.innerHTML).toContain("this one fails!"); - expect(failedSpecDiv.innerHTML).not.toContain("this one passes!"); - }); - }); - - describe('#reportSpecStarting', function() { - beforeEach(function() { - env.describe("suite 1", function() { - env.it("spec 1", function() { - }); - }); - spyOn(htmlReporter, 'log').andCallThrough(); - }); - - it('DOES NOT log running specs by default', function() { - env.addReporter(htmlReporter); - env.execute(); - - expect(htmlReporter.log).not.toHaveBeenCalled(); - }); - - it('logs running specs when log_running_specs is true', function() { - htmlReporter.logRunningSpecs = true; - env.addReporter(htmlReporter); - env.execute(); - - expect(htmlReporter.log).toHaveBeenCalledWith('>> Jasmine Running suite 1 spec 1...'); - }); - }); -}); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js new file mode 100644 index 00000000..389fa278 --- /dev/null +++ b/spec/html/HtmlReporterSpec.js @@ -0,0 +1,571 @@ +describe("New HtmlReporter", function() { + + it("builds the initial DOM elements, including the title banner", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + reporter.initialize(); + + // Main top-level elements + var divs = container.getElementsByTagName("div"); + expect(findElement(divs, "html-reporter")).toBeTruthy(); + expect(findElement(divs, "banner")).toBeTruthy(); + expect(findElement(divs, "alert")).toBeTruthy(); + expect(findElement(divs, "results")).toBeTruthy(); + + var uls = container.getElementsByTagName("ul"); + expect(findElement(uls, "symbol-summary")).toBeTruthy(); + + // title banner + var banner = container.getElementsByClassName("banner")[0]; + + var title = banner.getElementsByClassName("title")[0]; + expect(title.innerHTML).toMatch(/Jasmine/); + + var version = banner.getElementsByClassName("version")[0]; + expect(version.innerHTML).toMatch(/\d+\.\d+\.\d+\srevision\s+\d+/); + }); + + describe("when a spec is done", function() { + it("reports the status symbol of a disabled spec", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + reporter.initialize(); + + reporter.specDone({id: 789, status: "disabled"}); + + var statuses = container.getElementsByClassName('symbol-summary')[0]; + var specEl = statuses.getElementsByTagName('li')[0]; + expect(specEl.getAttribute("class")).toEqual("disabled"); + expect(specEl.getAttribute("id")).toEqual("spec_789"); + }); + + it("reports the status symbol of a passing spec", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + reporter.initialize(); + + reporter.specDone({id: 123, status: "passed"}); + + var statuses = container.getElementsByClassName("symbol-summary")[0]; + var specEl = statuses.getElementsByTagName("li")[0]; + expect(specEl.getAttribute("class")).toEqual("passed"); + expect(specEl.getAttribute("id")).toEqual("spec_123"); + }); + + it("reports the status symbol of a failing spec", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + reporter.initialize(); + + reporter.specDone({ + id: 345, + status: "failed", + failedExpectations: [] + }); + + var statuses = container.getElementsByClassName('symbol-summary')[0]; + var specEl = statuses.getElementsByTagName('li')[0]; + expect(specEl.getAttribute("class")).toEqual("failed"); + expect(specEl.getAttribute("id")).toEqual("spec_345"); + }); + }); + + describe("when Jasmine is done", function() { + it("reports the run time", function() { + var env = new jasmine.Env(), + fakeNow = jasmine.createSpy('fake Date.now'), + container = document.createElement("div"), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + now: fakeNow, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + reporter.initialize(); + + fakeNow.andReturn(500); + reporter.jasmineStarted({}); + fakeNow.andReturn(600); + reporter.jasmineDone(); + + var banner = container.getElementsByClassName("banner")[0]; + var duration = banner.getElementsByClassName("duration")[0]; + expect(duration.innerHTML).toMatch(/finished in 0.1s/); + }); + + it("reports the suite and spec names with status", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + reporter.initialize(); + + reporter.jasmineStarted({}); + reporter.suiteStarted({ + id: 1, + description: "A Suite", + fullName: "A Suite" + }); + + var specResult = { + id: 123, + description: "with a spec", + fullName: "A Suite with a spec", + status: "passed" + }; + reporter.specStarted(specResult); + reporter.specDone(specResult); + + reporter.suiteStarted({ + id: 2, + description: "inner suite", + fullName: "A Suite inner suite" + }); + + var specResult = { + id: 124, + description: "with another spec", + fullName: "A Suite inner suite with another spec", + status: "passed" + }; + reporter.specStarted(specResult); + reporter.specDone(specResult); + + reporter.suiteDone({id: 2}); + + specResult = { + id: 209, + description: "with a failing spec", + fullName: "A Suite inner with a failing spec", + status: "failed", + failedExpectations: [] + }; + reporter.specStarted(specResult); + reporter.specDone(specResult); + + reporter.suiteDone({id: 1}); + + reporter.jasmineDone(); + var summary = container.getElementsByClassName("summary")[0]; + + console.error("=============>", summary); + expect(summary.childNodes.length).toEqual(1); + + var outerSuite = summary.childNodes[0]; + expect(outerSuite.childNodes.length).toEqual(4); + + var classes = []; + for (var i = 0; i < outerSuite.childNodes.length; i++) { + var node = outerSuite.childNodes[i]; + classes.push(node.getAttribute("class")); + } + expect(classes).toEqual(["suite-detail", "specs", "suite", "specs"]); + + var suiteDetail = outerSuite.childNodes[0]; + var suiteLink = suiteDetail.childNodes[0]; + expect(suiteLink.text).toEqual("A Suite"); + expect(suiteLink.getAttribute('href')).toEqual("?spec=A%20Suite"); + + var specs = outerSuite.childNodes[1]; + var spec = specs.childNodes[0]; + expect(spec.getAttribute("class")).toEqual("passed"); + expect(spec.getAttribute("id")).toEqual("spec-123"); + + var specLink = spec.childNodes[0]; + expect(specLink.text).toEqual("with a spec"); + expect(specLink.getAttribute("href")).toEqual("?spec=A%20Suite%20with%20a%20spec"); +// expect(specLink.getAttribute("title")).toEqual("A Suite with a spec"); + }); + + describe("UI for raising/catching exceptions", function() { + it("should be unchecked if the env is catching", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + reporter.initialize(); + reporter.jasmineDone(); + + var raisingExceptionsUI = container.getElementsByClassName("raise")[0]; + expect(raisingExceptionsUI.checked).toBe(false); + }); + + it("should be checked if the env is not catching", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + fakeQueryString = "", + fakeWindowLocation = { + search: fakeQueryString + }, + queryString = new jasmine.QueryString({ + getWindowLocation: function() { return fakeWindowLocation; } + }), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + queryString: queryString, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + reporter.initialize(); + env.catchExceptions(false); + reporter.jasmineDone(); + + var raisingExceptionsUI = container.getElementsByClassName("raise")[0]; + expect(raisingExceptionsUI.checked).toBe(true); + }); + + it("should affect the query param for catching exceptions", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + fakeQueryString = "", + fakeWindowLocation = { + search: fakeQueryString + }, + queryString = new jasmine.QueryString({ + getWindowLocation: function() { return fakeWindowLocation; } + }), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + queryString: queryString, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + reporter.initialize(); + reporter.jasmineDone(); + + var input = container.getElementsByClassName("raise")[0]; + input.click(); + expect(queryString.getParam("catch")).toEqual(false); + + input.click(); + expect(queryString.getParam("catch")).toEqual(true); + }); + }); + + describe("and all specs pass", function() { + var env, container, reporter; + beforeEach(function() { + env = new jasmine.Env(); + container = document.createElement("div"); + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + reporter.initialize(); + + reporter.jasmineStarted({}); + reporter.specDone({ + id: 123, + description: "with a spec", + fullName: "A Suite with a spec", + status: "passed" + }); + reporter.specDone({ + id: 124, + description: "with another spec", + fullName: "A Suite inner suite with another spec", + status: "passed" + }); + reporter.jasmineDone(); + }); + + it("reports the specs counts", function() { + var alert = container.getElementsByClassName("alert")[0]; + var alertBars = alert.getElementsByClassName("bar"); + + expect(alertBars.length).toEqual(1); + expect(alertBars[0].getAttribute('class')).toMatch(/passed/); + expect(alertBars[0].innerHTML).toMatch(/2 specs, 0 failures/); + }); + + it("reports no failure details", function() { + var specFailure = container.getElementsByClassName("failures")[0]; + + expect(specFailure.childNodes.length).toEqual(0); + }); + }); + + describe("and some tests fail", function() { + var env, container, reporter; + + beforeEach(function() { + env = new jasmine.Env(); + container = document.createElement("div"); + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + reporter.initialize(); + + reporter.jasmineStarted({}); + + var passingResult = {id: 123, status: "passed"}; + reporter.specStarted(passingResult); + reporter.specDone(passingResult); + + var failingResult = { + id: 124, + status: "failed", + description: "a failing spec", + fullName: "a suite with a failing spec", + failedExpectations: [ + { + message: "a failure message", + trace: { + stack: "a stack trace" + } + } + ] + }; + reporter.specStarted(failingResult); + reporter.specDone(failingResult); + reporter.jasmineDone(); + }); + + it("reports the specs counts", function() { + var alert = container.getElementsByClassName("alert")[0]; + var alertBars = alert.getElementsByClassName("bar"); + + expect(alertBars[0].getAttribute('class')).toMatch(/failed/); + expect(alertBars[0].innerHTML).toMatch(/2 specs, 1 failure/); + }); + + it("reports failure messages and stack traces", function() { + var specFailures = container.getElementsByClassName("failures")[0]; + + var failure = specFailures.childNodes[0]; + expect(failure.getAttribute("class")).toMatch(/failed/); + expect(failure.getAttribute("class")).toMatch(/spec-detail/); + + var specLink = failure.childNodes[0]; + expect(specLink.getAttribute("class")).toEqual("description"); + expect(specLink.getAttribute("title")).toEqual("a suite with a failing spec"); + expect(specLink.getAttribute("href")).toEqual("?spec=a%20suite%20with%20a%20failing%20spec"); + + var message = failure.childNodes[1].childNodes[0]; + expect(message.getAttribute("class")).toEqual("result-message"); + expect(message.innerHTML).toEqual("a failure message"); + + var stackTrace = failure.childNodes[1].childNodes[1]; + expect(stackTrace.getAttribute("class")).toEqual("stack-trace"); + expect(stackTrace.innerHTML).toEqual("a stack trace"); + }); + + it("allows switching between failure details and the spec summary", function() { + var menuBar = container.getElementsByClassName("bar")[1]; + + expect(menuBar.getAttribute("class")).not.toMatch(/hidden/); + + var link = menuBar.getElementsByTagName('a')[0]; + expect(link.text).toEqual("Failures"); + expect(link.getAttribute("href")).toEqual("#"); + }); + + it("sets the reporter to 'Failures List' mode", function() { + var reporterNode = container.getElementsByClassName("html-reporter")[0]; + expect(reporterNode.getAttribute("class")).toMatch("failure-list"); + }); + }); + }); + + describe("specFilter", function() { + + it("always returns true if there is no filter", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + fakeQueryString = "", + fakeWindowLocation = { + search: fakeQueryString + }, + queryString = new jasmine.QueryString({ + getWindowLocation: function() { return fakeWindowLocation; } + }), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + queryString: queryString, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }), + fakeSpec = { + getFullName: function() { return "A suite with a spec"} + }; + + expect(reporter.specFilter(fakeSpec)).toBe(true); + }); + + it("matches a focused spec name", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + fakeWindowLocation = { + search: "?spec=A%20suite%20with%20a%20spec" + }, + queryString = new jasmine.QueryString({ + getWindowLocation: function() { return fakeWindowLocation; } + }), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + queryString: queryString, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }), + fakeMatchingSpec = { + getFullName: function() { return "A suite with a spec"} + }, + fakeNonMatchingSpec = { + getFullName: function() { return "sasquatch"} + }; + + expect(reporter.specFilter(fakeMatchingSpec)).toBe(true); + expect(reporter.specFilter(fakeNonMatchingSpec)).toBe(false); + }); + + it("matches a substring of a spec name", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + fakeWindowLocation = { + search: "?spec=with" + }, + queryString = new jasmine.QueryString({ + getWindowLocation: function() { return fakeWindowLocation; } + }), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + queryString: queryString, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }), + fakeMatchingSpec = { + getFullName: function() { return "A suite with a spec" } + }, + fakeNonMatchingSpec = { + getFullName: function() { return "sasquatch"} + }; + + expect(reporter.specFilter(fakeMatchingSpec)).toBe(true); + expect(reporter.specFilter(fakeNonMatchingSpec)).toBe(false); + }); + }); + + describe("when specs are filtered", function() { + it("shows the count of run specs and defined specs", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + reporter.initialize(); + + reporter.jasmineStarted({ + totalSpecsDefined: 2 + }); + reporter.specDone({ + id: 123, + description: "with a spec", + fullName: "A Suite with a spec", + status: "passed" + }); + reporter.specDone({ + id: 124, + description: "with another spec", + fullName: "A Suite inner suite with another spec", + status: "disabled" + }); + reporter.jasmineDone(); + + var skippedBar = container.getElementsByClassName("bar")[0]; + expect(skippedBar.getAttribute("class")).toMatch(/skipped/); + + var runAllLink = skippedBar.childNodes[0]; + expect(runAllLink.getAttribute("href")).toEqual("?"); + expect(runAllLink.text).toMatch(/Ran \d+ of \d+ specs - run all/); + }); + }); + + // try/catch + + // utility functions + function findElements(divs, withClass) { + var els = []; + for (var i = 0; i < divs.length; i++) { + if (divs[i].className == withClass) els.push(divs[i]); + } + return els; + } + + function findElement(divs, withClass) { + var els = findElements(divs, withClass); + if (els.length > 0) { + return els[0]; + } + throw new Error("couldn't find div with class " + withClass); + } +}); diff --git a/spec/html/MatchersHtmlSpec.js b/spec/html/MatchersHtmlSpec.js index b528753a..e21f4abc 100644 --- a/spec/html/MatchersHtmlSpec.js +++ b/spec/html/MatchersHtmlSpec.js @@ -9,14 +9,14 @@ describe("MatchersSpec - HTML Dependent", function () { spec = env.it("spec", function() { }); }); - spyOn(spec, 'addMatcherResult'); + spyOn(spec, 'addExpectationResult'); - this.addMatchers({ + addMatchers({ toPass: function() { - return lastResult().passed(); + return lastResult().passed; }, toFail: function() { - return !lastResult().passed(); + return !lastResult().passed; } }); }); @@ -26,7 +26,7 @@ describe("MatchersSpec - HTML Dependent", function () { } function lastResult() { - return spec.addMatcherResult.mostRecentCall.args[0]; + return spec.addExpectationResult.mostRecentCall.args[1]; } it("toEqual with DOM nodes", function() { @@ -35,4 +35,4 @@ describe("MatchersSpec - HTML Dependent", function () { expect((match(nodeA).toEqual(nodeA))).toPass(); expect((match(nodeA).toEqual(nodeB))).toFail(); }); -}); \ No newline at end of file +}); diff --git a/spec/html/QueryStringSpec.js b/spec/html/QueryStringSpec.js new file mode 100644 index 00000000..e95d1811 --- /dev/null +++ b/spec/html/QueryStringSpec.js @@ -0,0 +1,43 @@ +describe("QueryString", function() { + + describe("#setParam", function() { + + it("sets the query string to include the given key/value pair", function() { + var windowLocation = { + search: "" + }, + queryString = new jasmine.QueryString({ + getWindowLocation: function() { return windowLocation } + }); + + queryString.setParam("foo", "bar baz"); + + expect(windowLocation.search).toMatch(/foo=bar%20baz/); + }); + }); + + describe("#getParam", function() { + + it("returns the value of the requested key", function() { + var windowLocation = { + search: "?baz=quux%20corge" + }, + queryString = new jasmine.QueryString({ + getWindowLocation: function() { return windowLocation } + }); + + expect(queryString.getParam("baz")).toEqual("quux corge"); + }); + + it("returns null if the key is not present", function() { + var windowLocation = { + search: "" + }, + queryString = new jasmine.QueryString({ + getWindowLocation: function() { return windowLocation } + }); + + expect(queryString.getParam("baz")).toBeFalsy(); + }); + }); +}); \ No newline at end of file diff --git a/spec/html/ResultsNodeSpec.js b/spec/html/ResultsNodeSpec.js new file mode 100644 index 00000000..db46bc13 --- /dev/null +++ b/spec/html/ResultsNodeSpec.js @@ -0,0 +1,62 @@ +describe("ResultsNode", function() { + it("wraps a result", function() { + var fakeResult = { + id: 123, + message: "foo" + }, + node = new jasmine.ResultsNode(fakeResult, "suite", null); + + expect(node.result).toBe(fakeResult); + expect(node.type).toEqual("suite"); + }); + + it("can add children with a type", function() { + var fakeResult = { + id: 123, + message: "foo" + }, + fakeChildResult = { + id: 456, + message: "bar" + }, + node = new jasmine.ResultsNode(fakeResult, "suite", null); + + node.addChild(fakeChildResult, "spec"); + + expect(node.children.length).toEqual(1); + expect(node.children[0].result).toEqual(fakeChildResult); + expect(node.children[0].type).toEqual("spec"); + }); + + it("has a pointer back to its parent ResultNode", function() { + var fakeResult = { + id: 123, + message: "foo" + }, + fakeChildResult = { + id: 456, + message: "bar" + }, + node = new jasmine.ResultsNode(fakeResult, "suite", null); + + node.addChild(fakeChildResult, "spec"); + + expect(node.children[0].parent).toBe(node); + }); + + it("can provide the most recent child", function() { + var fakeResult = { + id: 123, + message: "foo" + }, + fakeChildResult = { + id: 456, + message: "bar" + }, + node = new jasmine.ResultsNode(fakeResult, "suite", null); + + node.addChild(fakeChildResult, "spec"); + + expect(node.last()).toBe(node.children[node.children.length - 1]); + }); +}); \ No newline at end of file diff --git a/spec/html/TrivialReporterSpec.js b/spec/html/TrivialReporterSpec.js deleted file mode 100644 index 4e0830da..00000000 --- a/spec/html/TrivialReporterSpec.js +++ /dev/null @@ -1,239 +0,0 @@ -describe("TrivialReporter", function() { - var env; - var trivialReporter; - var body; - var fakeDocument; - - beforeEach(function() { - env = new jasmine.Env(); - env.updateInterval = 0; - - body = document.createElement("body"); - fakeDocument = { body: body, location: { search: "" } }; - trivialReporter = new jasmine.TrivialReporter(fakeDocument); - }); - - function fakeSpec(name) { - return { - getFullName: function() { - return name; - } - }; - } - - function findElements(divs, withClass) { - var els = []; - for (var i = 0; i < divs.length; i++) { - if (divs[i].className == withClass) els.push(divs[i]); - } - return els; - } - - function findElement(divs, withClass) { - var els = findElements(divs, withClass); - if (els.length > 0) { - return els[0]; - } - throw new Error("couldn't find div with class " + withClass); - } - - it("should run only specs beginning with spec parameter", function() { - fakeDocument.location.search = "?spec=run%20this"; - expect(trivialReporter.specFilter(fakeSpec("run this"))).toBeTruthy(); - expect(trivialReporter.specFilter(fakeSpec("not the right spec"))).toBeFalsy(); - expect(trivialReporter.specFilter(fakeSpec("not run this"))).toBeFalsy(); - }); - - it("should display empty divs for every suite when the runner is starting", function() { - trivialReporter.reportRunnerStarting({ - env: env, - suites: function() { - return [ new jasmine.Suite({}, "suite 1", null, null) ]; - } - }); - - var divs = findElements(body.getElementsByTagName("div"), "suite"); - expect(divs.length).toEqual(1); - expect(divs[0].innerHTML).toContain("suite 1"); - }); - - describe('Matcher reporting', function () { - var getResultMessageDiv = function (body) { - var divs = body.getElementsByTagName("div"); - for (var i = 0; i < divs.length; i++) { - if (divs[i].className.match(/resultMessage/)) { - return divs[i]; - } - } - }; - - var runner, spec, fakeTimer; - beforeEach(function () { - fakeTimer = new jasmine.FakeTimer(); - env.setTimeout = fakeTimer.setTimeout; - env.clearTimeout = fakeTimer.clearTimeout; - env.setInterval = fakeTimer.setInterval; - env.clearInterval = fakeTimer.clearInterval; - runner = env.currentRunner(); - var suite = new jasmine.Suite(env, 'some suite'); - runner.add(suite); - spec = new jasmine.Spec(env, suite, 'some spec'); - suite.add(spec); - fakeDocument.location.search = "?"; - env.addReporter(trivialReporter); - }); - - describe('toContain', function () { - it('should show actual and expected', function () { - spec.runs(function () { - this.expect('foo').toContain('bar'); - }); - runner.execute(); - fakeTimer.tick(0); - - var resultEl = getResultMessageDiv(body); - expect(resultEl.innerHTML).toMatch(/foo/); - expect(resultEl.innerHTML).toMatch(/bar/); - }); - }); - }); - - describe("failure messages (integration)", function () { - var spec, results, expectationResult; - - beforeEach(function() { - results = { - passed: function() { - return false; - }, - getItems: function() { - }}; - - var suite1 = new jasmine.Suite(env, "suite 1", null, null); - - spec = { - suite: suite1, - getFullName: function() { - return "foo"; - }, - results: function() { - return results; - } - }; - - trivialReporter.reportRunnerStarting({ - env: env, - suites: function() { - return [ suite1 ]; - } - }); - }); - - it("should add the failure message to the DOM (non-toEquals matchers)", function() { - expectationResult = new jasmine.ExpectationResult({ - matcherName: "toBeNull", passed: false, message: "Expected 'a' to be null, but it was not" - }); - - spyOn(results, 'getItems').andReturn([expectationResult]); - - trivialReporter.reportSpecResults(spec); - - var divs = body.getElementsByTagName("div"); - var errorDiv = findElement(divs, 'resultMessage fail'); - expect(errorDiv.innerHTML).toEqual("Expected 'a' to be null, but it was not"); - }); - - it("should add the failure message to the DOM (non-toEquals matchers) html escaping", function() { - expectationResult = new jasmine.ExpectationResult({ - matcherName: "toBeNull", passed: false, message: "Expected '1 < 2' to e null, & it was not" - }); - - spyOn(results, 'getItems').andReturn([expectationResult]); - - trivialReporter.reportSpecResults(spec); - - var divs = body.getElementsByTagName("div"); - var errorDiv = findElement(divs, 'resultMessage fail'); - expect(errorDiv.innerHTML).toEqual("Expected '1 < 2' to <b>e null, & it was not"); - }); - }); - - describe("log messages", function() { - it("should appear in the report", function() { - env.describe("suite", function() { - env.it("will have log messages", function() { - this.log("this is a", "multipart log message"); - }); - }); - - env.addReporter(trivialReporter); - env.execute(); - - var divs = body.getElementsByTagName("div"); - var errorDiv = findElement(divs, 'resultMessage log'); - expect(errorDiv.innerHTML).toEqual("this is a multipart log message"); - }); - - xit("should work on IE without console.log.apply", function() { - }); - }); - - describe("duplicate example names", function() { - it("should report failures correctly", function() { - var suite1 = env.describe("suite", function() { - env.it("will have log messages", function() { - this.log("this one fails!"); - this.expect(true).toBeFalsy(); - }); - }); - - var suite2 = env.describe("suite", function() { - env.it("will have log messages", function() { - this.log("this one passes!"); - this.expect(true).toBeTruthy(); - }); - }); - - env.addReporter(trivialReporter); - env.execute(); - - var divs = body.getElementsByTagName("div"); - var passedSpecDiv = findElement(divs, 'suite passed'); - expect(passedSpecDiv.className).toEqual('suite passed'); - expect(passedSpecDiv.innerHTML).toContain("this one passes!"); - expect(passedSpecDiv.innerHTML).not.toContain("this one fails!"); - - var failedSpecDiv = findElement(divs, 'suite failed'); - expect(failedSpecDiv.className).toEqual('suite failed'); - expect(failedSpecDiv.innerHTML).toContain("this one fails!"); - expect(failedSpecDiv.innerHTML).not.toContain("this one passes!"); - }); - }); - - describe('#reportSpecStarting', function() { - var spec1; - beforeEach(function () { - env.describe("suite 1", function() { - spec1 = env.it("spec 1", function() { - }); - }); - }); - - it('DOES NOT log running specs by default', function() { - spyOn(trivialReporter, 'log'); - - trivialReporter.reportSpecStarting(spec1); - - expect(trivialReporter.log).not.toHaveBeenCalled(); - }); - - it('logs running specs when log_running_specs is true', function() { - trivialReporter.logRunningSpecs = true; - spyOn(trivialReporter, 'log'); - - trivialReporter.reportSpecStarting(spec1); - - expect(trivialReporter.log).toHaveBeenCalledWith('>> Jasmine Running suite 1 spec 1...'); - }); - }); -}); diff --git a/spec/jasmine-performance.yml b/spec/jasmine-performance.yml new file mode 100644 index 00000000..27a329e4 --- /dev/null +++ b/spec/jasmine-performance.yml @@ -0,0 +1,21 @@ +jasmine_dir: + - 'src' +jasmine_files: + - 'core/base.js' + - 'core/util.js' + - 'core/Reporter.js' + - 'html/HtmlReporterHelpers.js' + - 'core/ExpectationResult.js' + - '**/*.js' +jasmine_css_files: + - 'html/jasmine.css' +src_files: +stylesheets: +helpers: +spec_files: + - 'smoke/performance_test.js' +src_dir: +spec_dir: + - 'spec' + + diff --git a/spec/jasmine.yml b/spec/jasmine.yml index bd25c368..3561922e 100644 --- a/spec/jasmine.yml +++ b/spec/jasmine.yml @@ -1,38 +1,28 @@ -jasmine_dir: - - 'src' #This 'magic' inclusion order allows the travis build to pass. #TODO: search for the correct files to include to prevent -jasmine_files: +src_dir: + - 'src' +src_files: - 'core/base.js' - 'core/util.js' - - 'core/Reporter.js' #end of known dependencies + - 'core/Spec.js' - 'core/Env.js' - - 'core/Block.js' - 'core/JsApiReporter.js' - 'core/Matchers.js' - - 'core/mock-timeout.js' - - 'core/MultiReporter.js' - - 'core/NestedResults.js' - 'core/PrettyPrinter.js' - - 'core/Queue.js' - - 'core/Runner.js' - - 'core/Spec.js' - 'core/Suite.js' - - 'core/WaitsBlock.js' - - 'core/WaitsForBlock.js' - - 'html/HtmlReporterHelpers.js' - - 'html/HtmlReporter.js' + - 'html/**.js' - '**/*.js' -jasmine_css_files: - - 'html/jasmine.css' -src_files: stylesheets: +boot_dir: 'spec/support' +boot_files: + - 'boot.js' + - 'dev_boot.js' helpers: - 'helpers/**/*.js' spec_files: - '**/*[Ss]pec.js' -src_dir: spec_dir: - 'spec' diff --git a/spec/node_performance_suite.js b/spec/node_performance_suite.js new file mode 100644 index 00000000..15678385 --- /dev/null +++ b/spec/node_performance_suite.js @@ -0,0 +1,180 @@ +var fs = require('fs'); +var util = require('util'); +var path = require('path'); + +// yes, really keep this here to keep us honest, but only for jasmine's own runner! [xw] +// undefined = "diz be undefined yo"; + + +var jasmineGlobals = require('../lib/jasmine-core/jasmine.js'); +for (var k in jasmineGlobals) { + global[k] = jasmineGlobals[k]; +} +var env = jasmine.getEnv(); + +var jasmineInterface = { + describe: function(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + xdescribe: function(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + it: function(desc, func) { + return env.it(desc, func); + }, + + xit: function(desc, func) { + return env.xit(desc, func); + }, + + beforeEach: function(beforeEachFunction) { + return env.beforeEach(beforeEachFunction); + }, + + afterEach: function(afterEachFunction) { + return env.afterEach(afterEachFunction); + }, + + expect: function(actual) { + return env.expect(actual); + }, + + addMatchers: function(matchers) { + return env.addMatchers(matchers); + }, + + spyOn: function(obj, methodName) { + return env.spyOn(obj, methodName); + }, + + + jsApiReporter: new jasmine.JsApiReporter(jasmine) +}; + +for (var k in jasmineInterface) { + global[k] = jasmineInterface[k]; +} + +require('../src/console/ConsoleReporter.js'); + +/* + Pulling in code from jasmine-node. + + We can't just depend on jasmine-node because it has its own jasmine that it uses. + */ + +global.window = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval +}; + +delete global.window; + +function noop() { +} + +jasmine.executeSpecs = function(specs, done, isVerbose, showColors) { + global.originalJasmine = jasmine; + + for (var i = 0, len = specs.length; i < len; ++i) { + var filename = specs[i]; + require(filename.replace(/\.\w+$/, "")); + } + + var jasmineEnv = jasmine.getEnv(); + var consoleReporter = new jasmine.ConsoleReporter({ + print: util.print, + onComplete: done, + showColors: showColors + }); + + jasmineEnv.addReporter(consoleReporter); + jasmineEnv.execute(); +}; + +jasmine.getAllSpecFiles = function(dir, matcher) { + var specs = []; + + if (fs.statSync(dir).isFile() && dir.match(matcher)) { + specs.push(dir); + } else { + var files = fs.readdirSync(dir); + for (var i = 0, len = files.length; i < len; ++i) { + var filename = dir + '/' + files[i]; + if (fs.statSync(filename).isFile() && filename.match(matcher)) { + specs.push(filename); + } else if (fs.statSync(filename).isDirectory()) { + var subfiles = this.getAllSpecFiles(filename, matcher); + subfiles.forEach(function(result) { + specs.push(result); + }); + } + } + } + + return specs; +}; + +function now() { + return new Date().getTime(); +} + +jasmine.asyncSpecWait = function() { + var wait = jasmine.asyncSpecWait; + wait.start = now(); + wait.done = false; + (function innerWait() { + waits(10); + runs(function() { + if (wait.start + wait.timeout < now()) { + expect('timeout waiting for spec').toBeNull(); + } else if (wait.done) { + wait.done = false; + } else { + innerWait(); + } + }); + })(); +}; +jasmine.asyncSpecWait.timeout = 4 * 1000; +jasmine.asyncSpecDone = function() { + jasmine.asyncSpecWait.done = true; +}; + +for (var key in jasmine) { + exports[key] = jasmine[key]; +} + +/* + End jasmine-node runner + */ + +var isVerbose = false; +var showColors = true; +process.argv.forEach(function(arg) { + switch (arg) { + case '--color': showColors = true; break; + case '--noColor': showColors = false; break; + case '--verbose': isVerbose = true; break; + } +}); + +var specs = jasmine.getAllSpecFiles(__dirname + '/smoke/', new RegExp("test.js$")); +var domIndependentSpecs = []; +for (var i = 0; i < specs.length; i++) { + if (fs.readFileSync(specs[i], "utf8").indexOf("document.createElement") < 0) { + domIndependentSpecs.push(specs[i]); + } +} + +jasmine.executeSpecs(domIndependentSpecs, function(passed) { + if (passed) { + process.exit(0); + } else { + process.exit(1); + } +}, isVerbose, showColors); diff --git a/spec/node_suite.js b/spec/node_suite.js index bd2867b3..569f03c6 100644 --- a/spec/node_suite.js +++ b/spec/node_suite.js @@ -10,6 +10,58 @@ var jasmineGlobals = require('../lib/jasmine-core/jasmine.js'); for (var k in jasmineGlobals) { global[k] = jasmineGlobals[k]; } +var env = jasmine.getEnv(); + +var jasmineInterface = { + describe: function(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + xdescribe: function(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + it: function(desc, func) { + return env.it(desc, func); + }, + + xit: function(desc, func) { + return env.xit(desc, func); + }, + + beforeEach: function(beforeEachFunction) { + return env.beforeEach(beforeEachFunction); + }, + + afterEach: function(afterEachFunction) { + return env.afterEach(afterEachFunction); + }, + + expect: function(actual) { + return env.expect(actual); + }, + + addMatchers: function(matchers) { + return env.addMatchers(matchers); + }, + + spyOn: function(obj, methodName) { + return env.spyOn(obj, methodName); + }, + + clock: env.clock, + setTimeout: env.clock.setTimeout, + clearTimeout: env.clock.clearTimeout, + setInterval: env.clock.setInterval, + clearInterval: env.clock.clearInterval, + + jsApiReporter: new jasmine.JsApiReporter(jasmine) +}; + +for (var k in jasmineInterface) { + global[k] = jasmineInterface[k]; +} + require('../src/console/ConsoleReporter.js'); /* @@ -31,13 +83,19 @@ function noop() { } jasmine.executeSpecs = function(specs, done, isVerbose, showColors) { + global.originalJasmine = jasmine; + for (var i = 0, len = specs.length; i < len; ++i) { var filename = specs[i]; require(filename.replace(/\.\w+$/, "")); } var jasmineEnv = jasmine.getEnv(); - var consoleReporter = new jasmine.ConsoleReporter(util.print, done, showColors); + var consoleReporter = new jasmine.ConsoleReporter({ + print: util.print, + onComplete: done, + showColors: showColors + }); jasmineEnv.addReporter(consoleReporter); jasmineEnv.execute(); @@ -110,16 +168,17 @@ process.argv.forEach(function(arg) { } }); -var specs = jasmine.getAllSpecFiles(__dirname, new RegExp(".js$")); +// var specs = jasmine.getAllSpecFiles(__dirname + '/smoke', new RegExp("test.js$")); +var specs = jasmine.getAllSpecFiles(__dirname, new RegExp("Spec.js$")); var domIndependentSpecs = []; for (var i = 0; i < specs.length; i++) { - if (fs.readFileSync(specs[i], "utf8").indexOf("document.createElement") < 0) { + if (!specs[i].match('html')) { domIndependentSpecs.push(specs[i]); } } -jasmine.executeSpecs(domIndependentSpecs, function(runner, log) { - if (runner.results().failedCount === 0) { +jasmine.executeSpecs(domIndependentSpecs, function(passed) { + if (passed) { process.exit(0); } else { process.exit(1); diff --git a/spec/runner.html b/spec/runner.html index 0cde61fb..461632ad 100644 --- a/spec/runner.html +++ b/spec/runner.html @@ -1,5 +1,5 @@ + "http://www.w3.org/TR/html4/loose.dtd"> Jasmine Spec Runner: Jasmine Core @@ -8,77 +8,48 @@ + + + + + + + + + - - - - - - - - - - - + + + + + - - - - - - + + - - + - - - - - + + diff --git a/spec/smoke/performance_test.js b/spec/smoke/performance_test.js new file mode 100644 index 00000000..0c97d20c --- /dev/null +++ b/spec/smoke/performance_test.js @@ -0,0 +1,10 @@ +describe("performance", function() { + for (var i = 0; i < 10000; i++) { + it("should pass", function() { + expect(true).toBe(true); + }); + it("should fail", function() { + expect(true).toBe(false); + }); + } +}); diff --git a/spec/support/boot.js b/spec/support/boot.js new file mode 120000 index 00000000..fc5f90d3 --- /dev/null +++ b/spec/support/boot.js @@ -0,0 +1 @@ +../../lib/jasmine-core/boot/boot.js \ No newline at end of file diff --git a/spec/support/dev_boot.js b/spec/support/dev_boot.js new file mode 100644 index 00000000..acbfb235 --- /dev/null +++ b/spec/support/dev_boot.js @@ -0,0 +1,2 @@ +var originalJasmine = jasmine; +jasmine = null; diff --git a/spec/templates/runner.html.erb b/spec/templates/runner.html.erb index a4aceedd..01354438 100644 --- a/spec/templates/runner.html.erb +++ b/spec/templates/runner.html.erb @@ -14,6 +14,7 @@ <%= spec_file_tags %>