Changeset 2726:1b842e54f6bc for tests/functional/lib
- Timestamp:
- 07/09/14 15:03:41 (11 years ago)
- Branch:
- default
- Location:
- tests/functional/lib
- Files:
-
- 1 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
tests/functional/lib/jasmine-html.js
r1602 r2726 1 jasmine.HtmlReporterHelpers = {}; 2 3 jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { 4 var el = document.createElement(type); 5 6 for (var i = 2; i < arguments.length; i++) { 7 var child = arguments[i]; 8 9 if (typeof child === 'string') { 10 el.appendChild(document.createTextNode(child)); 11 } else { 12 if (child) { 13 el.appendChild(child); 14 } 1 /* 2 Copyright (c) 2008-2013 Pivotal Labs 3 4 Permission is hereby granted, free of charge, to any person obtaining 5 a copy of this software and associated documentation files (the 6 "Software"), to deal in the Software without restriction, including 7 without limitation the rights to use, copy, modify, merge, publish, 8 distribute, sublicense, and/or sell copies of the Software, and to 9 permit persons to whom the Software is furnished to do so, subject to 10 the following conditions: 11 12 The above copyright notice and this permission notice shall be 13 included in all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 jasmineRequire.html = function(j$) { 24 j$.ResultsNode = jasmineRequire.ResultsNode(); 25 j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); 26 j$.QueryString = jasmineRequire.QueryString(); 27 j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); 28 }; 29 30 jasmineRequire.HtmlReporter = function(j$) { 31 32 var noopTimer = { 33 start: function() {}, 34 elapsed: function() { return 0; } 35 }; 36 37 function HtmlReporter(options) { 38 var env = options.env || {}, 39 getContainer = options.getContainer, 40 createElement = options.createElement, 41 createTextNode = options.createTextNode, 42 onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, 43 timer = options.timer || noopTimer, 44 results = [], 45 specsExecuted = 0, 46 failureCount = 0, 47 pendingSpecCount = 0, 48 htmlReporterMain, 49 symbols; 50 51 this.initialize = function() { 52 htmlReporterMain = createDom("div", {className: "html-reporter"}, 53 createDom("div", {className: "banner"}, 54 createDom("span", {className: "title"}, "Jasmine"), 55 createDom("span", {className: "version"}, j$.version) 56 ), 57 createDom("ul", {className: "symbol-summary"}), 58 createDom("div", {className: "alert"}), 59 createDom("div", {className: "results"}, 60 createDom("div", {className: "failures"}) 61 ) 62 ); 63 getContainer().appendChild(htmlReporterMain); 64 65 symbols = find(".symbol-summary"); 66 }; 67 68 var totalSpecsDefined; 69 this.jasmineStarted = function(options) { 70 totalSpecsDefined = options.totalSpecsDefined || 0; 71 timer.start(); 72 }; 73 74 var summary = createDom("div", {className: "summary"}); 75 76 var topResults = new j$.ResultsNode({}, "", null), 77 currentParent = topResults; 78 79 this.suiteStarted = function(result) { 80 currentParent.addChild(result, "suite"); 81 currentParent = currentParent.last(); 82 }; 83 84 this.suiteDone = function(result) { 85 if (currentParent == topResults) { 86 return; 87 } 88 89 currentParent = currentParent.parent; 90 }; 91 92 this.specStarted = function(result) { 93 currentParent.addChild(result, "spec"); 94 }; 95 96 var failures = []; 97 this.specDone = function(result) { 98 if (result.status != "disabled") { 99 specsExecuted++; 100 } 101 102 symbols.appendChild(createDom("li", { 103 className: result.status, 104 id: "spec_" + result.id, 105 title: result.fullName 106 } 107 )); 108 109 if (result.status == "failed") { 110 failureCount++; 111 112 var failure = 113 createDom("div", {className: "spec-detail failed"}, 114 createDom("div", {className: "description"}, 115 createDom("a", {title: result.fullName, href: specHref(result)}, result.fullName) 116 ), 117 createDom("div", {className: "messages"}) 118 ); 119 var messages = failure.childNodes[1]; 120 121 for (var i = 0; i < result.failedExpectations.length; i++) { 122 var expectation = result.failedExpectations[i]; 123 messages.appendChild(createDom("div", {className: "result-message"}, expectation.message)); 124 messages.appendChild(createDom("div", {className: "stack-trace"}, expectation.stack)); 125 } 126 127 failures.push(failure); 128 } 129 130 if (result.status == "pending") { 131 pendingSpecCount++; 132 } 133 }; 134 135 this.jasmineDone = function() { 136 var banner = find(".banner"); 137 banner.appendChild(createDom("span", {className: "duration"}, "finished in " + timer.elapsed() / 1000 + "s")); 138 139 var alert = find(".alert"); 140 141 alert.appendChild(createDom("span", { className: "exceptions" }, 142 createDom("label", { className: "label", 'for': "raise-exceptions" }, "raise exceptions"), 143 createDom("input", { 144 className: "raise", 145 id: "raise-exceptions", 146 type: "checkbox" 147 }) 148 )); 149 var checkbox = find("input"); 150 151 checkbox.checked = !env.catchingExceptions(); 152 checkbox.onclick = onRaiseExceptionsClick; 153 154 if (specsExecuted < totalSpecsDefined) { 155 var skippedMessage = "Ran " + specsExecuted + " of " + totalSpecsDefined + " specs - run all"; 156 alert.appendChild( 157 createDom("span", {className: "bar skipped"}, 158 createDom("a", {href: "?", title: "Run all specs"}, skippedMessage) 159 ) 160 ); 161 } 162 var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount); 163 if (pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); } 164 165 var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); 166 alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage)); 167 168 var results = find(".results"); 169 results.appendChild(summary); 170 171 summaryList(topResults, summary); 172 173 function summaryList(resultsTree, domParent) { 174 var specListNode; 175 for (var i = 0; i < resultsTree.children.length; i++) { 176 var resultNode = resultsTree.children[i]; 177 if (resultNode.type == "suite") { 178 var suiteListNode = createDom("ul", {className: "suite", id: "suite-" + resultNode.result.id}, 179 createDom("li", {className: "suite-detail"}, 180 createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) 181 ) 182 ); 183 184 summaryList(resultNode, suiteListNode); 185 domParent.appendChild(suiteListNode); 186 } 187 if (resultNode.type == "spec") { 188 if (domParent.getAttribute("class") != "specs") { 189 specListNode = createDom("ul", {className: "specs"}); 190 domParent.appendChild(specListNode); 191 } 192 specListNode.appendChild( 193 createDom("li", { 194 className: resultNode.result.status, 195 id: "spec-" + resultNode.result.id 196 }, 197 createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) 198 ) 199 ); 200 } 201 } 202 } 203 204 if (failures.length) { 205 alert.appendChild( 206 createDom('span', {className: "menu bar spec-list"}, 207 createDom("span", {}, "Spec List | "), 208 createDom('a', {className: "failures-menu", href: "#"}, "Failures"))); 209 alert.appendChild( 210 createDom('span', {className: "menu bar failure-list"}, 211 createDom('a', {className: "spec-list-menu", href: "#"}, "Spec List"), 212 createDom("span", {}, " | Failures "))); 213 214 find(".failures-menu").onclick = function() { 215 setMenuModeTo('failure-list'); 216 }; 217 find(".spec-list-menu").onclick = function() { 218 setMenuModeTo('spec-list'); 219 }; 220 221 setMenuModeTo('failure-list'); 222 223 var failureNode = find(".failures"); 224 for (var i = 0; i < failures.length; i++) { 225 failureNode.appendChild(failures[i]); 226 } 227 } 228 }; 229 230 return this; 231 232 function find(selector) { 233 return getContainer().querySelector(selector); 234 } 235 236 function createDom(type, attrs, childrenVarArgs) { 237 var el = createElement(type); 238 239 for (var i = 2; i < arguments.length; i++) { 240 var child = arguments[i]; 241 242 if (typeof child === 'string') { 243 el.appendChild(createTextNode(child)); 244 } else { 245 if (child) { 246 el.appendChild(child); 247 } 248 } 249 } 250 251 for (var attr in attrs) { 252 if (attr == "className") { 253 el[attr] = attrs[attr]; 254 } else { 255 el.setAttribute(attr, attrs[attr]); 256 } 257 } 258 259 return el; 260 } 261 262 function pluralize(singular, count) { 263 var word = (count == 1 ? singular : singular + "s"); 264 265 return "" + count + " " + word; 266 } 267 268 function specHref(result) { 269 return "?spec=" + encodeURIComponent(result.fullName); 270 } 271 272 function setMenuModeTo(mode) { 273 htmlReporterMain.setAttribute("class", "html-reporter " + mode); 15 274 } 16 275 } 17 276 18 for (var attr in attrs) { 19 if (attr == "className") { 20 el[attr] = attrs[attr]; 21 } else { 22 el.setAttribute(attr, attrs[attr]); 23 } 277 return HtmlReporter; 278 }; 279 280 jasmineRequire.HtmlSpecFilter = function() { 281 function HtmlSpecFilter(options) { 282 var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 283 var filterPattern = new RegExp(filterString); 284 285 this.matches = function(specName) { 286 return filterPattern.test(specName); 287 }; 24 288 } 25 289 26 return el; 27 }; 28 29 jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { 30 var results = child.results(); 31 var status = results.passed() ? 'passed' : 'failed'; 32 if (results.skipped) { 33 status = 'skipped'; 290 return HtmlSpecFilter; 291 }; 292 293 jasmineRequire.ResultsNode = function() { 294 function ResultsNode(result, type, parent) { 295 this.result = result; 296 this.type = type; 297 this.parent = parent; 298 299 this.children = []; 300 301 this.addChild = function(result, type) { 302 this.children.push(new ResultsNode(result, type, this)); 303 }; 304 305 this.last = function() { 306 return this.children[this.children.length - 1]; 307 }; 34 308 } 35 309 36 return status; 37 }; 38 39 jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { 40 var parentDiv = this.dom.summary; 41 var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; 42 var parent = child[parentSuite]; 43 44 if (parent) { 45 if (typeof this.views.suites[parent.id] == 'undefined') { 46 this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); 47 } 48 parentDiv = this.views.suites[parent.id].element; 310 return ResultsNode; 311 }; 312 313 jasmineRequire.QueryString = function() { 314 function QueryString(options) { 315 316 this.setParam = function(key, value) { 317 var paramMap = queryStringToParamMap(); 318 paramMap[key] = value; 319 options.getWindowLocation().search = toQueryString(paramMap); 320 }; 321 322 this.getParam = function(key) { 323 return queryStringToParamMap()[key]; 324 }; 325 326 return this; 327 328 function toQueryString(paramMap) { 329 var qStrPairs = []; 330 for (var prop in paramMap) { 331 qStrPairs.push(encodeURIComponent(prop) + "=" + encodeURIComponent(paramMap[prop])); 332 } 333 return "?" + qStrPairs.join('&'); 334 } 335 336 function queryStringToParamMap() { 337 var paramStr = options.getWindowLocation().search.substring(1), 338 params = [], 339 paramMap = {}; 340 341 if (paramStr.length > 0) { 342 params = paramStr.split('&'); 343 for (var i = 0; i < params.length; i++) { 344 var p = params[i].split('='); 345 var value = decodeURIComponent(p[1]); 346 if (value === "true" || value === "false") { 347 value = JSON.parse(value); 348 } 349 paramMap[decodeURIComponent(p[0])] = value; 350 } 351 } 352 353 return paramMap; 354 } 355 49 356 } 50 357 51 parentDiv.appendChild(childElement); 52 }; 53 54 55 jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { 56 for(var fn in jasmine.HtmlReporterHelpers) { 57 ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; 58 } 59 }; 60 61 jasmine.HtmlReporter = function(_doc) { 62 var self = this; 63 var doc = _doc || window.document; 64 65 var reporterView; 66 67 var dom = {}; 68 69 // Jasmine Reporter Public Interface 70 self.logRunningSpecs = false; 71 72 self.reportRunnerStarting = function(runner) { 73 var specs = runner.specs() || []; 74 75 if (specs.length == 0) { 76 return; 77 } 78 79 createReporterDom(runner.env.versionString()); 80 doc.body.appendChild(dom.reporter); 81 setExceptionHandling(); 82 83 reporterView = new jasmine.HtmlReporter.ReporterView(dom); 84 reporterView.addSpecs(specs, self.specFilter); 85 }; 86 87 self.reportRunnerResults = function(runner) { 88 reporterView && reporterView.complete(); 89 }; 90 91 self.reportSuiteResults = function(suite) { 92 reporterView.suiteComplete(suite); 93 }; 94 95 self.reportSpecStarting = function(spec) { 96 if (self.logRunningSpecs) { 97 self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); 98 } 99 }; 100 101 self.reportSpecResults = function(spec) { 102 reporterView.specComplete(spec); 103 }; 104 105 self.log = function() { 106 var console = jasmine.getGlobal().console; 107 if (console && console.log) { 108 if (console.log.apply) { 109 console.log.apply(console, arguments); 110 } else { 111 console.log(arguments); // ie fix: console.log.apply doesn't exist on ie 112 } 113 } 114 }; 115 116 self.specFilter = function(spec) { 117 if (!focusedSpecName()) { 118 return true; 119 } 120 121 return spec.getFullName().indexOf(focusedSpecName()) === 0; 122 }; 123 124 return self; 125 126 function focusedSpecName() { 127 var specName; 128 129 (function memoizeFocusedSpec() { 130 if (specName) { 131 return; 132 } 133 134 var paramMap = []; 135 var params = jasmine.HtmlReporter.parameters(doc); 136 137 for (var i = 0; i < params.length; i++) { 138 var p = params[i].split('='); 139 paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); 140 } 141 142 specName = paramMap.spec; 143 })(); 144 145 return specName; 146 } 147 148 function createReporterDom(version) { 149 dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, 150 dom.banner = self.createDom('div', { className: 'banner' }, 151 self.createDom('span', { className: 'title' }, "Jasmine "), 152 self.createDom('span', { className: 'version' }, version)), 153 154 dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), 155 dom.alert = self.createDom('div', {className: 'alert'}, 156 self.createDom('span', { className: 'exceptions' }, 157 self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'), 158 self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))), 159 dom.results = self.createDom('div', {className: 'results'}, 160 dom.summary = self.createDom('div', { className: 'summary' }), 161 dom.details = self.createDom('div', { id: 'details' })) 162 ); 163 } 164 165 function noTryCatch() { 166 return window.location.search.match(/catch=false/); 167 } 168 169 function searchWithCatch() { 170 var params = jasmine.HtmlReporter.parameters(window.document); 171 var removed = false; 172 var i = 0; 173 174 while (!removed && i < params.length) { 175 if (params[i].match(/catch=/)) { 176 params.splice(i, 1); 177 removed = true; 178 } 179 i++; 180 } 181 if (jasmine.CATCH_EXCEPTIONS) { 182 params.push("catch=false"); 183 } 184 185 return params.join("&"); 186 } 187 188 function setExceptionHandling() { 189 var chxCatch = document.getElementById('no_try_catch'); 190 191 if (noTryCatch()) { 192 chxCatch.setAttribute('checked', true); 193 jasmine.CATCH_EXCEPTIONS = false; 194 } 195 chxCatch.onclick = function() { 196 window.location.search = searchWithCatch(); 197 }; 198 } 199 }; 200 jasmine.HtmlReporter.parameters = function(doc) { 201 var paramStr = doc.location.search.substring(1); 202 var params = []; 203 204 if (paramStr.length > 0) { 205 params = paramStr.split('&'); 206 } 207 return params; 208 } 209 jasmine.HtmlReporter.sectionLink = function(sectionName) { 210 var link = '?'; 211 var params = []; 212 213 if (sectionName) { 214 params.push('spec=' + encodeURIComponent(sectionName)); 215 } 216 if (!jasmine.CATCH_EXCEPTIONS) { 217 params.push("catch=false"); 218 } 219 if (params.length > 0) { 220 link += params.join("&"); 221 } 222 223 return link; 224 }; 225 jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); 226 jasmine.HtmlReporter.ReporterView = function(dom) { 227 this.startedAt = new Date(); 228 this.runningSpecCount = 0; 229 this.completeSpecCount = 0; 230 this.passedCount = 0; 231 this.failedCount = 0; 232 this.skippedCount = 0; 233 234 this.createResultsMenu = function() { 235 this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, 236 this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), 237 ' | ', 238 this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); 239 240 this.summaryMenuItem.onclick = function() { 241 dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); 242 }; 243 244 this.detailsMenuItem.onclick = function() { 245 showDetails(); 246 }; 247 }; 248 249 this.addSpecs = function(specs, specFilter) { 250 this.totalSpecCount = specs.length; 251 252 this.views = { 253 specs: {}, 254 suites: {} 255 }; 256 257 for (var i = 0; i < specs.length; i++) { 258 var spec = specs[i]; 259 this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); 260 if (specFilter(spec)) { 261 this.runningSpecCount++; 262 } 263 } 264 }; 265 266 this.specComplete = function(spec) { 267 this.completeSpecCount++; 268 269 if (isUndefined(this.views.specs[spec.id])) { 270 this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); 271 } 272 273 var specView = this.views.specs[spec.id]; 274 275 switch (specView.status()) { 276 case 'passed': 277 this.passedCount++; 278 break; 279 280 case 'failed': 281 this.failedCount++; 282 break; 283 284 case 'skipped': 285 this.skippedCount++; 286 break; 287 } 288 289 specView.refresh(); 290 this.refresh(); 291 }; 292 293 this.suiteComplete = function(suite) { 294 var suiteView = this.views.suites[suite.id]; 295 if (isUndefined(suiteView)) { 296 return; 297 } 298 suiteView.refresh(); 299 }; 300 301 this.refresh = function() { 302 303 if (isUndefined(this.resultsMenu)) { 304 this.createResultsMenu(); 305 } 306 307 // currently running UI 308 if (isUndefined(this.runningAlert)) { 309 this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" }); 310 dom.alert.appendChild(this.runningAlert); 311 } 312 this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); 313 314 // skipped specs UI 315 if (isUndefined(this.skippedAlert)) { 316 this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" }); 317 } 318 319 this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; 320 321 if (this.skippedCount === 1 && isDefined(dom.alert)) { 322 dom.alert.appendChild(this.skippedAlert); 323 } 324 325 // passing specs UI 326 if (isUndefined(this.passedAlert)) { 327 this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" }); 328 } 329 this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); 330 331 // failing specs UI 332 if (isUndefined(this.failedAlert)) { 333 this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); 334 } 335 this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); 336 337 if (this.failedCount === 1 && isDefined(dom.alert)) { 338 dom.alert.appendChild(this.failedAlert); 339 dom.alert.appendChild(this.resultsMenu); 340 } 341 342 // summary info 343 this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); 344 this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; 345 }; 346 347 this.complete = function() { 348 dom.alert.removeChild(this.runningAlert); 349 350 this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; 351 352 if (this.failedCount === 0) { 353 dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); 354 } else { 355 showDetails(); 356 } 357 358 dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); 359 }; 360 361 return this; 362 363 function showDetails() { 364 if (dom.reporter.className.search(/showDetails/) === -1) { 365 dom.reporter.className += " showDetails"; 366 } 367 } 368 369 function isUndefined(obj) { 370 return typeof obj === 'undefined'; 371 } 372 373 function isDefined(obj) { 374 return !isUndefined(obj); 375 } 376 377 function specPluralizedFor(count) { 378 var str = count + " spec"; 379 if (count > 1) { 380 str += "s" 381 } 382 return str; 383 } 384 385 }; 386 387 jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); 388 389 390 jasmine.HtmlReporter.SpecView = function(spec, dom, views) { 391 this.spec = spec; 392 this.dom = dom; 393 this.views = views; 394 395 this.symbol = this.createDom('li', { className: 'pending' }); 396 this.dom.symbolSummary.appendChild(this.symbol); 397 398 this.summary = this.createDom('div', { className: 'specSummary' }, 399 this.createDom('a', { 400 className: 'description', 401 href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()), 402 title: this.spec.getFullName() 403 }, this.spec.description) 404 ); 405 406 this.detail = this.createDom('div', { className: 'specDetail' }, 407 this.createDom('a', { 408 className: 'description', 409 href: '?spec=' + encodeURIComponent(this.spec.getFullName()), 410 title: this.spec.getFullName() 411 }, this.spec.getFullName()) 412 ); 413 }; 414 415 jasmine.HtmlReporter.SpecView.prototype.status = function() { 416 return this.getSpecStatus(this.spec); 417 }; 418 419 jasmine.HtmlReporter.SpecView.prototype.refresh = function() { 420 this.symbol.className = this.status(); 421 422 switch (this.status()) { 423 case 'skipped': 424 break; 425 426 case 'passed': 427 this.appendSummaryToSuiteDiv(); 428 break; 429 430 case 'failed': 431 this.appendSummaryToSuiteDiv(); 432 this.appendFailureDetail(); 433 break; 434 } 435 }; 436 437 jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { 438 this.summary.className += ' ' + this.status(); 439 this.appendToSummary(this.spec, this.summary); 440 }; 441 442 jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { 443 this.detail.className += ' ' + this.status(); 444 445 var resultItems = this.spec.results().getItems(); 446 var messagesDiv = this.createDom('div', { className: 'messages' }); 447 448 for (var i = 0; i < resultItems.length; i++) { 449 var result = resultItems[i]; 450 451 if (result.type == 'log') { 452 messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); 453 } else if (result.type == 'expect' && result.passed && !result.passed()) { 454 messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); 455 456 if (result.trace.stack) { 457 messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); 458 } 459 } 460 } 461 462 if (messagesDiv.childNodes.length > 0) { 463 this.detail.appendChild(messagesDiv); 464 this.dom.details.appendChild(this.detail); 465 } 466 }; 467 468 jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { 469 this.suite = suite; 470 this.dom = dom; 471 this.views = views; 472 473 this.element = this.createDom('div', { className: 'suite' }, 474 this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description) 475 ); 476 477 this.appendToSummary(this.suite, this.element); 478 }; 479 480 jasmine.HtmlReporter.SuiteView.prototype.status = function() { 481 return this.getSpecStatus(this.suite); 482 }; 483 484 jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { 485 this.element.className += " " + this.status(); 486 }; 487 488 jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); 489 490 /* @deprecated Use jasmine.HtmlReporter instead 491 */ 492 jasmine.TrivialReporter = function(doc) { 493 this.document = doc || document; 494 this.suiteDivs = {}; 495 this.logRunningSpecs = false; 496 }; 497 498 jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { 499 var el = document.createElement(type); 500 501 for (var i = 2; i < arguments.length; i++) { 502 var child = arguments[i]; 503 504 if (typeof child === 'string') { 505 el.appendChild(document.createTextNode(child)); 506 } else { 507 if (child) { el.appendChild(child); } 508 } 509 } 510 511 for (var attr in attrs) { 512 if (attr == "className") { 513 el[attr] = attrs[attr]; 514 } else { 515 el.setAttribute(attr, attrs[attr]); 516 } 517 } 518 519 return el; 520 }; 521 522 jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { 523 var showPassed, showSkipped; 524 525 this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, 526 this.createDom('div', { className: 'banner' }, 527 this.createDom('div', { className: 'logo' }, 528 this.createDom('span', { className: 'title' }, "Jasmine"), 529 this.createDom('span', { className: 'version' }, runner.env.versionString())), 530 this.createDom('div', { className: 'options' }, 531 "Show ", 532 showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), 533 this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), 534 showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), 535 this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") 536 ) 537 ), 538 539 this.runnerDiv = this.createDom('div', { className: 'runner running' }, 540 this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), 541 this.runnerMessageSpan = this.createDom('span', {}, "Running..."), 542 this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) 543 ); 544 545 this.document.body.appendChild(this.outerDiv); 546 547 var suites = runner.suites(); 548 for (var i = 0; i < suites.length; i++) { 549 var suite = suites[i]; 550 var suiteDiv = this.createDom('div', { className: 'suite' }, 551 this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), 552 this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); 553 this.suiteDivs[suite.id] = suiteDiv; 554 var parentDiv = this.outerDiv; 555 if (suite.parentSuite) { 556 parentDiv = this.suiteDivs[suite.parentSuite.id]; 557 } 558 parentDiv.appendChild(suiteDiv); 559 } 560 561 this.startedAt = new Date(); 562 563 var self = this; 564 showPassed.onclick = function(evt) { 565 if (showPassed.checked) { 566 self.outerDiv.className += ' show-passed'; 567 } else { 568 self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); 569 } 570 }; 571 572 showSkipped.onclick = function(evt) { 573 if (showSkipped.checked) { 574 self.outerDiv.className += ' show-skipped'; 575 } else { 576 self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); 577 } 578 }; 579 }; 580 581 jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { 582 var results = runner.results(); 583 var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; 584 this.runnerDiv.setAttribute("class", className); 585 //do it twice for IE 586 this.runnerDiv.setAttribute("className", className); 587 var specs = runner.specs(); 588 var specCount = 0; 589 for (var i = 0; i < specs.length; i++) { 590 if (this.specFilter(specs[i])) { 591 specCount++; 592 } 593 } 594 var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); 595 message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; 596 this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); 597 598 this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); 599 }; 600 601 jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { 602 var results = suite.results(); 603 var status = results.passed() ? 'passed' : 'failed'; 604 if (results.totalCount === 0) { // todo: change this to check results.skipped 605 status = 'skipped'; 606 } 607 this.suiteDivs[suite.id].className += " " + status; 608 }; 609 610 jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { 611 if (this.logRunningSpecs) { 612 this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); 613 } 614 }; 615 616 jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { 617 var results = spec.results(); 618 var status = results.passed() ? 'passed' : 'failed'; 619 if (results.skipped) { 620 status = 'skipped'; 621 } 622 var specDiv = this.createDom('div', { className: 'spec ' + status }, 623 this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), 624 this.createDom('a', { 625 className: 'description', 626 href: '?spec=' + encodeURIComponent(spec.getFullName()), 627 title: spec.getFullName() 628 }, spec.description)); 629 630 631 var resultItems = results.getItems(); 632 var messagesDiv = this.createDom('div', { className: 'messages' }); 633 for (var i = 0; i < resultItems.length; i++) { 634 var result = resultItems[i]; 635 636 if (result.type == 'log') { 637 messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); 638 } else if (result.type == 'expect' && result.passed && !result.passed()) { 639 messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); 640 641 if (result.trace.stack) { 642 messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); 643 } 644 } 645 } 646 647 if (messagesDiv.childNodes.length > 0) { 648 specDiv.appendChild(messagesDiv); 649 } 650 651 this.suiteDivs[spec.suite.id].appendChild(specDiv); 652 }; 653 654 jasmine.TrivialReporter.prototype.log = function() { 655 var console = jasmine.getGlobal().console; 656 if (console && console.log) { 657 if (console.log.apply) { 658 console.log.apply(console, arguments); 659 } else { 660 console.log(arguments); // ie fix: console.log.apply doesn't exist on ie 661 } 662 } 663 }; 664 665 jasmine.TrivialReporter.prototype.getLocation = function() { 666 return this.document.location; 667 }; 668 669 jasmine.TrivialReporter.prototype.specFilter = function(spec) { 670 var paramMap = {}; 671 var params = this.getLocation().search.substring(1).split('&'); 672 for (var i = 0; i < params.length; i++) { 673 var p = params[i].split('='); 674 paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); 675 } 676 677 if (!paramMap.spec) { 678 return true; 679 } 680 return spec.getFullName().indexOf(paramMap.spec) === 0; 681 }; 358 return QueryString; 359 }; -
tests/functional/lib/jasmine-jquery-bootstrap.js
r1724 r2726 2 2 jasmine.getFixtures().fixturesPath = 'fixtures'; 3 3 jasmine.getStyleFixtures().fixturesPath = '../../admin/style'; 4 5 var jasmineEnv = jasmine.getEnv();6 jasmineEnv.updateInterval = 1000;7 8 var htmlReporter = new jasmine.HtmlReporter();9 10 jasmineEnv.addReporter(htmlReporter);11 12 jasmineEnv.specFilter = function(spec) {13 return htmlReporter.specFilter(spec);14 };15 16 var currentWindowOnload = window.onload;17 18 window.onload = function() {19 if (currentWindowOnload) {20 currentWindowOnload();21 }22 execJasmine();23 };24 25 function execJasmine() {26 jasmineEnv.execute();27 }28 29 4 })(); -
tests/functional/lib/jasmine-jquery.js
r1602 r2726 1 1 /*! 2 3 4 Version 1.5.8 5 6 7 8 Copyright (c) 2010-2013Wojciech Zawistowski, Travis Jeffery9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 2 Jasmine-jQuery: a set of jQuery helpers for Jasmine tests. 3 4 Version 2.0.5 5 6 https://github.com/velesin/jasmine-jquery 7 8 Copyright (c) 2010-2014 Wojciech Zawistowski, Travis Jeffery 9 10 Permission is hereby granted, free of charge, to any person obtaining 11 a copy of this software and associated documentation files (the 12 "Software"), to deal in the Software without restriction, including 13 without limitation the rights to use, copy, modify, merge, publish, 14 distribute, sublicense, and/or sell copies of the Software, and to 15 permit persons to whom the Software is furnished to do so, subject to 16 the following conditions: 17 18 The above copyright notice and this permission notice shall be 19 included in all copies or substantial portions of the Software. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 25 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 28 */ 29 var readFixtures = function() { 30 return jasmine.getFixtures().proxyCallTo_('read', arguments) 31 } 32 33 var preloadFixtures = function() { 34 jasmine.getFixtures().proxyCallTo_('preload', arguments) 35 } 36 37 var loadFixtures = function() { 38 jasmine.getFixtures().proxyCallTo_('load', arguments) 39 } 40 41 var appendLoadFixtures = function() { 42 jasmine.getFixtures().proxyCallTo_('appendLoad', arguments) 43 } 44 45 var setFixtures = function(html) { 46 return jasmine.getFixtures().proxyCallTo_('set', arguments) 47 } 48 49 var appendSetFixtures = function() { 50 jasmine.getFixtures().proxyCallTo_('appendSet', arguments) 51 } 52 53 var sandbox = function(attributes) { 54 return jasmine.getFixtures().sandbox(attributes) 55 } 56 57 var spyOnEvent = function(selector, eventName) { 58 return jasmine.JQuery.events.spyOn(selector, eventName) 59 } 60 61 var preloadStyleFixtures = function() { 62 jasmine.getStyleFixtures().proxyCallTo_('preload', arguments) 63 } 64 65 var loadStyleFixtures = function() { 66 jasmine.getStyleFixtures().proxyCallTo_('load', arguments) 67 } 68 69 var appendLoadStyleFixtures = function() { 70 jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments) 71 } 72 73 var setStyleFixtures = function(html) { 74 jasmine.getStyleFixtures().proxyCallTo_('set', arguments) 75 } 76 77 var appendSetStyleFixtures = function(html) { 78 jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments) 79 } 80 81 var loadJSONFixtures = function() { 82 return jasmine.getJSONFixtures().proxyCallTo_('load', arguments) 83 } 84 85 var getJSONFixture = function(url) { 86 return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url] 87 } 88 89 jasmine.spiedEventsKey = function (selector, eventName) { 90 return [$(selector).selector, eventName].toString() 91 } 92 93 jasmine.getFixtures = function() { 94 return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures() 95 } 96 97 jasmine.getStyleFixtures = function() { 98 return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures() 99 } 100 101 jasmine.Fixtures = function() { 102 this.containerId = 'jasmine-fixtures' 103 this.fixturesCache_ = {} 104 this.fixturesPath = 'spec/javascripts/fixtures' 105 } 106 107 jasmine.Fixtures.prototype.set = function(html) { 108 this.cleanUp() 109 return this.createContainer_(html) 110 } 111 112 jasmine.Fixtures.prototype.appendSet= function(html) { 113 this.addToContainer_(html) 114 } 115 116 jasmine.Fixtures.prototype.preload = function() { 117 this.read.apply(this, arguments) 118 } 119 120 jasmine.Fixtures.prototype.load = function() { 121 this.cleanUp() 122 this.createContainer_(this.read.apply(this, arguments)) 123 } 124 125 jasmine.Fixtures.prototype.appendLoad = function() { 126 this.addToContainer_(this.read.apply(this, arguments)) 127 } 128 129 jasmine.Fixtures.prototype.read = function() { 130 var htmlChunks = [] 131 132 var fixtureUrls = arguments 133 for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { 134 htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex])) 135 } 136 137 return htmlChunks.join('') 138 } 139 140 jasmine.Fixtures.prototype.clearCache = function() { 141 this.fixturesCache_ = {} 142 } 143 144 jasmine.Fixtures.prototype.cleanUp = function() { 145 $('#' + this.containerId).remove() 146 } 147 148 jasmine.Fixtures.prototype.sandbox = function(attributes) { 149 var attributesToSet = attributes || {} 150 return $('<div id="sandbox" />').attr(attributesToSet) 151 } 152 153 jasmine.Fixtures.prototype.createContainer_ = function(html) { 154 var container = $('<div>') 29 30 +function (window, jasmine, $) { "use strict"; 31 32 jasmine.spiedEventsKey = function (selector, eventName) { 33 return [$(selector).selector, eventName].toString() 34 } 35 36 jasmine.getFixtures = function () { 37 return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures() 38 } 39 40 jasmine.getStyleFixtures = function () { 41 return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures() 42 } 43 44 jasmine.Fixtures = function () { 45 this.containerId = 'jasmine-fixtures' 46 this.fixturesCache_ = {} 47 this.fixturesPath = 'spec/javascripts/fixtures' 48 } 49 50 jasmine.Fixtures.prototype.set = function (html) { 51 this.cleanUp() 52 return this.createContainer_(html) 53 } 54 55 jasmine.Fixtures.prototype.appendSet= function (html) { 56 this.addToContainer_(html) 57 } 58 59 jasmine.Fixtures.prototype.preload = function () { 60 this.read.apply(this, arguments) 61 } 62 63 jasmine.Fixtures.prototype.load = function () { 64 this.cleanUp() 65 this.createContainer_(this.read.apply(this, arguments)) 66 } 67 68 jasmine.Fixtures.prototype.appendLoad = function () { 69 this.addToContainer_(this.read.apply(this, arguments)) 70 } 71 72 jasmine.Fixtures.prototype.read = function () { 73 var htmlChunks = [] 74 , fixtureUrls = arguments 75 76 for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { 77 htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex])) 78 } 79 80 return htmlChunks.join('') 81 } 82 83 jasmine.Fixtures.prototype.clearCache = function () { 84 this.fixturesCache_ = {} 85 } 86 87 jasmine.Fixtures.prototype.cleanUp = function () { 88 $('#' + this.containerId).remove() 89 } 90 91 jasmine.Fixtures.prototype.sandbox = function (attributes) { 92 var attributesToSet = attributes || {} 93 return $('<div id="sandbox" />').attr(attributesToSet) 94 } 95 96 jasmine.Fixtures.prototype.createContainer_ = function (html) { 97 var container = $('<div>') 155 98 .attr('id', this.containerId) 156 .html(html); 157 $(document.body).append(container) 158 return container 159 } 160 161 jasmine.Fixtures.prototype.addToContainer_ = function(html){ 162 var container = $(document.body).find('#'+this.containerId).append(html) 163 if(!container.length){ 164 this.createContainer_(html) 165 } 166 } 167 168 jasmine.Fixtures.prototype.getFixtureHtml_ = function(url) { 169 if (typeof this.fixturesCache_[url] === 'undefined') { 170 this.loadFixtureIntoCache_(url) 171 } 172 return this.fixturesCache_[url] 173 } 174 175 jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) { 176 var url = this.makeFixtureUrl_(relativeUrl) 177 var request = $.ajax({ 178 type: "GET", 179 url: url + "?" + new Date().getTime(), 180 async: false 181 }) 182 this.fixturesCache_[relativeUrl] = request.responseText 183 } 184 185 jasmine.Fixtures.prototype.makeFixtureUrl_ = function(relativeUrl){ 186 return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl 187 } 188 189 jasmine.Fixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) { 190 return this[methodName].apply(this, passedArguments) 191 } 192 193 194 jasmine.StyleFixtures = function() { 195 this.fixturesCache_ = {} 196 this.fixturesNodes_ = [] 197 this.fixturesPath = 'spec/javascripts/fixtures' 198 } 199 200 jasmine.StyleFixtures.prototype.set = function(css) { 201 this.cleanUp() 202 this.createStyle_(css) 203 } 204 205 jasmine.StyleFixtures.prototype.appendSet = function(css) { 206 this.createStyle_(css) 207 } 208 209 jasmine.StyleFixtures.prototype.preload = function() { 210 this.read_.apply(this, arguments) 211 } 212 213 jasmine.StyleFixtures.prototype.load = function() { 214 this.cleanUp() 215 this.createStyle_(this.read_.apply(this, arguments)) 216 } 217 218 jasmine.StyleFixtures.prototype.appendLoad = function() { 219 this.createStyle_(this.read_.apply(this, arguments)) 220 } 221 222 jasmine.StyleFixtures.prototype.cleanUp = function() { 223 while(this.fixturesNodes_.length) { 224 this.fixturesNodes_.pop().remove() 225 } 226 } 227 228 jasmine.StyleFixtures.prototype.createStyle_ = function(html) { 229 var styleText = $('<div></div>').html(html).text(), 230 style = $('<style>' + styleText + '</style>') 231 232 this.fixturesNodes_.push(style) 233 234 $('head').append(style) 235 } 236 237 jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache 238 239 jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read 240 241 jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_ 242 243 jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_ 244 245 jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_ 246 247 jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_ 248 249 jasmine.getJSONFixtures = function() { 250 return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures() 251 } 252 253 jasmine.JSONFixtures = function() { 254 this.fixturesCache_ = {} 255 this.fixturesPath = 'spec/javascripts/fixtures/json' 256 } 257 258 jasmine.JSONFixtures.prototype.load = function() { 259 this.read.apply(this, arguments) 260 return this.fixturesCache_ 261 } 262 263 jasmine.JSONFixtures.prototype.read = function() { 264 var fixtureUrls = arguments 265 for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { 266 this.getFixtureData_(fixtureUrls[urlIndex]) 267 } 268 return this.fixturesCache_ 269 } 270 271 jasmine.JSONFixtures.prototype.clearCache = function() { 272 this.fixturesCache_ = {} 273 } 274 275 jasmine.JSONFixtures.prototype.getFixtureData_ = function(url) { 276 this.loadFixtureIntoCache_(url) 277 return this.fixturesCache_[url] 278 } 279 280 jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) { 281 var self = this 282 var url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl 283 $.ajax({ 284 async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded 285 cache: false, 286 dataType: 'json', 287 url: url, 288 success: function(data) { 289 self.fixturesCache_[relativeUrl] = data 290 }, 291 error: function(jqXHR, status, errorThrown) { 292 throw Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + errorThrown.message + ')') 99 .html(html) 100 101 $(document.body).append(container) 102 return container 103 } 104 105 jasmine.Fixtures.prototype.addToContainer_ = function (html){ 106 var container = $(document.body).find('#'+this.containerId).append(html) 107 108 if (!container.length) { 109 this.createContainer_(html) 293 110 } 294 }) 295 } 296 297 jasmine.JSONFixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) { 298 return this[methodName].apply(this, passedArguments) 299 } 300 301 jasmine.JQuery = function() {} 302 303 jasmine.JQuery.browserTagCaseIndependentHtml = function(html) { 304 return $('<div/>').append(html).html() 305 } 306 307 jasmine.JQuery.elementToString = function(element) { 308 var domEl = $(element).get(0) 309 if (domEl == undefined || domEl.cloneNode) 310 return $('<div />').append($(element).clone()).html() 311 else 312 return element.toString() 313 } 314 315 jasmine.JQuery.matchersClass = {} 316 317 !function(namespace) { 111 } 112 113 jasmine.Fixtures.prototype.getFixtureHtml_ = function (url) { 114 if (typeof this.fixturesCache_[url] === 'undefined') { 115 this.loadFixtureIntoCache_(url) 116 } 117 return this.fixturesCache_[url] 118 } 119 120 jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { 121 var self = this 122 , url = this.makeFixtureUrl_(relativeUrl) 123 , htmlText = '' 124 , request = $.ajax({ 125 async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded 126 cache: false, 127 url: url, 128 success: function (data, status, $xhr) { 129 htmlText = $xhr.responseText 130 } 131 }).fail(function ($xhr, status, err) { 132 throw new Error('Fixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') 133 }) 134 135 var scripts = $($.parseHTML(htmlText, true)).find('script[src]') || []; 136 137 scripts.each(function(){ 138 $.ajax({ 139 async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded 140 cache: false, 141 dataType: 'script', 142 url: $(this).attr('src'), 143 success: function (data, status, $xhr) { 144 htmlText += '<script>' + $xhr.responseText + '</script>' 145 }, 146 error: function ($xhr, status, err) { 147 throw new Error('Script could not be loaded: ' + scriptSrc + ' (status: ' + status + ', message: ' + err.message + ')') 148 } 149 }); 150 }) 151 152 self.fixturesCache_[relativeUrl] = htmlText; 153 } 154 155 jasmine.Fixtures.prototype.makeFixtureUrl_ = function (relativeUrl){ 156 return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl 157 } 158 159 jasmine.Fixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { 160 return this[methodName].apply(this, passedArguments) 161 } 162 163 164 jasmine.StyleFixtures = function () { 165 this.fixturesCache_ = {} 166 this.fixturesNodes_ = [] 167 this.fixturesPath = 'spec/javascripts/fixtures' 168 } 169 170 jasmine.StyleFixtures.prototype.set = function (css) { 171 this.cleanUp() 172 this.createStyle_(css) 173 } 174 175 jasmine.StyleFixtures.prototype.appendSet = function (css) { 176 this.createStyle_(css) 177 } 178 179 jasmine.StyleFixtures.prototype.preload = function () { 180 this.read_.apply(this, arguments) 181 } 182 183 jasmine.StyleFixtures.prototype.load = function () { 184 this.cleanUp() 185 this.createStyle_(this.read_.apply(this, arguments)) 186 } 187 188 jasmine.StyleFixtures.prototype.appendLoad = function () { 189 this.createStyle_(this.read_.apply(this, arguments)) 190 } 191 192 jasmine.StyleFixtures.prototype.cleanUp = function () { 193 while(this.fixturesNodes_.length) { 194 this.fixturesNodes_.pop().remove() 195 } 196 } 197 198 jasmine.StyleFixtures.prototype.createStyle_ = function (html) { 199 var styleText = $('<div></div>').html(html).text() 200 , style = $('<style>' + styleText + '</style>') 201 202 this.fixturesNodes_.push(style) 203 $('head').append(style) 204 } 205 206 jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache 207 jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read 208 jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_ 209 jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_ 210 jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_ 211 jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_ 212 213 jasmine.getJSONFixtures = function () { 214 return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures() 215 } 216 217 jasmine.JSONFixtures = function () { 218 this.fixturesCache_ = {} 219 this.fixturesPath = 'spec/javascripts/fixtures/json' 220 } 221 222 jasmine.JSONFixtures.prototype.load = function () { 223 this.read.apply(this, arguments) 224 return this.fixturesCache_ 225 } 226 227 jasmine.JSONFixtures.prototype.read = function () { 228 var fixtureUrls = arguments 229 230 for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { 231 this.getFixtureData_(fixtureUrls[urlIndex]) 232 } 233 234 return this.fixturesCache_ 235 } 236 237 jasmine.JSONFixtures.prototype.clearCache = function () { 238 this.fixturesCache_ = {} 239 } 240 241 jasmine.JSONFixtures.prototype.getFixtureData_ = function (url) { 242 if (!this.fixturesCache_[url]) this.loadFixtureIntoCache_(url) 243 return this.fixturesCache_[url] 244 } 245 246 jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { 247 var self = this 248 , url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl 249 250 $.ajax({ 251 async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded 252 cache: false, 253 dataType: 'json', 254 url: url, 255 success: function (data) { 256 self.fixturesCache_[relativeUrl] = data 257 }, 258 error: function ($xhr, status, err) { 259 throw new Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') 260 } 261 }) 262 } 263 264 jasmine.JSONFixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { 265 return this[methodName].apply(this, passedArguments) 266 } 267 268 jasmine.jQuery = function () {} 269 270 jasmine.jQuery.browserTagCaseIndependentHtml = function (html) { 271 return $('<div/>').append(html).html() 272 } 273 274 jasmine.jQuery.elementToString = function (element) { 275 return $(element).map(function () { return this.outerHTML; }).toArray().join(', ') 276 } 277 318 278 var data = { 319 spiedEvents: {},320 handlers: []321 } 322 323 namespace.events = {324 spyOn: function (selector, eventName) {325 var handler = function (e) {279 spiedEvents: {} 280 , handlers: [] 281 } 282 283 jasmine.jQuery.events = { 284 spyOn: function (selector, eventName) { 285 var handler = function (e) { 326 286 data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = jasmine.util.argsToArray(arguments) 327 287 } 288 328 289 $(selector).on(eventName, handler) 329 290 data.handlers.push(handler) 291 330 292 return { 331 293 selector: selector, 332 294 eventName: eventName, 333 295 handler: handler, 334 reset: function (){296 reset: function (){ 335 297 delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] 336 298 } … … 338 300 }, 339 301 340 args: function (selector, eventName) {341 var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ;302 args: function (selector, eventName) { 303 var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] 342 304 343 305 if (!actualArgs) { 344 throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent." ;306 throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent." 345 307 } 346 308 347 return actualArgs ;309 return actualArgs 348 310 }, 349 311 350 wasTriggered: function (selector, eventName) {312 wasTriggered: function (selector, eventName) { 351 313 return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]) 352 314 }, 353 315 354 wasTriggeredWith: function(selector, eventName, expectedArgs, env) { 355 var actualArgs = jasmine.JQuery.events.args(selector, eventName).slice(1); 356 if (Object.prototype.toString.call(expectedArgs) !== '[object Array]') { 357 actualArgs = actualArgs[0]; 358 } 359 return env.equals_(expectedArgs, actualArgs); 316 wasTriggeredWith: function (selector, eventName, expectedArgs, util, customEqualityTesters) { 317 var actualArgs = jasmine.jQuery.events.args(selector, eventName).slice(1) 318 319 if (Object.prototype.toString.call(expectedArgs) !== '[object Array]') 320 actualArgs = actualArgs[0] 321 322 return util.equals(expectedArgs, actualArgs, customEqualityTesters) 360 323 }, 361 324 362 wasPrevented: function(selector, eventName) { 363 var args = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)], 364 e = args ? args[0] : undefined; 325 wasPrevented: function (selector, eventName) { 326 var args = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] 327 , e = args ? args[0] : undefined 328 365 329 return e && e.isDefaultPrevented() 366 330 }, 367 331 368 wasStopped: function (selector, eventName) {369 var args = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ,370 e = args ? args[0] : undefined;332 wasStopped: function (selector, eventName) { 333 var args = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] 334 , e = args ? args[0] : undefined 371 335 return e && e.isPropagationStopped() 372 336 }, 373 337 374 cleanUp: function () {338 cleanUp: function () { 375 339 data.spiedEvents = {} 376 340 data.handlers = [] 377 341 } 378 342 } 379 }(jasmine.JQuery) 380 381 !function(){ 382 var jQueryMatchers = { 383 toHaveClass: function(className) { 384 return this.actual.hasClass(className) 385 }, 386 387 toHaveCss: function(css){ 388 for (var prop in css){ 389 if (this.actual.css(prop) !== css[prop]) return false 343 344 var hasProperty = function (actualValue, expectedValue) { 345 if (expectedValue === undefined) 346 return actualValue !== undefined 347 348 return actualValue === expectedValue 349 } 350 351 beforeEach(function () { 352 jasmine.addMatchers({ 353 toHaveClass: function () { 354 return { 355 compare: function (actual, className) { 356 return { pass: $(actual).hasClass(className) } 357 } 358 } 359 }, 360 361 toHaveCss: function () { 362 return { 363 compare: function (actual, css) { 364 for (var prop in css){ 365 var value = css[prop] 366 // see issue #147 on gh 367 ;if (value === 'auto' && $(actual).get(0).style[prop] === 'auto') continue 368 if ($(actual).css(prop) !== value) return { pass: false } 369 } 370 return { pass: true } 371 } 372 } 373 }, 374 375 toBeVisible: function () { 376 return { 377 compare: function (actual) { 378 return { pass: $(actual).is(':visible') } 379 } 380 } 381 }, 382 383 toBeHidden: function () { 384 return { 385 compare: function (actual) { 386 return { pass: $(actual).is(':hidden') } 387 } 388 } 389 }, 390 391 toBeSelected: function () { 392 return { 393 compare: function (actual) { 394 return { pass: $(actual).is(':selected') } 395 } 396 } 397 }, 398 399 toBeChecked: function () { 400 return { 401 compare: function (actual) { 402 return { pass: $(actual).is(':checked') } 403 } 404 } 405 }, 406 407 toBeEmpty: function () { 408 return { 409 compare: function (actual) { 410 return { pass: $(actual).is(':empty') } 411 } 412 } 413 }, 414 415 toBeInDOM: function () { 416 return { 417 compare: function (actual) { 418 return { pass: $.contains(document.documentElement, $(actual)[0]) } 419 } 420 } 421 }, 422 423 toExist: function () { 424 return { 425 compare: function (actual) { 426 return { pass: $(actual).length } 427 } 428 } 429 }, 430 431 toHaveLength: function () { 432 return { 433 compare: function (actual, length) { 434 return { pass: $(actual).length === length } 435 } 436 } 437 }, 438 439 toHaveAttr: function () { 440 return { 441 compare: function (actual, attributeName, expectedAttributeValue) { 442 return { pass: hasProperty($(actual).attr(attributeName), expectedAttributeValue) } 443 } 444 } 445 }, 446 447 toHaveProp: function () { 448 return { 449 compare: function (actual, propertyName, expectedPropertyValue) { 450 return { pass: hasProperty($(actual).prop(propertyName), expectedPropertyValue) } 451 } 452 } 453 }, 454 455 toHaveId: function () { 456 return { 457 compare: function (actual, id) { 458 return { pass: $(actual).attr('id') == id } 459 } 460 } 461 }, 462 463 toHaveHtml: function () { 464 return { 465 compare: function (actual, html) { 466 return { pass: $(actual).html() == jasmine.jQuery.browserTagCaseIndependentHtml(html) } 467 } 468 } 469 }, 470 471 toContainHtml: function () { 472 return { 473 compare: function (actual, html) { 474 var actualHtml = $(actual).html() 475 , expectedHtml = jasmine.jQuery.browserTagCaseIndependentHtml(html) 476 477 return { pass: (actualHtml.indexOf(expectedHtml) >= 0) } 478 } 479 } 480 }, 481 482 toHaveText: function () { 483 return { 484 compare: function (actual, text) { 485 var actualText = $(actual).text() 486 var trimmedText = $.trim(actualText) 487 488 if (text && $.isFunction(text.test)) { 489 return { pass: text.test(actualText) || text.test(trimmedText) } 490 } else { 491 return { pass: (actualText == text || trimmedText == text) } 492 } 493 } 494 } 495 }, 496 497 toContainText: function () { 498 return { 499 compare: function (actual, text) { 500 var trimmedText = $.trim($(actual).text()) 501 502 if (text && $.isFunction(text.test)) { 503 return { pass: text.test(trimmedText) } 504 } else { 505 return { pass: trimmedText.indexOf(text) != -1 } 506 } 507 } 508 } 509 }, 510 511 toHaveValue: function () { 512 return { 513 compare: function (actual, value) { 514 return { pass: $(actual).val() === value } 515 } 516 } 517 }, 518 519 toHaveData: function () { 520 return { 521 compare: function (actual, key, expectedValue) { 522 return { pass: hasProperty($(actual).data(key), expectedValue) } 523 } 524 } 525 }, 526 527 toContainElement: function () { 528 return { 529 compare: function (actual, selector) { 530 if (window.debug) debugger 531 return { pass: $(actual).find(selector).length } 532 } 533 } 534 }, 535 536 toBeMatchedBy: function () { 537 return { 538 compare: function (actual, selector) { 539 return { pass: $(actual).filter(selector).length } 540 } 541 } 542 }, 543 544 toBeDisabled: function () { 545 return { 546 compare: function (actual, selector) { 547 return { pass: $(actual).is(':disabled') } 548 } 549 } 550 }, 551 552 toBeFocused: function (selector) { 553 return { 554 compare: function (actual, selector) { 555 return { pass: $(actual)[0] === $(actual)[0].ownerDocument.activeElement } 556 } 557 } 558 }, 559 560 toHandle: function () { 561 return { 562 compare: function (actual, event) { 563 var events = $._data($(actual).get(0), "events") 564 565 if (!events || !event || typeof event !== "string") { 566 return { pass: false } 567 } 568 569 var namespaces = event.split(".") 570 , eventType = namespaces.shift() 571 , sortedNamespaces = namespaces.slice(0).sort() 572 , namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") 573 574 if (events[eventType] && namespaces.length) { 575 for (var i = 0; i < events[eventType].length; i++) { 576 var namespace = events[eventType][i].namespace 577 578 if (namespaceRegExp.test(namespace)) 579 return { pass: true } 580 } 581 } else { 582 return { pass: (events[eventType] && events[eventType].length > 0) } 583 } 584 585 return { pass: false } 586 } 587 } 588 }, 589 590 toHandleWith: function () { 591 return { 592 compare: function (actual, eventName, eventHandler) { 593 var normalizedEventName = eventName.split('.')[0] 594 , stack = $._data($(actual).get(0), "events")[normalizedEventName] 595 596 for (var i = 0; i < stack.length; i++) { 597 if (stack[i].handler == eventHandler) return { pass: true } 598 } 599 600 return { pass: false } 601 } 602 } 603 }, 604 605 toHaveBeenTriggeredOn: function () { 606 return { 607 compare: function (actual, selector) { 608 var result = { pass: jasmine.jQuery.events.wasTriggered(selector, actual) } 609 610 result.message = result.pass ? 611 "Expected event " + $(actual) + " not to have been triggered on " + selector : 612 "Expected event " + $(actual) + " to have been triggered on " + selector 613 614 return result; 615 } 616 } 617 }, 618 619 toHaveBeenTriggered: function (){ 620 return { 621 compare: function (actual) { 622 var eventName = actual.eventName 623 , selector = actual.selector 624 , result = { pass: jasmine.jQuery.events.wasTriggered(selector, eventName) } 625 626 result.message = result.pass ? 627 "Expected event " + eventName + " not to have been triggered on " + selector : 628 "Expected event " + eventName + " to have been triggered on " + selector 629 630 return result 631 } 632 } 633 }, 634 635 toHaveBeenTriggeredOnAndWith: function (j$, customEqualityTesters) { 636 return { 637 compare: function (actual, selector, expectedArgs) { 638 var wasTriggered = jasmine.jQuery.events.wasTriggered(selector, actual) 639 , result = { pass: wasTriggered && jasmine.jQuery.events.wasTriggeredWith(selector, actual, expectedArgs, j$, customEqualityTesters) } 640 641 if (wasTriggered) { 642 var actualArgs = jasmine.jQuery.events.args(selector, actual, expectedArgs)[1] 643 result.message = result.pass ? 644 "Expected event " + actual + " not to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) : 645 "Expected event " + actual + " to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) 646 647 } else { 648 // todo check on this 649 result.message = result.pass ? 650 "Expected event " + actual + " not to have been triggered on " + selector : 651 "Expected event " + actual + " to have been triggered on " + selector 652 } 653 654 return result 655 } 656 } 657 }, 658 659 toHaveBeenPreventedOn: function () { 660 return { 661 compare: function (actual, selector) { 662 var result = { pass: jasmine.jQuery.events.wasPrevented(selector, actual) } 663 664 result.message = result.pass ? 665 "Expected event " + actual + " not to have been prevented on " + selector : 666 "Expected event " + actual + " to have been prevented on " + selector 667 668 return result 669 } 670 } 671 }, 672 673 toHaveBeenPrevented: function () { 674 return { 675 compare: function (actual) { 676 var eventName = actual.eventName 677 , selector = actual.selector 678 , result = { pass: jasmine.jQuery.events.wasPrevented(selector, eventName) } 679 680 result.message = result.pass ? 681 "Expected event " + eventName + " not to have been prevented on " + selector : 682 "Expected event " + eventName + " to have been prevented on " + selector 683 684 return result 685 } 686 } 687 }, 688 689 toHaveBeenStoppedOn: function () { 690 return { 691 compare: function (actual, selector) { 692 var result = { pass: jasmine.jQuery.events.wasStopped(selector, actual) } 693 694 result.message = result.pass ? 695 "Expected event " + actual + " not to have been stopped on " + selector : 696 "Expected event " + actual + " to have been stopped on " + selector 697 698 return result; 699 } 700 } 701 }, 702 703 toHaveBeenStopped: function () { 704 return { 705 compare: function (actual) { 706 var eventName = actual.eventName 707 , selector = actual.selector 708 , result = { pass: jasmine.jQuery.events.wasStopped(selector, eventName) } 709 710 result.message = result.pass ? 711 "Expected event " + eventName + " not to have been stopped on " + selector : 712 "Expected event " + eventName + " to have been stopped on " + selector 713 714 return result 715 } 716 } 390 717 } 391 return true 392 }, 393 394 toBeVisible: function() { 395 return this.actual.is(':visible') 396 }, 397 398 toBeHidden: function() { 399 return this.actual.is(':hidden') 400 }, 401 402 toBeSelected: function() { 403 return this.actual.is(':selected') 404 }, 405 406 toBeChecked: function() { 407 return this.actual.is(':checked') 408 }, 409 410 toBeEmpty: function() { 411 return this.actual.is(':empty') 412 }, 413 414 toExist: function() { 415 return $(document).find(this.actual).length 416 }, 417 418 toHaveLength: function(length) { 419 return this.actual.length === length 420 }, 421 422 toHaveAttr: function(attributeName, expectedAttributeValue) { 423 return hasProperty(this.actual.attr(attributeName), expectedAttributeValue) 424 }, 425 426 toHaveProp: function(propertyName, expectedPropertyValue) { 427 return hasProperty(this.actual.prop(propertyName), expectedPropertyValue) 428 }, 429 430 toHaveId: function(id) { 431 return this.actual.attr('id') == id 432 }, 433 434 toHaveHtml: function(html) { 435 return this.actual.html() == jasmine.JQuery.browserTagCaseIndependentHtml(html) 436 }, 437 438 toContainHtml: function(html){ 439 var actualHtml = this.actual.html() 440 var expectedHtml = jasmine.JQuery.browserTagCaseIndependentHtml(html) 441 return (actualHtml.indexOf(expectedHtml) >= 0) 442 }, 443 444 toHaveText: function(text) { 445 var trimmedText = $.trim(this.actual.text()) 446 if (text && $.isFunction(text.test)) { 447 return text.test(trimmedText) 448 } else { 449 return trimmedText == text 450 } 451 }, 452 453 toContainText: function(text) { 454 var trimmedText = $.trim(this.actual.text()) 455 if (text && $.isFunction(text.test)) { 456 return text.test(trimmedText) 457 } else { 458 return trimmedText.indexOf(text) != -1; 459 } 460 }, 461 462 toHaveValue: function(value) { 463 return this.actual.val() === value 464 }, 465 466 toHaveData: function(key, expectedValue) { 467 return hasProperty(this.actual.data(key), expectedValue) 468 }, 469 470 toBe: function(selector) { 471 return this.actual.is(selector) 472 }, 473 474 toContain: function(selector) { 475 return this.actual.find(selector).length 476 }, 477 478 toBeMatchedBy: function(selector) { 479 return this.actual.filter(selector).length 480 }, 481 482 toBeDisabled: function(selector){ 483 return this.actual.is(':disabled') 484 }, 485 486 toBeFocused: function(selector) { 487 return this.actual[0] === this.actual[0].ownerDocument.activeElement 488 }, 489 490 toHandle: function(event) { 491 492 var events = $._data(this.actual.get(0), "events") 493 494 if(!events || !event || typeof event !== "string") { 495 return false 496 } 497 498 var namespaces = event.split(".") 499 var eventType = namespaces.shift() 500 var sortedNamespaces = namespaces.slice(0).sort() 501 var namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") 502 503 if(events[eventType] && namespaces.length) { 504 for(var i = 0; i < events[eventType].length; i++) { 505 var namespace = events[eventType][i].namespace 506 if(namespaceRegExp.test(namespace)) { 507 return true 508 } 509 } 510 } else { 511 return events[eventType] && events[eventType].length > 0 512 } 513 }, 514 515 // tests the existence of a specific event binding + handler 516 toHandleWith: function(eventName, eventHandler) { 517 var normalizedEventName = eventName.split('.')[0]; 518 var stack = $._data(this.actual.get(0), "events")[normalizedEventName] 519 for (var i = 0; i < stack.length; i++) { 520 if (stack[i].handler == eventHandler) return true 521 } 522 return false 523 } 524 } 525 526 var hasProperty = function(actualValue, expectedValue) { 527 if (expectedValue === undefined) return actualValue !== undefined 528 return actualValue == expectedValue 529 } 530 531 var bindMatcher = function(methodName) { 532 var builtInMatcher = jasmine.Matchers.prototype[methodName] 533 534 jasmine.JQuery.matchersClass[methodName] = function() { 535 if (this.actual 536 && (this.actual instanceof $ 537 || jasmine.isDomNode(this.actual))) { 538 this.actual = $(this.actual) 539 var result = jQueryMatchers[methodName].apply(this, arguments) 540 var element 541 if (this.actual.get && (element = this.actual.get()[0]) && !$.isWindow(element) && element.tagName !== "HTML") 542 this.actual = jasmine.JQuery.elementToString(this.actual) 543 return result 544 } 545 546 if (builtInMatcher) { 547 return builtInMatcher.apply(this, arguments) 548 } 549 550 return false 551 } 552 } 553 554 for(var methodName in jQueryMatchers) { 555 bindMatcher(methodName) 556 } 557 }() 558 559 beforeEach(function() { 560 this.addMatchers(jasmine.JQuery.matchersClass) 561 this.addMatchers({ 562 toHaveBeenTriggeredOn: function(selector) { 563 this.message = function() { 564 return [ 565 "Expected event " + this.actual + " to have been triggered on " + selector, 566 "Expected event " + this.actual + " not to have been triggered on " + selector 567 ] 568 } 569 return jasmine.JQuery.events.wasTriggered(selector, this.actual) 570 } 718 }) 719 720 jasmine.getEnv().addCustomEqualityTester(function(a, b) { 721 if (a && b) { 722 if (a instanceof $ || jasmine.isDomNode(a)) { 723 var $a = $(a) 724 725 if (b instanceof $) 726 return $a.length == b.length && a.is(b) 727 728 return $a.is(b); 729 } 730 731 if (b instanceof $ || jasmine.isDomNode(b)) { 732 var $b = $(b) 733 734 if (a instanceof $) 735 return a.length == $b.length && $b.is(a) 736 737 return $(b).is(a); 738 } 739 } 740 }) 741 742 jasmine.getEnv().addCustomEqualityTester(function (a, b) { 743 if (a instanceof $ && b instanceof $ && a.size() == b.size()) 744 return a.is(b) 745 }) 571 746 }) 572 this.addMatchers({ 573 toHaveBeenTriggered: function(){ 574 var eventName = this.actual.eventName, 575 selector = this.actual.selector 576 this.message = function() { 577 return [ 578 "Expected event " + eventName + " to have been triggered on " + selector, 579 "Expected event " + eventName + " not to have been triggered on " + selector 580 ] 581 } 582 return jasmine.JQuery.events.wasTriggered(selector, eventName) 583 } 747 748 afterEach(function () { 749 jasmine.getFixtures().cleanUp() 750 jasmine.getStyleFixtures().cleanUp() 751 jasmine.jQuery.events.cleanUp() 584 752 }) 585 this.addMatchers({ 586 toHaveBeenTriggeredOnAndWith: function() { 587 var selector = arguments[0], 588 expectedArgs = arguments[1], 589 wasTriggered = jasmine.JQuery.events.wasTriggered(selector, this.actual); 590 this.message = function() { 591 if (wasTriggered) { 592 var actualArgs = jasmine.JQuery.events.args(selector, this.actual, expectedArgs)[1]; 593 return [ 594 "Expected event " + this.actual + " to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs), 595 "Expected event " + this.actual + " not to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) 596 ] 597 } else { 598 return [ 599 "Expected event " + this.actual + " to have been triggered on " + selector, 600 "Expected event " + this.actual + " not to have been triggered on " + selector 601 ] 602 } 603 } 604 return wasTriggered && jasmine.JQuery.events.wasTriggeredWith(selector, this.actual, expectedArgs, this.env); 605 } 606 }) 607 this.addMatchers({ 608 toHaveBeenPreventedOn: function(selector) { 609 this.message = function() { 610 return [ 611 "Expected event " + this.actual + " to have been prevented on " + selector, 612 "Expected event " + this.actual + " not to have been prevented on " + selector 613 ] 614 } 615 return jasmine.JQuery.events.wasPrevented(selector, this.actual) 616 } 617 }) 618 this.addMatchers({ 619 toHaveBeenPrevented: function() { 620 var eventName = this.actual.eventName, 621 selector = this.actual.selector 622 this.message = function() { 623 return [ 624 "Expected event " + eventName + " to have been prevented on " + selector, 625 "Expected event " + eventName + " not to have been prevented on " + selector 626 ] 627 } 628 return jasmine.JQuery.events.wasPrevented(selector, eventName) 629 } 630 }) 631 this.addMatchers({ 632 toHaveBeenStoppedOn: function(selector) { 633 this.message = function() { 634 return [ 635 "Expected event " + this.actual + " to have been stopped on " + selector, 636 "Expected event " + this.actual + " not to have been stopped on " + selector 637 ] 638 } 639 return jasmine.JQuery.events.wasStopped(selector, this.actual) 640 } 641 }) 642 this.addMatchers({ 643 toHaveBeenStopped: function() { 644 var eventName = this.actual.eventName, 645 selector = this.actual.selector 646 this.message = function() { 647 return [ 648 "Expected event " + eventName + " to have been stopped on " + selector, 649 "Expected event " + eventName + " not to have been stopped on " + selector 650 ] 651 } 652 return jasmine.JQuery.events.wasStopped(selector, eventName) 653 } 654 }) 655 jasmine.getEnv().addEqualityTester(function(a, b) { 656 if(a instanceof jQuery && b instanceof jQuery) { 657 if(a.size() != b.size()) { 658 return jasmine.undefined; 659 } 660 else if(a.is(b)) { 661 return true; 662 } 663 } 664 return jasmine.undefined; 665 666 }) 667 }) 668 669 afterEach(function() { 670 jasmine.getFixtures().cleanUp() 671 jasmine.getStyleFixtures().cleanUp() 672 jasmine.JQuery.events.cleanUp() 673 }) 753 754 window.readFixtures = function () { 755 return jasmine.getFixtures().proxyCallTo_('read', arguments) 756 } 757 758 window.preloadFixtures = function () { 759 jasmine.getFixtures().proxyCallTo_('preload', arguments) 760 } 761 762 window.loadFixtures = function () { 763 jasmine.getFixtures().proxyCallTo_('load', arguments) 764 } 765 766 window.appendLoadFixtures = function () { 767 jasmine.getFixtures().proxyCallTo_('appendLoad', arguments) 768 } 769 770 window.setFixtures = function (html) { 771 return jasmine.getFixtures().proxyCallTo_('set', arguments) 772 } 773 774 window.appendSetFixtures = function () { 775 jasmine.getFixtures().proxyCallTo_('appendSet', arguments) 776 } 777 778 window.sandbox = function (attributes) { 779 return jasmine.getFixtures().sandbox(attributes) 780 } 781 782 window.spyOnEvent = function (selector, eventName) { 783 return jasmine.jQuery.events.spyOn(selector, eventName) 784 } 785 786 window.preloadStyleFixtures = function () { 787 jasmine.getStyleFixtures().proxyCallTo_('preload', arguments) 788 } 789 790 window.loadStyleFixtures = function () { 791 jasmine.getStyleFixtures().proxyCallTo_('load', arguments) 792 } 793 794 window.appendLoadStyleFixtures = function () { 795 jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments) 796 } 797 798 window.setStyleFixtures = function (html) { 799 jasmine.getStyleFixtures().proxyCallTo_('set', arguments) 800 } 801 802 window.appendSetStyleFixtures = function (html) { 803 jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments) 804 } 805 806 window.loadJSONFixtures = function () { 807 return jasmine.getJSONFixtures().proxyCallTo_('load', arguments) 808 } 809 810 window.getJSONFixture = function (url) { 811 return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url] 812 } 813 }(window, window.jasmine, window.jQuery); -
tests/functional/lib/jasmine.js
r1602 r2726 1 var isCommonJS = typeof window == "undefined" && typeof exports == "object"; 2 3 /** 4 * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. 5 * 6 * @namespace 7 */ 8 var jasmine = {}; 9 if (isCommonJS) exports.jasmine = jasmine; 10 /** 11 * @private 12 */ 13 jasmine.unimplementedMethod_ = function() { 14 throw new Error("unimplemented method"); 15 }; 16 17 /** 18 * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just 19 * a plain old variable and may be redefined by somebody else. 20 * 21 * @private 22 */ 23 jasmine.undefined = jasmine.___undefined___; 24 25 /** 26 * Show diagnostic messages in the console if set to true 27 * 28 */ 29 jasmine.VERBOSE = false; 30 31 /** 32 * 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. 33 * 34 */ 35 jasmine.DEFAULT_UPDATE_INTERVAL = 250; 36 37 /** 38 * Maximum levels of nesting that will be included when an object is pretty-printed 39 */ 40 jasmine.MAX_PRETTY_PRINT_DEPTH = 40; 41 42 /** 43 * Default timeout interval in milliseconds for waitsFor() blocks. 44 */ 45 jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; 46 47 /** 48 * 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. 49 * Set to false to let the exception bubble up in the browser. 50 * 51 */ 52 jasmine.CATCH_EXCEPTIONS = true; 53 54 jasmine.getGlobal = function() { 55 function getGlobal() { 1 /* 2 Copyright (c) 2008-2013 Pivotal Labs 3 4 Permission is hereby granted, free of charge, to any person obtaining 5 a copy of this software and associated documentation files (the 6 "Software"), to deal in the Software without restriction, including 7 without limitation the rights to use, copy, modify, merge, publish, 8 distribute, sublicense, and/or sell copies of the Software, and to 9 permit persons to whom the Software is furnished to do so, subject to 10 the following conditions: 11 12 The above copyright notice and this permission notice shall be 13 included in all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 function getJasmineRequireObj() { 24 if (typeof module !== "undefined" && module.exports) { 25 return exports; 26 } else { 27 window.jasmineRequire = window.jasmineRequire || {}; 28 return window.jasmineRequire; 29 } 30 } 31 32 getJasmineRequireObj().core = function(jRequire) { 33 var j$ = {}; 34 35 jRequire.base(j$); 36 j$.util = jRequire.util(); 37 j$.Any = jRequire.Any(); 38 j$.CallTracker = jRequire.CallTracker(); 39 j$.Clock = jRequire.Clock(); 40 j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); 41 j$.Env = jRequire.Env(j$); 42 j$.ExceptionFormatter = jRequire.ExceptionFormatter(); 43 j$.Expectation = jRequire.Expectation(); 44 j$.buildExpectationResult = jRequire.buildExpectationResult(); 45 j$.JsApiReporter = jRequire.JsApiReporter(); 46 j$.matchersUtil = jRequire.matchersUtil(j$); 47 j$.ObjectContaining = jRequire.ObjectContaining(j$); 48 j$.pp = jRequire.pp(j$); 49 j$.QueueRunner = jRequire.QueueRunner(); 50 j$.ReportDispatcher = jRequire.ReportDispatcher(); 51 j$.Spec = jRequire.Spec(j$); 52 j$.SpyStrategy = jRequire.SpyStrategy(); 53 j$.Suite = jRequire.Suite(); 54 j$.Timer = jRequire.Timer(); 55 j$.version = jRequire.version(); 56 57 j$.matchers = jRequire.requireMatchers(jRequire, j$); 58 59 return j$; 60 }; 61 62 getJasmineRequireObj().requireMatchers = function(jRequire, j$) { 63 var availableMatchers = [ 64 "toBe", 65 "toBeCloseTo", 66 "toBeDefined", 67 "toBeFalsy", 68 "toBeGreaterThan", 69 "toBeLessThan", 70 "toBeNaN", 71 "toBeNull", 72 "toBeTruthy", 73 "toBeUndefined", 74 "toContain", 75 "toEqual", 76 "toHaveBeenCalled", 77 "toHaveBeenCalledWith", 78 "toMatch", 79 "toThrow", 80 "toThrowError" 81 ], 82 matchers = {}; 83 84 for (var i = 0; i < availableMatchers.length; i++) { 85 var name = availableMatchers[i]; 86 matchers[name] = jRequire[name](j$); 87 } 88 89 return matchers; 90 }; 91 92 getJasmineRequireObj().base = function(j$) { 93 j$.unimplementedMethod_ = function() { 94 throw new Error("unimplemented method"); 95 }; 96 97 j$.MAX_PRETTY_PRINT_DEPTH = 40; 98 j$.DEFAULT_TIMEOUT_INTERVAL = 5000; 99 100 j$.getGlobal = (function() { 101 var jasmineGlobal = eval.call(null, "this"); 102 return function() { 103 return jasmineGlobal; 104 }; 105 })(); 106 107 j$.getEnv = function(options) { 108 var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options); 109 //jasmine. singletons in here (setTimeout blah blah). 110 return env; 111 }; 112 113 j$.isArray_ = function(value) { 114 return j$.isA_("Array", value); 115 }; 116 117 j$.isString_ = function(value) { 118 return j$.isA_("String", value); 119 }; 120 121 j$.isNumber_ = function(value) { 122 return j$.isA_("Number", value); 123 }; 124 125 j$.isA_ = function(typeName, value) { 126 return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; 127 }; 128 129 j$.isDomNode = function(obj) { 130 return obj.nodeType > 0; 131 }; 132 133 j$.any = function(clazz) { 134 return new j$.Any(clazz); 135 }; 136 137 j$.objectContaining = function(sample) { 138 return new j$.ObjectContaining(sample); 139 }; 140 141 j$.createSpy = function(name, originalFn) { 142 143 var spyStrategy = new j$.SpyStrategy({ 144 name: name, 145 fn: originalFn, 146 getSpy: function() { return spy; } 147 }), 148 callTracker = new j$.CallTracker(), 149 spy = function() { 150 callTracker.track({ 151 object: this, 152 args: Array.prototype.slice.apply(arguments) 153 }); 154 return spyStrategy.exec.apply(this, arguments); 155 }; 156 157 for (var prop in originalFn) { 158 if (prop === 'and' || prop === 'calls') { 159 throw new Error("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon"); 160 } 161 162 spy[prop] = originalFn[prop]; 163 } 164 165 spy.and = spyStrategy; 166 spy.calls = callTracker; 167 168 return spy; 169 }; 170 171 j$.isSpy = function(putativeSpy) { 172 if (!putativeSpy) { 173 return false; 174 } 175 return putativeSpy.and instanceof j$.SpyStrategy && 176 putativeSpy.calls instanceof j$.CallTracker; 177 }; 178 179 j$.createSpyObj = function(baseName, methodNames) { 180 if (!j$.isArray_(methodNames) || methodNames.length === 0) { 181 throw "createSpyObj requires a non-empty array of method names to create spies for"; 182 } 183 var obj = {}; 184 for (var i = 0; i < methodNames.length; i++) { 185 obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]); 186 } 187 return obj; 188 }; 189 }; 190 191 getJasmineRequireObj().util = function() { 192 193 var util = {}; 194 195 util.inherit = function(childClass, parentClass) { 196 var Subclass = function() { 197 }; 198 Subclass.prototype = parentClass.prototype; 199 childClass.prototype = new Subclass(); 200 }; 201 202 util.htmlEscape = function(str) { 203 if (!str) { 204 return str; 205 } 206 return str.replace(/&/g, '&') 207 .replace(/</g, '<') 208 .replace(/>/g, '>'); 209 }; 210 211 util.argsToArray = function(args) { 212 var arrayOfArgs = []; 213 for (var i = 0; i < args.length; i++) { 214 arrayOfArgs.push(args[i]); 215 } 216 return arrayOfArgs; 217 }; 218 219 util.isUndefined = function(obj) { 220 return obj === void 0; 221 }; 222 223 return util; 224 }; 225 226 getJasmineRequireObj().Spec = function(j$) { 227 function Spec(attrs) { 228 this.expectationFactory = attrs.expectationFactory; 229 this.resultCallback = attrs.resultCallback || function() {}; 230 this.id = attrs.id; 231 this.description = attrs.description || ''; 232 this.fn = attrs.fn; 233 this.beforeFns = attrs.beforeFns || function() { return []; }; 234 this.afterFns = attrs.afterFns || function() { return []; }; 235 this.onStart = attrs.onStart || function() {}; 236 this.exceptionFormatter = attrs.exceptionFormatter || function() {}; 237 this.getSpecName = attrs.getSpecName || function() { return ''; }; 238 this.expectationResultFactory = attrs.expectationResultFactory || function() { }; 239 this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; 240 this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; 241 242 this.timer = attrs.timer || {setTimeout: setTimeout, clearTimeout: clearTimeout}; 243 244 if (!this.fn) { 245 this.pend(); 246 } 247 248 this.result = { 249 id: this.id, 250 description: this.description, 251 fullName: this.getFullName(), 252 failedExpectations: [] 253 }; 254 } 255 256 Spec.prototype.addExpectationResult = function(passed, data) { 257 if (passed) { 258 return; 259 } 260 this.result.failedExpectations.push(this.expectationResultFactory(data)); 261 }; 262 263 Spec.prototype.expect = function(actual) { 264 return this.expectationFactory(actual, this); 265 }; 266 267 Spec.prototype.execute = function(onComplete) { 268 var self = this, 269 timeout; 270 271 this.onStart(this); 272 273 if (this.markedPending || this.disabled) { 274 complete(); 275 return; 276 } 277 278 function timeoutable(fn) { 279 return function(done) { 280 timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { 281 onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.')); 282 done(); 283 }, j$.DEFAULT_TIMEOUT_INTERVAL]]); 284 285 var callDone = function() { 286 clearTimeoutable(); 287 done(); 288 }; 289 290 fn.call(this, callDone); //TODO: do we care about more than 1 arg? 291 }; 292 } 293 294 function clearTimeoutable() { 295 Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]); 296 timeout = void 0; 297 } 298 299 var allFns = this.beforeFns().concat(this.fn).concat(this.afterFns()), 300 allTimeoutableFns = []; 301 for (var i = 0; i < allFns.length; i++) { 302 var fn = allFns[i]; 303 allTimeoutableFns.push(fn.length > 0 ? timeoutable(fn) : fn); 304 } 305 306 this.queueRunnerFactory({ 307 fns: allTimeoutableFns, 308 onException: onException, 309 onComplete: complete 310 }); 311 312 function onException(e) { 313 clearTimeoutable(); 314 if (Spec.isPendingSpecException(e)) { 315 self.pend(); 316 return; 317 } 318 319 self.addExpectationResult(false, { 320 matcherName: "", 321 passed: false, 322 expected: "", 323 actual: "", 324 error: e 325 }); 326 } 327 328 function complete() { 329 self.result.status = self.status(); 330 self.resultCallback(self.result); 331 332 if (onComplete) { 333 onComplete(); 334 } 335 } 336 }; 337 338 Spec.prototype.disable = function() { 339 this.disabled = true; 340 }; 341 342 Spec.prototype.pend = function() { 343 this.markedPending = true; 344 }; 345 346 Spec.prototype.status = function() { 347 if (this.disabled) { 348 return 'disabled'; 349 } 350 351 if (this.markedPending) { 352 return 'pending'; 353 } 354 355 if (this.result.failedExpectations.length > 0) { 356 return 'failed'; 357 } else { 358 return 'passed'; 359 } 360 }; 361 362 Spec.prototype.getFullName = function() { 363 return this.getSpecName(this); 364 }; 365 366 Spec.pendingSpecExceptionMessage = "=> marked Pending"; 367 368 Spec.isPendingSpecException = function(e) { 369 return e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1; 370 }; 371 372 return Spec; 373 }; 374 375 if (typeof window == void 0 && typeof exports == "object") { 376 exports.Spec = jasmineRequire.Spec; 377 } 378 379 getJasmineRequireObj().Env = function(j$) { 380 function Env(options) { 381 options = options || {}; 382 383 var self = this; 384 var global = options.global || j$.getGlobal(); 385 386 var totalSpecsDefined = 0; 387 388 var catchExceptions = true; 389 390 var realSetTimeout = j$.getGlobal().setTimeout; 391 var realClearTimeout = j$.getGlobal().clearTimeout; 392 this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler()); 393 394 var runnableLookupTable = {}; 395 396 var spies = []; 397 398 var currentSpec = null; 399 var currentSuite = null; 400 401 var reporter = new j$.ReportDispatcher([ 402 "jasmineStarted", 403 "jasmineDone", 404 "suiteStarted", 405 "suiteDone", 406 "specStarted", 407 "specDone" 408 ]); 409 410 this.specFilter = function() { 411 return true; 412 }; 413 414 var equalityTesters = []; 415 416 var customEqualityTesters = []; 417 this.addCustomEqualityTester = function(tester) { 418 customEqualityTesters.push(tester); 419 }; 420 421 j$.Expectation.addCoreMatchers(j$.matchers); 422 423 var nextSpecId = 0; 424 var getNextSpecId = function() { 425 return 'spec' + nextSpecId++; 426 }; 427 428 var nextSuiteId = 0; 429 var getNextSuiteId = function() { 430 return 'suite' + nextSuiteId++; 431 }; 432 433 var expectationFactory = function(actual, spec) { 434 return j$.Expectation.Factory({ 435 util: j$.matchersUtil, 436 customEqualityTesters: customEqualityTesters, 437 actual: actual, 438 addExpectationResult: addExpectationResult 439 }); 440 441 function addExpectationResult(passed, result) { 442 return spec.addExpectationResult(passed, result); 443 } 444 }; 445 446 var specStarted = function(spec) { 447 currentSpec = spec; 448 reporter.specStarted(spec.result); 449 }; 450 451 var beforeFns = function(suite) { 452 return function() { 453 var befores = []; 454 while(suite) { 455 befores = befores.concat(suite.beforeFns); 456 suite = suite.parentSuite; 457 } 458 return befores.reverse(); 459 }; 460 }; 461 462 var afterFns = function(suite) { 463 return function() { 464 var afters = []; 465 while(suite) { 466 afters = afters.concat(suite.afterFns); 467 suite = suite.parentSuite; 468 } 469 return afters; 470 }; 471 }; 472 473 var getSpecName = function(spec, suite) { 474 return suite.getFullName() + ' ' + spec.description; 475 }; 476 477 // TODO: we may just be able to pass in the fn instead of wrapping here 478 var buildExpectationResult = j$.buildExpectationResult, 479 exceptionFormatter = new j$.ExceptionFormatter(), 480 expectationResultFactory = function(attrs) { 481 attrs.messageFormatter = exceptionFormatter.message; 482 attrs.stackFormatter = exceptionFormatter.stack; 483 484 return buildExpectationResult(attrs); 485 }; 486 487 // TODO: fix this naming, and here's where the value comes in 488 this.catchExceptions = function(value) { 489 catchExceptions = !!value; 490 return catchExceptions; 491 }; 492 493 this.catchingExceptions = function() { 494 return catchExceptions; 495 }; 496 497 var maximumSpecCallbackDepth = 20; 498 var currentSpecCallbackDepth = 0; 499 500 function clearStack(fn) { 501 currentSpecCallbackDepth++; 502 if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) { 503 currentSpecCallbackDepth = 0; 504 realSetTimeout(fn, 0); 505 } else { 506 fn(); 507 } 508 } 509 510 var catchException = function(e) { 511 return j$.Spec.isPendingSpecException(e) || catchExceptions; 512 }; 513 514 var queueRunnerFactory = function(options) { 515 options.catchException = catchException; 516 options.clearStack = options.clearStack || clearStack; 517 518 new j$.QueueRunner(options).execute(); 519 }; 520 521 var topSuite = new j$.Suite({ 522 env: this, 523 id: getNextSuiteId(), 524 description: 'Jasmine__TopLevel__Suite', 525 queueRunner: queueRunnerFactory, 526 resultCallback: function() {} // TODO - hook this up 527 }); 528 runnableLookupTable[topSuite.id] = topSuite; 529 currentSuite = topSuite; 530 531 this.topSuite = function() { 532 return topSuite; 533 }; 534 535 this.execute = function(runnablesToRun) { 536 runnablesToRun = runnablesToRun || [topSuite.id]; 537 538 var allFns = []; 539 for(var i = 0; i < runnablesToRun.length; i++) { 540 var runnable = runnableLookupTable[runnablesToRun[i]]; 541 allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable)); 542 } 543 544 reporter.jasmineStarted({ 545 totalSpecsDefined: totalSpecsDefined 546 }); 547 548 queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone}); 549 }; 550 551 this.addReporter = function(reporterToAdd) { 552 reporter.addReporter(reporterToAdd); 553 }; 554 555 this.addMatchers = function(matchersToAdd) { 556 j$.Expectation.addMatchers(matchersToAdd); 557 }; 558 559 this.spyOn = function(obj, methodName) { 560 if (j$.util.isUndefined(obj)) { 561 throw new Error("spyOn could not find an object to spy upon for " + methodName + "()"); 562 } 563 564 if (j$.util.isUndefined(obj[methodName])) { 565 throw new Error(methodName + '() method does not exist'); 566 } 567 568 if (obj[methodName] && j$.isSpy(obj[methodName])) { 569 //TODO?: should this return the current spy? Downside: may cause user confusion about spy state 570 throw new Error(methodName + ' has already been spied upon'); 571 } 572 573 var spy = j$.createSpy(methodName, obj[methodName]); 574 575 spies.push({ 576 spy: spy, 577 baseObj: obj, 578 methodName: methodName, 579 originalValue: obj[methodName] 580 }); 581 582 obj[methodName] = spy; 583 584 return spy; 585 }; 586 587 var suiteFactory = function(description) { 588 var suite = new j$.Suite({ 589 env: self, 590 id: getNextSuiteId(), 591 description: description, 592 parentSuite: currentSuite, 593 queueRunner: queueRunnerFactory, 594 onStart: suiteStarted, 595 resultCallback: function(attrs) { 596 reporter.suiteDone(attrs); 597 } 598 }); 599 600 runnableLookupTable[suite.id] = suite; 601 return suite; 602 }; 603 604 this.describe = function(description, specDefinitions) { 605 var suite = suiteFactory(description); 606 607 var parentSuite = currentSuite; 608 parentSuite.addChild(suite); 609 currentSuite = suite; 610 611 var declarationError = null; 612 try { 613 specDefinitions.call(suite); 614 } catch (e) { 615 declarationError = e; 616 } 617 618 if (declarationError) { 619 this.it("encountered a declaration exception", function() { 620 throw declarationError; 621 }); 622 } 623 624 currentSuite = parentSuite; 625 626 return suite; 627 }; 628 629 this.xdescribe = function(description, specDefinitions) { 630 var suite = this.describe(description, specDefinitions); 631 suite.disable(); 632 return suite; 633 }; 634 635 var specFactory = function(description, fn, suite) { 636 totalSpecsDefined++; 637 638 var spec = new j$.Spec({ 639 id: getNextSpecId(), 640 beforeFns: beforeFns(suite), 641 afterFns: afterFns(suite), 642 expectationFactory: expectationFactory, 643 exceptionFormatter: exceptionFormatter, 644 resultCallback: specResultCallback, 645 getSpecName: function(spec) { 646 return getSpecName(spec, suite); 647 }, 648 onStart: specStarted, 649 description: description, 650 expectationResultFactory: expectationResultFactory, 651 queueRunnerFactory: queueRunnerFactory, 652 fn: fn, 653 timer: {setTimeout: realSetTimeout, clearTimeout: realClearTimeout} 654 }); 655 656 runnableLookupTable[spec.id] = spec; 657 658 if (!self.specFilter(spec)) { 659 spec.disable(); 660 } 661 662 return spec; 663 664 function removeAllSpies() { 665 for (var i = 0; i < spies.length; i++) { 666 var spyEntry = spies[i]; 667 spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; 668 } 669 spies = []; 670 } 671 672 function specResultCallback(result) { 673 removeAllSpies(); 674 j$.Expectation.resetMatchers(); 675 customEqualityTesters = []; 676 currentSpec = null; 677 reporter.specDone(result); 678 } 679 }; 680 681 var suiteStarted = function(suite) { 682 reporter.suiteStarted(suite.result); 683 }; 684 685 this.it = function(description, fn) { 686 var spec = specFactory(description, fn, currentSuite); 687 currentSuite.addChild(spec); 688 return spec; 689 }; 690 691 this.xit = function(description, fn) { 692 var spec = this.it(description, fn); 693 spec.pend(); 694 return spec; 695 }; 696 697 this.expect = function(actual) { 698 return currentSpec.expect(actual); 699 }; 700 701 this.beforeEach = function(beforeEachFunction) { 702 currentSuite.beforeEach(beforeEachFunction); 703 }; 704 705 this.afterEach = function(afterEachFunction) { 706 currentSuite.afterEach(afterEachFunction); 707 }; 708 709 this.pending = function() { 710 throw j$.Spec.pendingSpecExceptionMessage; 711 }; 712 } 713 714 return Env; 715 }; 716 717 getJasmineRequireObj().JsApiReporter = function() { 718 719 var noopTimer = { 720 start: function(){}, 721 elapsed: function(){ return 0; } 722 }; 723 724 function JsApiReporter(options) { 725 var timer = options.timer || noopTimer, 726 status = "loaded"; 727 728 this.started = false; 729 this.finished = false; 730 731 this.jasmineStarted = function() { 732 this.started = true; 733 status = 'started'; 734 timer.start(); 735 }; 736 737 var executionTime; 738 739 this.jasmineDone = function() { 740 this.finished = true; 741 executionTime = timer.elapsed(); 742 status = 'done'; 743 }; 744 745 this.status = function() { 746 return status; 747 }; 748 749 var suites = {}; 750 751 this.suiteStarted = function(result) { 752 storeSuite(result); 753 }; 754 755 this.suiteDone = function(result) { 756 storeSuite(result); 757 }; 758 759 function storeSuite(result) { 760 suites[result.id] = result; 761 } 762 763 this.suites = function() { 764 return suites; 765 }; 766 767 var specs = []; 768 this.specStarted = function(result) { }; 769 770 this.specDone = function(result) { 771 specs.push(result); 772 }; 773 774 this.specResults = function(index, length) { 775 return specs.slice(index, index + length); 776 }; 777 778 this.specs = function() { 779 return specs; 780 }; 781 782 this.executionTime = function() { 783 return executionTime; 784 }; 785 786 } 787 788 return JsApiReporter; 789 }; 790 791 getJasmineRequireObj().Any = function() { 792 793 function Any(expectedObject) { 794 this.expectedObject = expectedObject; 795 } 796 797 Any.prototype.jasmineMatches = function(other) { 798 if (this.expectedObject == String) { 799 return typeof other == 'string' || other instanceof String; 800 } 801 802 if (this.expectedObject == Number) { 803 return typeof other == 'number' || other instanceof Number; 804 } 805 806 if (this.expectedObject == Function) { 807 return typeof other == 'function' || other instanceof Function; 808 } 809 810 if (this.expectedObject == Object) { 811 return typeof other == 'object'; 812 } 813 814 if (this.expectedObject == Boolean) { 815 return typeof other == 'boolean'; 816 } 817 818 return other instanceof this.expectedObject; 819 }; 820 821 Any.prototype.jasmineToString = function() { 822 return '<jasmine.any(' + this.expectedClass + ')>'; 823 }; 824 825 return Any; 826 }; 827 828 getJasmineRequireObj().CallTracker = function() { 829 830 function CallTracker() { 831 var calls = []; 832 833 this.track = function(context) { 834 calls.push(context); 835 }; 836 837 this.any = function() { 838 return !!calls.length; 839 }; 840 841 this.count = function() { 842 return calls.length; 843 }; 844 845 this.argsFor = function(index) { 846 var call = calls[index]; 847 return call ? call.args : []; 848 }; 849 850 this.all = function() { 851 return calls; 852 }; 853 854 this.allArgs = function() { 855 var callArgs = []; 856 for(var i = 0; i < calls.length; i++){ 857 callArgs.push(calls[i].args); 858 } 859 860 return callArgs; 861 }; 862 863 this.first = function() { 864 return calls[0]; 865 }; 866 867 this.mostRecent = function() { 868 return calls[calls.length - 1]; 869 }; 870 871 this.reset = function() { 872 calls = []; 873 }; 874 } 875 876 return CallTracker; 877 }; 878 879 getJasmineRequireObj().Clock = function() { 880 function Clock(global, delayedFunctionScheduler) { 881 var self = this, 882 realTimingFunctions = { 883 setTimeout: global.setTimeout, 884 clearTimeout: global.clearTimeout, 885 setInterval: global.setInterval, 886 clearInterval: global.clearInterval 887 }, 888 fakeTimingFunctions = { 889 setTimeout: setTimeout, 890 clearTimeout: clearTimeout, 891 setInterval: setInterval, 892 clearInterval: clearInterval 893 }, 894 installed = false, 895 timer; 896 897 self.install = function() { 898 replace(global, fakeTimingFunctions); 899 timer = fakeTimingFunctions; 900 installed = true; 901 }; 902 903 self.uninstall = function() { 904 delayedFunctionScheduler.reset(); 905 replace(global, realTimingFunctions); 906 timer = realTimingFunctions; 907 installed = false; 908 }; 909 910 self.setTimeout = function(fn, delay, params) { 911 if (legacyIE()) { 912 if (arguments.length > 2) { 913 throw new Error("IE < 9 cannot support extra params to setTimeout without a polyfill"); 914 } 915 return timer.setTimeout(fn, delay); 916 } 917 return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]); 918 }; 919 920 self.setInterval = function(fn, delay, params) { 921 if (legacyIE()) { 922 if (arguments.length > 2) { 923 throw new Error("IE < 9 cannot support extra params to setInterval without a polyfill"); 924 } 925 return timer.setInterval(fn, delay); 926 } 927 return Function.prototype.apply.apply(timer.setInterval, [global, arguments]); 928 }; 929 930 self.clearTimeout = function(id) { 931 return Function.prototype.call.apply(timer.clearTimeout, [global, id]); 932 }; 933 934 self.clearInterval = function(id) { 935 return Function.prototype.call.apply(timer.clearInterval, [global, id]); 936 }; 937 938 self.tick = function(millis) { 939 if (installed) { 940 delayedFunctionScheduler.tick(millis); 941 } else { 942 throw new Error("Mock clock is not installed, use jasmine.clock().install()"); 943 } 944 }; 945 946 return self; 947 948 function legacyIE() { 949 //if these methods are polyfilled, apply will be present 950 return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply; 951 } 952 953 function replace(dest, source) { 954 for (var prop in source) { 955 dest[prop] = source[prop]; 956 } 957 } 958 959 function setTimeout(fn, delay) { 960 return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); 961 } 962 963 function clearTimeout(id) { 964 return delayedFunctionScheduler.removeFunctionWithId(id); 965 } 966 967 function setInterval(fn, interval) { 968 return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); 969 } 970 971 function clearInterval(id) { 972 return delayedFunctionScheduler.removeFunctionWithId(id); 973 } 974 975 function argSlice(argsObj, n) { 976 return Array.prototype.slice.call(argsObj, 2); 977 } 978 } 979 980 return Clock; 981 }; 982 983 getJasmineRequireObj().DelayedFunctionScheduler = function() { 984 function DelayedFunctionScheduler() { 985 var self = this; 986 var scheduledLookup = []; 987 var scheduledFunctions = {}; 988 var currentTime = 0; 989 var delayedFnCount = 0; 990 991 self.tick = function(millis) { 992 millis = millis || 0; 993 var endTime = currentTime + millis; 994 995 runScheduledFunctions(endTime); 996 currentTime = endTime; 997 }; 998 999 self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) { 1000 var f; 1001 if (typeof(funcToCall) === 'string') { 1002 /* jshint evil: true */ 1003 f = function() { return eval(funcToCall); }; 1004 /* jshint evil: false */ 1005 } else { 1006 f = funcToCall; 1007 } 1008 1009 millis = millis || 0; 1010 timeoutKey = timeoutKey || ++delayedFnCount; 1011 runAtMillis = runAtMillis || (currentTime + millis); 1012 1013 var funcToSchedule = { 1014 runAtMillis: runAtMillis, 1015 funcToCall: f, 1016 recurring: recurring, 1017 params: params, 1018 timeoutKey: timeoutKey, 1019 millis: millis 1020 }; 1021 1022 if (runAtMillis in scheduledFunctions) { 1023 scheduledFunctions[runAtMillis].push(funcToSchedule); 1024 } else { 1025 scheduledFunctions[runAtMillis] = [funcToSchedule]; 1026 scheduledLookup.push(runAtMillis); 1027 scheduledLookup.sort(function (a, b) { 1028 return a - b; 1029 }); 1030 } 1031 1032 return timeoutKey; 1033 }; 1034 1035 self.removeFunctionWithId = function(timeoutKey) { 1036 for (var runAtMillis in scheduledFunctions) { 1037 var funcs = scheduledFunctions[runAtMillis]; 1038 var i = indexOfFirstToPass(funcs, function (func) { 1039 return func.timeoutKey === timeoutKey; 1040 }); 1041 1042 if (i > -1) { 1043 if (funcs.length === 1) { 1044 delete scheduledFunctions[runAtMillis]; 1045 deleteFromLookup(runAtMillis); 1046 } else { 1047 funcs.splice(i, 1); 1048 } 1049 1050 // intervals get rescheduled when executed, so there's never more 1051 // than a single scheduled function with a given timeoutKey 1052 break; 1053 } 1054 } 1055 }; 1056 1057 self.reset = function() { 1058 currentTime = 0; 1059 scheduledLookup = []; 1060 scheduledFunctions = {}; 1061 delayedFnCount = 0; 1062 }; 1063 1064 return self; 1065 1066 function indexOfFirstToPass(array, testFn) { 1067 var index = -1; 1068 1069 for (var i = 0; i < array.length; ++i) { 1070 if (testFn(array[i])) { 1071 index = i; 1072 break; 1073 } 1074 } 1075 1076 return index; 1077 } 1078 1079 function deleteFromLookup(key) { 1080 var value = Number(key); 1081 var i = indexOfFirstToPass(scheduledLookup, function (millis) { 1082 return millis === value; 1083 }); 1084 1085 if (i > -1) { 1086 scheduledLookup.splice(i, 1); 1087 } 1088 } 1089 1090 function reschedule(scheduledFn) { 1091 self.scheduleFunction(scheduledFn.funcToCall, 1092 scheduledFn.millis, 1093 scheduledFn.params, 1094 true, 1095 scheduledFn.timeoutKey, 1096 scheduledFn.runAtMillis + scheduledFn.millis); 1097 } 1098 1099 function runScheduledFunctions(endTime) { 1100 if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { 1101 return; 1102 } 1103 1104 do { 1105 currentTime = scheduledLookup.shift(); 1106 1107 var funcsToRun = scheduledFunctions[currentTime]; 1108 delete scheduledFunctions[currentTime]; 1109 1110 for (var i = 0; i < funcsToRun.length; ++i) { 1111 var funcToRun = funcsToRun[i]; 1112 funcToRun.funcToCall.apply(null, funcToRun.params || []); 1113 1114 if (funcToRun.recurring) { 1115 reschedule(funcToRun); 1116 } 1117 } 1118 } while (scheduledLookup.length > 0 && 1119 // checking first if we're out of time prevents setTimeout(0) 1120 // scheduled in a funcToRun from forcing an extra iteration 1121 currentTime !== endTime && 1122 scheduledLookup[0] <= endTime); 1123 } 1124 } 1125 1126 return DelayedFunctionScheduler; 1127 }; 1128 1129 getJasmineRequireObj().ExceptionFormatter = function() { 1130 function ExceptionFormatter() { 1131 this.message = function(error) { 1132 var message = error.name + 1133 ': ' + 1134 error.message; 1135 1136 if (error.fileName || error.sourceURL) { 1137 message += " in " + (error.fileName || error.sourceURL); 1138 } 1139 1140 if (error.line || error.lineNumber) { 1141 message += " (line " + (error.line || error.lineNumber) + ")"; 1142 } 1143 1144 return message; 1145 }; 1146 1147 this.stack = function(error) { 1148 return error ? error.stack : null; 1149 }; 1150 } 1151 1152 return ExceptionFormatter; 1153 }; 1154 1155 getJasmineRequireObj().Expectation = function() { 1156 1157 var matchers = {}; 1158 1159 function Expectation(options) { 1160 this.util = options.util || { buildFailureMessage: function() {} }; 1161 this.customEqualityTesters = options.customEqualityTesters || []; 1162 this.actual = options.actual; 1163 this.addExpectationResult = options.addExpectationResult || function(){}; 1164 this.isNot = options.isNot; 1165 1166 for (var matcherName in matchers) { 1167 this[matcherName] = matchers[matcherName]; 1168 } 1169 } 1170 1171 Expectation.prototype.wrapCompare = function(name, matcherFactory) { 1172 return function() { 1173 var args = Array.prototype.slice.call(arguments, 0), 1174 expected = args.slice(0), 1175 message = ""; 1176 1177 args.unshift(this.actual); 1178 1179 var matcher = matcherFactory(this.util, this.customEqualityTesters), 1180 matcherCompare = matcher.compare; 1181 1182 function defaultNegativeCompare() { 1183 var result = matcher.compare.apply(null, args); 1184 result.pass = !result.pass; 1185 return result; 1186 } 1187 1188 if (this.isNot) { 1189 matcherCompare = matcher.negativeCompare || defaultNegativeCompare; 1190 } 1191 1192 var result = matcherCompare.apply(null, args); 1193 1194 if (!result.pass) { 1195 if (!result.message) { 1196 args.unshift(this.isNot); 1197 args.unshift(name); 1198 message = this.util.buildFailureMessage.apply(null, args); 1199 } else { 1200 message = result.message; 1201 } 1202 } 1203 1204 if (expected.length == 1) { 1205 expected = expected[0]; 1206 } 1207 1208 // TODO: how many of these params are needed? 1209 this.addExpectationResult( 1210 result.pass, 1211 { 1212 matcherName: name, 1213 passed: result.pass, 1214 message: message, 1215 actual: this.actual, 1216 expected: expected // TODO: this may need to be arrayified/sliced 1217 } 1218 ); 1219 }; 1220 }; 1221 1222 Expectation.addCoreMatchers = function(matchers) { 1223 var prototype = Expectation.prototype; 1224 for (var matcherName in matchers) { 1225 var matcher = matchers[matcherName]; 1226 prototype[matcherName] = prototype.wrapCompare(matcherName, matcher); 1227 } 1228 }; 1229 1230 Expectation.addMatchers = function(matchersToAdd) { 1231 for (var name in matchersToAdd) { 1232 var matcher = matchersToAdd[name]; 1233 matchers[name] = Expectation.prototype.wrapCompare(name, matcher); 1234 } 1235 }; 1236 1237 Expectation.resetMatchers = function() { 1238 for (var name in matchers) { 1239 delete matchers[name]; 1240 } 1241 }; 1242 1243 Expectation.Factory = function(options) { 1244 options = options || {}; 1245 1246 var expect = new Expectation(options); 1247 1248 // TODO: this would be nice as its own Object - NegativeExpectation 1249 // TODO: copy instead of mutate options 1250 options.isNot = true; 1251 expect.not = new Expectation(options); 1252 1253 return expect; 1254 }; 1255 1256 return Expectation; 1257 }; 1258 1259 //TODO: expectation result may make more sense as a presentation of an expectation. 1260 getJasmineRequireObj().buildExpectationResult = function() { 1261 function buildExpectationResult(options) { 1262 var messageFormatter = options.messageFormatter || function() {}, 1263 stackFormatter = options.stackFormatter || function() {}; 1264 1265 return { 1266 matcherName: options.matcherName, 1267 expected: options.expected, 1268 actual: options.actual, 1269 message: message(), 1270 stack: stack(), 1271 passed: options.passed 1272 }; 1273 1274 function message() { 1275 if (options.passed) { 1276 return "Passed."; 1277 } else if (options.message) { 1278 return options.message; 1279 } else if (options.error) { 1280 return messageFormatter(options.error); 1281 } 1282 return ""; 1283 } 1284 1285 function stack() { 1286 if (options.passed) { 1287 return ""; 1288 } 1289 1290 var error = options.error; 1291 if (!error) { 1292 try { 1293 throw new Error(message()); 1294 } catch (e) { 1295 error = e; 1296 } 1297 } 1298 return stackFormatter(error); 1299 } 1300 } 1301 1302 return buildExpectationResult; 1303 }; 1304 1305 getJasmineRequireObj().ObjectContaining = function(j$) { 1306 1307 function ObjectContaining(sample) { 1308 this.sample = sample; 1309 } 1310 1311 ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { 1312 if (typeof(this.sample) !== "object") { throw new Error("You must provide an object to objectContaining, not '"+this.sample+"'."); } 1313 1314 mismatchKeys = mismatchKeys || []; 1315 mismatchValues = mismatchValues || []; 1316 1317 var hasKey = function(obj, keyName) { 1318 return obj !== null && !j$.util.isUndefined(obj[keyName]); 1319 }; 1320 1321 for (var property in this.sample) { 1322 if (!hasKey(other, property) && hasKey(this.sample, property)) { 1323 mismatchKeys.push("expected has key '" + property + "', but missing from actual."); 1324 } 1325 else if (!j$.matchersUtil.equals(this.sample[property], other[property])) { 1326 mismatchValues.push("'" + property + "' was '" + (other[property] ? j$.util.htmlEscape(other[property].toString()) : other[property]) + "' in actual, but was '" + (this.sample[property] ? j$.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in expected."); 1327 } 1328 } 1329 1330 return (mismatchKeys.length === 0 && mismatchValues.length === 0); 1331 }; 1332 1333 ObjectContaining.prototype.jasmineToString = function() { 1334 return "<jasmine.objectContaining(" + j$.pp(this.sample) + ")>"; 1335 }; 1336 1337 return ObjectContaining; 1338 }; 1339 1340 getJasmineRequireObj().pp = function(j$) { 1341 1342 function PrettyPrinter() { 1343 this.ppNestLevel_ = 0; 1344 } 1345 1346 PrettyPrinter.prototype.format = function(value) { 1347 this.ppNestLevel_++; 1348 try { 1349 if (j$.util.isUndefined(value)) { 1350 this.emitScalar('undefined'); 1351 } else if (value === null) { 1352 this.emitScalar('null'); 1353 } else if (value === j$.getGlobal()) { 1354 this.emitScalar('<global>'); 1355 } else if (value.jasmineToString) { 1356 this.emitScalar(value.jasmineToString()); 1357 } else if (typeof value === 'string') { 1358 this.emitString(value); 1359 } else if (j$.isSpy(value)) { 1360 this.emitScalar("spy on " + value.and.identity()); 1361 } else if (value instanceof RegExp) { 1362 this.emitScalar(value.toString()); 1363 } else if (typeof value === 'function') { 1364 this.emitScalar('Function'); 1365 } else if (typeof value.nodeType === 'number') { 1366 this.emitScalar('HTMLNode'); 1367 } else if (value instanceof Date) { 1368 this.emitScalar('Date(' + value + ')'); 1369 } else if (value.__Jasmine_been_here_before__) { 1370 this.emitScalar('<circular reference: ' + (j$.isArray_(value) ? 'Array' : 'Object') + '>'); 1371 } else if (j$.isArray_(value) || j$.isA_('Object', value)) { 1372 value.__Jasmine_been_here_before__ = true; 1373 if (j$.isArray_(value)) { 1374 this.emitArray(value); 1375 } else { 1376 this.emitObject(value); 1377 } 1378 delete value.__Jasmine_been_here_before__; 1379 } else { 1380 this.emitScalar(value.toString()); 1381 } 1382 } finally { 1383 this.ppNestLevel_--; 1384 } 1385 }; 1386 1387 PrettyPrinter.prototype.iterateObject = function(obj, fn) { 1388 for (var property in obj) { 1389 if (!obj.hasOwnProperty(property)) { continue; } 1390 if (property == '__Jasmine_been_here_before__') { continue; } 1391 fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) && 1392 obj.__lookupGetter__(property) !== null) : false); 1393 } 1394 }; 1395 1396 PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; 1397 PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; 1398 PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; 1399 PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; 1400 1401 function StringPrettyPrinter() { 1402 PrettyPrinter.call(this); 1403 1404 this.string = ''; 1405 } 1406 1407 j$.util.inherit(StringPrettyPrinter, PrettyPrinter); 1408 1409 StringPrettyPrinter.prototype.emitScalar = function(value) { 1410 this.append(value); 1411 }; 1412 1413 StringPrettyPrinter.prototype.emitString = function(value) { 1414 this.append("'" + value + "'"); 1415 }; 1416 1417 StringPrettyPrinter.prototype.emitArray = function(array) { 1418 if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { 1419 this.append("Array"); 1420 return; 1421 } 1422 1423 this.append('[ '); 1424 for (var i = 0; i < array.length; i++) { 1425 if (i > 0) { 1426 this.append(', '); 1427 } 1428 this.format(array[i]); 1429 } 1430 this.append(' ]'); 1431 }; 1432 1433 StringPrettyPrinter.prototype.emitObject = function(obj) { 1434 if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { 1435 this.append("Object"); 1436 return; 1437 } 1438 1439 var self = this; 1440 this.append('{ '); 1441 var first = true; 1442 1443 this.iterateObject(obj, function(property, isGetter) { 1444 if (first) { 1445 first = false; 1446 } else { 1447 self.append(', '); 1448 } 1449 1450 self.append(property); 1451 self.append(' : '); 1452 if (isGetter) { 1453 self.append('<getter>'); 1454 } else { 1455 self.format(obj[property]); 1456 } 1457 }); 1458 1459 this.append(' }'); 1460 }; 1461 1462 StringPrettyPrinter.prototype.append = function(value) { 1463 this.string += value; 1464 }; 1465 1466 return function(value) { 1467 var stringPrettyPrinter = new StringPrettyPrinter(); 1468 stringPrettyPrinter.format(value); 1469 return stringPrettyPrinter.string; 1470 }; 1471 }; 1472 1473 getJasmineRequireObj().QueueRunner = function() { 1474 1475 function QueueRunner(attrs) { 1476 this.fns = attrs.fns || []; 1477 this.onComplete = attrs.onComplete || function() {}; 1478 this.clearStack = attrs.clearStack || function(fn) {fn();}; 1479 this.onException = attrs.onException || function() {}; 1480 this.catchException = attrs.catchException || function() { return true; }; 1481 this.userContext = {}; 1482 } 1483 1484 QueueRunner.prototype.execute = function() { 1485 this.run(this.fns, 0); 1486 }; 1487 1488 QueueRunner.prototype.run = function(fns, recursiveIndex) { 1489 var length = fns.length, 1490 self = this, 1491 iterativeIndex; 1492 1493 for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) { 1494 var fn = fns[iterativeIndex]; 1495 if (fn.length > 0) { 1496 return attemptAsync(fn); 1497 } else { 1498 attemptSync(fn); 1499 } 1500 } 1501 1502 var runnerDone = iterativeIndex >= length; 1503 1504 if (runnerDone) { 1505 this.clearStack(this.onComplete); 1506 } 1507 1508 function attemptSync(fn) { 1509 try { 1510 fn.call(self.userContext); 1511 } catch (e) { 1512 handleException(e); 1513 } 1514 } 1515 1516 function attemptAsync(fn) { 1517 var next = function () { self.run(fns, iterativeIndex + 1); }; 1518 1519 try { 1520 fn.call(self.userContext, next); 1521 } catch (e) { 1522 handleException(e); 1523 next(); 1524 } 1525 } 1526 1527 function handleException(e) { 1528 self.onException(e); 1529 if (!self.catchException(e)) { 1530 //TODO: set a var when we catch an exception and 1531 //use a finally block to close the loop in a nice way.. 1532 throw e; 1533 } 1534 } 1535 }; 1536 1537 return QueueRunner; 1538 }; 1539 1540 getJasmineRequireObj().ReportDispatcher = function() { 1541 function ReportDispatcher(methods) { 1542 1543 var dispatchedMethods = methods || []; 1544 1545 for (var i = 0; i < dispatchedMethods.length; i++) { 1546 var method = dispatchedMethods[i]; 1547 this[method] = (function(m) { 1548 return function() { 1549 dispatch(m, arguments); 1550 }; 1551 }(method)); 1552 } 1553 1554 var reporters = []; 1555 1556 this.addReporter = function(reporter) { 1557 reporters.push(reporter); 1558 }; 1559 56 1560 return this; 57 } 58 59 return getGlobal(); 60 }; 61 62 /** 63 * Allows for bound functions to be compared. Internal use only. 64 * 65 * @ignore 66 * @private 67 * @param base {Object} bound 'this' for the function 68 * @param name {Function} function to find 69 */ 70 jasmine.bindOriginal_ = function(base, name) { 71 var original = base[name]; 72 if (original.apply) { 73 return function() { 74 return original.apply(base, arguments); 75 }; 76 } else { 77 // IE support 78 return jasmine.getGlobal()[name]; 79 } 80 }; 81 82 jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); 83 jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); 84 jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); 85 jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); 86 87 jasmine.MessageResult = function(values) { 88 this.type = 'log'; 89 this.values = values; 90 this.trace = new Error(); // todo: test better 91 }; 92 93 jasmine.MessageResult.prototype.toString = function() { 94 var text = ""; 95 for (var i = 0; i < this.values.length; i++) { 96 if (i > 0) text += " "; 97 if (jasmine.isString_(this.values[i])) { 98 text += this.values[i]; 1561 1562 function dispatch(method, args) { 1563 for (var i = 0; i < reporters.length; i++) { 1564 var reporter = reporters[i]; 1565 if (reporter[method]) { 1566 reporter[method].apply(reporter, args); 1567 } 1568 } 1569 } 1570 } 1571 1572 return ReportDispatcher; 1573 }; 1574 1575 1576 getJasmineRequireObj().SpyStrategy = function() { 1577 1578 function SpyStrategy(options) { 1579 options = options || {}; 1580 1581 var identity = options.name || "unknown", 1582 originalFn = options.fn || function() {}, 1583 getSpy = options.getSpy || function() {}, 1584 plan = function() {}; 1585 1586 this.identity = function() { 1587 return identity; 1588 }; 1589 1590 this.exec = function() { 1591 return plan.apply(this, arguments); 1592 }; 1593 1594 this.callThrough = function() { 1595 plan = originalFn; 1596 return getSpy(); 1597 }; 1598 1599 this.returnValue = function(value) { 1600 plan = function() { 1601 return value; 1602 }; 1603 return getSpy(); 1604 }; 1605 1606 this.throwError = function(something) { 1607 var error = (something instanceof Error) ? something : new Error(something); 1608 plan = function() { 1609 throw error; 1610 }; 1611 return getSpy(); 1612 }; 1613 1614 this.callFake = function(fn) { 1615 plan = fn; 1616 return getSpy(); 1617 }; 1618 1619 this.stub = function(fn) { 1620 plan = function() {}; 1621 return getSpy(); 1622 }; 1623 } 1624 1625 return SpyStrategy; 1626 }; 1627 1628 getJasmineRequireObj().Suite = function() { 1629 function Suite(attrs) { 1630 this.env = attrs.env; 1631 this.id = attrs.id; 1632 this.parentSuite = attrs.parentSuite; 1633 this.description = attrs.description; 1634 this.onStart = attrs.onStart || function() {}; 1635 this.resultCallback = attrs.resultCallback || function() {}; 1636 this.clearStack = attrs.clearStack || function(fn) {fn();}; 1637 1638 this.beforeFns = []; 1639 this.afterFns = []; 1640 this.queueRunner = attrs.queueRunner || function() {}; 1641 this.disabled = false; 1642 1643 this.children = []; 1644 1645 this.result = { 1646 id: this.id, 1647 status: this.disabled ? 'disabled' : '', 1648 description: this.description, 1649 fullName: this.getFullName() 1650 }; 1651 } 1652 1653 Suite.prototype.getFullName = function() { 1654 var fullName = this.description; 1655 for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { 1656 if (parentSuite.parentSuite) { 1657 fullName = parentSuite.description + ' ' + fullName; 1658 } 1659 } 1660 return fullName; 1661 }; 1662 1663 Suite.prototype.disable = function() { 1664 this.disabled = true; 1665 }; 1666 1667 Suite.prototype.beforeEach = function(fn) { 1668 this.beforeFns.unshift(fn); 1669 }; 1670 1671 Suite.prototype.afterEach = function(fn) { 1672 this.afterFns.unshift(fn); 1673 }; 1674 1675 Suite.prototype.addChild = function(child) { 1676 this.children.push(child); 1677 }; 1678 1679 Suite.prototype.execute = function(onComplete) { 1680 var self = this; 1681 if (this.disabled) { 1682 complete(); 1683 return; 1684 } 1685 1686 var allFns = []; 1687 1688 for (var i = 0; i < this.children.length; i++) { 1689 allFns.push(wrapChildAsAsync(this.children[i])); 1690 } 1691 1692 this.onStart(this); 1693 1694 this.queueRunner({ 1695 fns: allFns, 1696 onComplete: complete 1697 }); 1698 1699 function complete() { 1700 self.resultCallback(self.result); 1701 1702 if (onComplete) { 1703 onComplete(); 1704 } 1705 } 1706 1707 function wrapChildAsAsync(child) { 1708 return function(done) { child.execute(done); }; 1709 } 1710 }; 1711 1712 return Suite; 1713 }; 1714 1715 if (typeof window == void 0 && typeof exports == "object") { 1716 exports.Suite = jasmineRequire.Suite; 1717 } 1718 1719 getJasmineRequireObj().Timer = function() { 1720 function Timer(options) { 1721 options = options || {}; 1722 1723 var now = options.now || function() { return new Date().getTime(); }, 1724 startTime; 1725 1726 this.start = function() { 1727 startTime = now(); 1728 }; 1729 1730 this.elapsed = function() { 1731 return now() - startTime; 1732 }; 1733 } 1734 1735 return Timer; 1736 }; 1737 1738 getJasmineRequireObj().matchersUtil = function(j$) { 1739 // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? 1740 1741 return { 1742 equals: function(a, b, customTesters) { 1743 customTesters = customTesters || []; 1744 1745 return eq(a, b, [], [], customTesters); 1746 }, 1747 1748 contains: function(haystack, needle, customTesters) { 1749 customTesters = customTesters || []; 1750 1751 if (Object.prototype.toString.apply(haystack) === "[object Array]") { 1752 for (var i = 0; i < haystack.length; i++) { 1753 if (eq(haystack[i], needle, [], [], customTesters)) { 1754 return true; 1755 } 1756 } 1757 return false; 1758 } 1759 return haystack.indexOf(needle) >= 0; 1760 }, 1761 1762 buildFailureMessage: function() { 1763 var args = Array.prototype.slice.call(arguments, 0), 1764 matcherName = args[0], 1765 isNot = args[1], 1766 actual = args[2], 1767 expected = args.slice(3), 1768 englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); 1769 1770 var message = "Expected " + 1771 j$.pp(actual) + 1772 (isNot ? " not " : " ") + 1773 englishyPredicate; 1774 1775 if (expected.length > 0) { 1776 for (var i = 0; i < expected.length; i++) { 1777 if (i > 0) { 1778 message += ","; 1779 } 1780 message += " " + j$.pp(expected[i]); 1781 } 1782 } 1783 1784 return message + "."; 1785 } 1786 }; 1787 1788 // Equality function lovingly adapted from isEqual in 1789 // [Underscore](http://underscorejs.org) 1790 function eq(a, b, aStack, bStack, customTesters) { 1791 var result = true; 1792 1793 for (var i = 0; i < customTesters.length; i++) { 1794 var customTesterResult = customTesters[i](a, b); 1795 if (!j$.util.isUndefined(customTesterResult)) { 1796 return customTesterResult; 1797 } 1798 } 1799 1800 if (a instanceof j$.Any) { 1801 result = a.jasmineMatches(b); 1802 if (result) { 1803 return true; 1804 } 1805 } 1806 1807 if (b instanceof j$.Any) { 1808 result = b.jasmineMatches(a); 1809 if (result) { 1810 return true; 1811 } 1812 } 1813 1814 if (b instanceof j$.ObjectContaining) { 1815 result = b.jasmineMatches(a); 1816 if (result) { 1817 return true; 1818 } 1819 } 1820 1821 if (a instanceof Error && b instanceof Error) { 1822 return a.message == b.message; 1823 } 1824 1825 // Identical objects are equal. `0 === -0`, but they aren't identical. 1826 // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). 1827 if (a === b) { return a !== 0 || 1 / a == 1 / b; } 1828 // A strict comparison is necessary because `null == undefined`. 1829 if (a === null || b === null) { return a === b; } 1830 var className = Object.prototype.toString.call(a); 1831 if (className != Object.prototype.toString.call(b)) { return false; } 1832 switch (className) { 1833 // Strings, numbers, dates, and booleans are compared by value. 1834 case '[object String]': 1835 // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 1836 // equivalent to `new String("5")`. 1837 return a == String(b); 1838 case '[object Number]': 1839 // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for 1840 // other numeric values. 1841 return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); 1842 case '[object Date]': 1843 case '[object Boolean]': 1844 // Coerce dates and booleans to numeric primitive values. Dates are compared by their 1845 // millisecond representations. Note that invalid dates with millisecond representations 1846 // of `NaN` are not equivalent. 1847 return +a == +b; 1848 // RegExps are compared by their source patterns and flags. 1849 case '[object RegExp]': 1850 return a.source == b.source && 1851 a.global == b.global && 1852 a.multiline == b.multiline && 1853 a.ignoreCase == b.ignoreCase; 1854 } 1855 if (typeof a != 'object' || typeof b != 'object') { return false; } 1856 // Assume equality for cyclic structures. The algorithm for detecting cyclic 1857 // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 1858 var length = aStack.length; 1859 while (length--) { 1860 // Linear search. Performance is inversely proportional to the number of 1861 // unique nested structures. 1862 if (aStack[length] == a) { return bStack[length] == b; } 1863 } 1864 // Add the first object to the stack of traversed objects. 1865 aStack.push(a); 1866 bStack.push(b); 1867 var size = 0; 1868 // Recursively compare objects and arrays. 1869 if (className == '[object Array]') { 1870 // Compare array lengths to determine if a deep comparison is necessary. 1871 size = a.length; 1872 result = size == b.length; 1873 if (result) { 1874 // Deep compare the contents, ignoring non-numeric properties. 1875 while (size--) { 1876 if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; } 1877 } 1878 } 99 1879 } else { 100 text += jasmine.pp(this.values[i]); 101 } 102 } 103 return text; 104 }; 105 106 jasmine.ExpectationResult = function(params) { 107 this.type = 'expect'; 108 this.matcherName = params.matcherName; 109 this.passed_ = params.passed; 110 this.expected = params.expected; 111 this.actual = params.actual; 112 this.message = this.passed_ ? 'Passed.' : params.message; 113 114 var trace = (params.trace || new Error(this.message)); 115 this.trace = this.passed_ ? '' : trace; 116 }; 117 118 jasmine.ExpectationResult.prototype.toString = function () { 119 return this.message; 120 }; 121 122 jasmine.ExpectationResult.prototype.passed = function () { 123 return this.passed_; 124 }; 125 126 /** 127 * Getter for the Jasmine environment. Ensures one gets created 128 */ 129 jasmine.getEnv = function() { 130 var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); 131 return env; 132 }; 133 134 /** 135 * @ignore 136 * @private 137 * @param value 138 * @returns {Boolean} 139 */ 140 jasmine.isArray_ = function(value) { 141 return jasmine.isA_("Array", value); 142 }; 143 144 /** 145 * @ignore 146 * @private 147 * @param value 148 * @returns {Boolean} 149 */ 150 jasmine.isString_ = function(value) { 151 return jasmine.isA_("String", value); 152 }; 153 154 /** 155 * @ignore 156 * @private 157 * @param value 158 * @returns {Boolean} 159 */ 160 jasmine.isNumber_ = function(value) { 161 return jasmine.isA_("Number", value); 162 }; 163 164 /** 165 * @ignore 166 * @private 167 * @param {String} typeName 168 * @param value 169 * @returns {Boolean} 170 */ 171 jasmine.isA_ = function(typeName, value) { 172 return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; 173 }; 174 175 /** 176 * Pretty printer for expecations. Takes any object and turns it into a human-readable string. 177 * 178 * @param value {Object} an object to be outputted 179 * @returns {String} 180 */ 181 jasmine.pp = function(value) { 182 var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); 183 stringPrettyPrinter.format(value); 184 return stringPrettyPrinter.string; 185 }; 186 187 /** 188 * Returns true if the object is a DOM Node. 189 * 190 * @param {Object} obj object to check 191 * @returns {Boolean} 192 */ 193 jasmine.isDomNode = function(obj) { 194 return obj.nodeType > 0; 195 }; 196 197 /** 198 * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. 199 * 200 * @example 201 * // don't care about which function is passed in, as long as it's a function 202 * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); 203 * 204 * @param {Class} clazz 205 * @returns matchable object of the type clazz 206 */ 207 jasmine.any = function(clazz) { 208 return new jasmine.Matchers.Any(clazz); 209 }; 210 211 /** 212 * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the 213 * attributes on the object. 214 * 215 * @example 216 * // don't care about any other attributes than foo. 217 * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); 218 * 219 * @param sample {Object} sample 220 * @returns matchable object for the sample 221 */ 222 jasmine.objectContaining = function (sample) { 223 return new jasmine.Matchers.ObjectContaining(sample); 224 }; 225 226 /** 227 * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. 228 * 229 * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine 230 * expectation syntax. Spies can be checked if they were called or not and what the calling params were. 231 * 232 * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). 233 * 234 * Spies are torn down at the end of every spec. 235 * 236 * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. 237 * 238 * @example 239 * // a stub 240 * var myStub = jasmine.createSpy('myStub'); // can be used anywhere 241 * 242 * // spy example 243 * var foo = { 244 * not: function(bool) { return !bool; } 245 * } 246 * 247 * // actual foo.not will not be called, execution stops 248 * spyOn(foo, 'not'); 249 250 // foo.not spied upon, execution will continue to implementation 251 * spyOn(foo, 'not').andCallThrough(); 252 * 253 * // fake example 254 * var foo = { 255 * not: function(bool) { return !bool; } 256 * } 257 * 258 * // foo.not(val) will return val 259 * spyOn(foo, 'not').andCallFake(function(value) {return value;}); 260 * 261 * // mock example 262 * foo.not(7 == 7); 263 * expect(foo.not).toHaveBeenCalled(); 264 * expect(foo.not).toHaveBeenCalledWith(true); 265 * 266 * @constructor 267 * @see spyOn, jasmine.createSpy, jasmine.createSpyObj 268 * @param {String} name 269 */ 270 jasmine.Spy = function(name) { 271 /** 272 * The name of the spy, if provided. 273 */ 274 this.identity = name || 'unknown'; 275 /** 276 * Is this Object a spy? 277 */ 278 this.isSpy = true; 279 /** 280 * The actual function this spy stubs. 281 */ 282 this.plan = function() { 283 }; 284 /** 285 * Tracking of the most recent call to the spy. 286 * @example 287 * var mySpy = jasmine.createSpy('foo'); 288 * mySpy(1, 2); 289 * mySpy.mostRecentCall.args = [1, 2]; 290 */ 291 this.mostRecentCall = {}; 292 293 /** 294 * Holds arguments for each call to the spy, indexed by call count 295 * @example 296 * var mySpy = jasmine.createSpy('foo'); 297 * mySpy(1, 2); 298 * mySpy(7, 8); 299 * mySpy.mostRecentCall.args = [7, 8]; 300 * mySpy.argsForCall[0] = [1, 2]; 301 * mySpy.argsForCall[1] = [7, 8]; 302 */ 303 this.argsForCall = []; 304 this.calls = []; 305 }; 306 307 /** 308 * Tells a spy to call through to the actual implemenatation. 309 * 310 * @example 311 * var foo = { 312 * bar: function() { // do some stuff } 313 * } 314 * 315 * // defining a spy on an existing property: foo.bar 316 * spyOn(foo, 'bar').andCallThrough(); 317 */ 318 jasmine.Spy.prototype.andCallThrough = function() { 319 this.plan = this.originalValue; 320 return this; 321 }; 322 323 /** 324 * For setting the return value of a spy. 325 * 326 * @example 327 * // defining a spy from scratch: foo() returns 'baz' 328 * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); 329 * 330 * // defining a spy on an existing property: foo.bar() returns 'baz' 331 * spyOn(foo, 'bar').andReturn('baz'); 332 * 333 * @param {Object} value 334 */ 335 jasmine.Spy.prototype.andReturn = function(value) { 336 this.plan = function() { 337 return value; 338 }; 339 return this; 340 }; 341 342 /** 343 * For throwing an exception when a spy is called. 344 * 345 * @example 346 * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' 347 * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); 348 * 349 * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' 350 * spyOn(foo, 'bar').andThrow('baz'); 351 * 352 * @param {String} exceptionMsg 353 */ 354 jasmine.Spy.prototype.andThrow = function(exceptionMsg) { 355 this.plan = function() { 356 throw exceptionMsg; 357 }; 358 return this; 359 }; 360 361 /** 362 * Calls an alternate implementation when a spy is called. 363 * 364 * @example 365 * var baz = function() { 366 * // do some stuff, return something 367 * } 368 * // defining a spy from scratch: foo() calls the function baz 369 * var foo = jasmine.createSpy('spy on foo').andCall(baz); 370 * 371 * // defining a spy on an existing property: foo.bar() calls an anonymnous function 372 * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); 373 * 374 * @param {Function} fakeFunc 375 */ 376 jasmine.Spy.prototype.andCallFake = function(fakeFunc) { 377 this.plan = fakeFunc; 378 return this; 379 }; 380 381 /** 382 * Resets all of a spy's the tracking variables so that it can be used again. 383 * 384 * @example 385 * spyOn(foo, 'bar'); 386 * 387 * foo.bar(); 388 * 389 * expect(foo.bar.callCount).toEqual(1); 390 * 391 * foo.bar.reset(); 392 * 393 * expect(foo.bar.callCount).toEqual(0); 394 */ 395 jasmine.Spy.prototype.reset = function() { 396 this.wasCalled = false; 397 this.callCount = 0; 398 this.argsForCall = []; 399 this.calls = []; 400 this.mostRecentCall = {}; 401 }; 402 403 jasmine.createSpy = function(name) { 404 405 var spyObj = function() { 406 spyObj.wasCalled = true; 407 spyObj.callCount++; 408 var args = jasmine.util.argsToArray(arguments); 409 spyObj.mostRecentCall.object = this; 410 spyObj.mostRecentCall.args = args; 411 spyObj.argsForCall.push(args); 412 spyObj.calls.push({object: this, args: args}); 413 return spyObj.plan.apply(this, arguments); 414 }; 415 416 var spy = new jasmine.Spy(name); 417 418 for (var prop in spy) { 419 spyObj[prop] = spy[prop]; 420 } 421 422 spyObj.reset(); 423 424 return spyObj; 425 }; 426 427 /** 428 * Determines whether an object is a spy. 429 * 430 * @param {jasmine.Spy|Object} putativeSpy 431 * @returns {Boolean} 432 */ 433 jasmine.isSpy = function(putativeSpy) { 434 return putativeSpy && putativeSpy.isSpy; 435 }; 436 437 /** 438 * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something 439 * large in one call. 440 * 441 * @param {String} baseName name of spy class 442 * @param {Array} methodNames array of names of methods to make spies 443 */ 444 jasmine.createSpyObj = function(baseName, methodNames) { 445 if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { 446 throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); 447 } 448 var obj = {}; 449 for (var i = 0; i < methodNames.length; i++) { 450 obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); 451 } 452 return obj; 453 }; 454 455 /** 456 * All parameters are pretty-printed and concatenated together, then written to the current spec's output. 457 * 458 * Be careful not to leave calls to <code>jasmine.log</code> in production code. 459 */ 460 jasmine.log = function() { 461 var spec = jasmine.getEnv().currentSpec; 462 spec.log.apply(spec, arguments); 463 }; 464 465 /** 466 * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. 467 * 468 * @example 469 * // spy example 470 * var foo = { 471 * not: function(bool) { return !bool; } 472 * } 473 * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops 474 * 475 * @see jasmine.createSpy 476 * @param obj 477 * @param methodName 478 * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods 479 */ 480 var spyOn = function(obj, methodName) { 481 return jasmine.getEnv().currentSpec.spyOn(obj, methodName); 482 }; 483 if (isCommonJS) exports.spyOn = spyOn; 484 485 /** 486 * Creates a Jasmine spec that will be added to the current suite. 487 * 488 * // TODO: pending tests 489 * 490 * @example 491 * it('should be true', function() { 492 * expect(true).toEqual(true); 493 * }); 494 * 495 * @param {String} desc description of this specification 496 * @param {Function} func defines the preconditions and expectations of the spec 497 */ 498 var it = function(desc, func) { 499 return jasmine.getEnv().it(desc, func); 500 }; 501 if (isCommonJS) exports.it = it; 502 503 /** 504 * Creates a <em>disabled</em> Jasmine spec. 505 * 506 * A convenience method that allows existing specs to be disabled temporarily during development. 507 * 508 * @param {String} desc description of this specification 509 * @param {Function} func defines the preconditions and expectations of the spec 510 */ 511 var xit = function(desc, func) { 512 return jasmine.getEnv().xit(desc, func); 513 }; 514 if (isCommonJS) exports.xit = xit; 515 516 /** 517 * Starts a chain for a Jasmine expectation. 518 * 519 * It is passed an Object that is the actual value and should chain to one of the many 520 * jasmine.Matchers functions. 521 * 522 * @param {Object} actual Actual value to test against and expected value 523 * @return {jasmine.Matchers} 524 */ 525 var expect = function(actual) { 526 return jasmine.getEnv().currentSpec.expect(actual); 527 }; 528 if (isCommonJS) exports.expect = expect; 529 530 /** 531 * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. 532 * 533 * @param {Function} func Function that defines part of a jasmine spec. 534 */ 535 var runs = function(func) { 536 jasmine.getEnv().currentSpec.runs(func); 537 }; 538 if (isCommonJS) exports.runs = runs; 539 540 /** 541 * Waits a fixed time period before moving to the next block. 542 * 543 * @deprecated Use waitsFor() instead 544 * @param {Number} timeout milliseconds to wait 545 */ 546 var waits = function(timeout) { 547 jasmine.getEnv().currentSpec.waits(timeout); 548 }; 549 if (isCommonJS) exports.waits = waits; 550 551 /** 552 * Waits for the latchFunction to return true before proceeding to the next block. 553 * 554 * @param {Function} latchFunction 555 * @param {String} optional_timeoutMessage 556 * @param {Number} optional_timeout 557 */ 558 var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 559 jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); 560 }; 561 if (isCommonJS) exports.waitsFor = waitsFor; 562 563 /** 564 * A function that is called before each spec in a suite. 565 * 566 * Used for spec setup, including validating assumptions. 567 * 568 * @param {Function} beforeEachFunction 569 */ 570 var beforeEach = function(beforeEachFunction) { 571 jasmine.getEnv().beforeEach(beforeEachFunction); 572 }; 573 if (isCommonJS) exports.beforeEach = beforeEach; 574 575 /** 576 * A function that is called after each spec in a suite. 577 * 578 * Used for restoring any state that is hijacked during spec execution. 579 * 580 * @param {Function} afterEachFunction 581 */ 582 var afterEach = function(afterEachFunction) { 583 jasmine.getEnv().afterEach(afterEachFunction); 584 }; 585 if (isCommonJS) exports.afterEach = afterEach; 586 587 /** 588 * Defines a suite of specifications. 589 * 590 * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared 591 * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization 592 * of setup in some tests. 593 * 594 * @example 595 * // TODO: a simple suite 596 * 597 * // TODO: a simple suite with a nested describe block 598 * 599 * @param {String} description A string, usually the class under test. 600 * @param {Function} specDefinitions function that defines several specs. 601 */ 602 var describe = function(description, specDefinitions) { 603 return jasmine.getEnv().describe(description, specDefinitions); 604 }; 605 if (isCommonJS) exports.describe = describe; 606 607 /** 608 * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. 609 * 610 * @param {String} description A string, usually the class under test. 611 * @param {Function} specDefinitions function that defines several specs. 612 */ 613 var xdescribe = function(description, specDefinitions) { 614 return jasmine.getEnv().xdescribe(description, specDefinitions); 615 }; 616 if (isCommonJS) exports.xdescribe = xdescribe; 617 618 619 // Provide the XMLHttpRequest class for IE 5.x-6.x: 620 jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { 621 function tryIt(f) { 622 try { 623 return f(); 624 } catch(e) { 625 } 626 return null; 627 } 628 629 var xhr = tryIt(function() { 630 return new ActiveXObject("Msxml2.XMLHTTP.6.0"); 631 }) || 632 tryIt(function() { 633 return new ActiveXObject("Msxml2.XMLHTTP.3.0"); 634 }) || 635 tryIt(function() { 636 return new ActiveXObject("Msxml2.XMLHTTP"); 637 }) || 638 tryIt(function() { 639 return new ActiveXObject("Microsoft.XMLHTTP"); 640 }); 641 642 if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); 643 644 return xhr; 645 } : XMLHttpRequest; 646 /** 647 * @namespace 648 */ 649 jasmine.util = {}; 650 651 /** 652 * Declare that a child class inherit it's prototype from the parent class. 653 * 654 * @private 655 * @param {Function} childClass 656 * @param {Function} parentClass 657 */ 658 jasmine.util.inherit = function(childClass, parentClass) { 659 /** 660 * @private 661 */ 662 var subclass = function() { 663 }; 664 subclass.prototype = parentClass.prototype; 665 childClass.prototype = new subclass(); 666 }; 667 668 jasmine.util.formatException = function(e) { 669 var lineNumber; 670 if (e.line) { 671 lineNumber = e.line; 672 } 673 else if (e.lineNumber) { 674 lineNumber = e.lineNumber; 675 } 676 677 var file; 678 679 if (e.sourceURL) { 680 file = e.sourceURL; 681 } 682 else if (e.fileName) { 683 file = e.fileName; 684 } 685 686 var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); 687 688 if (file && lineNumber) { 689 message += ' in ' + file + ' (line ' + lineNumber + ')'; 690 } 691 692 return message; 693 }; 694 695 jasmine.util.htmlEscape = function(str) { 696 if (!str) return str; 697 return str.replace(/&/g, '&') 698 .replace(/</g, '<') 699 .replace(/>/g, '>'); 700 }; 701 702 jasmine.util.argsToArray = function(args) { 703 var arrayOfArgs = []; 704 for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); 705 return arrayOfArgs; 706 }; 707 708 jasmine.util.extend = function(destination, source) { 709 for (var property in source) destination[property] = source[property]; 710 return destination; 711 }; 712 713 /** 714 * Environment for Jasmine 715 * 716 * @constructor 717 */ 718 jasmine.Env = function() { 719 this.currentSpec = null; 720 this.currentSuite = null; 721 this.currentRunner_ = new jasmine.Runner(this); 722 723 this.reporter = new jasmine.MultiReporter(); 724 725 this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; 726 this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; 727 this.lastUpdate = 0; 728 this.specFilter = function() { 729 return true; 730 }; 731 732 this.nextSpecId_ = 0; 733 this.nextSuiteId_ = 0; 734 this.equalityTesters_ = []; 735 736 // wrap matchers 737 this.matchersClass = function() { 738 jasmine.Matchers.apply(this, arguments); 739 }; 740 jasmine.util.inherit(this.matchersClass, jasmine.Matchers); 741 742 jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); 743 }; 744 745 746 jasmine.Env.prototype.setTimeout = jasmine.setTimeout; 747 jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; 748 jasmine.Env.prototype.setInterval = jasmine.setInterval; 749 jasmine.Env.prototype.clearInterval = jasmine.clearInterval; 750 751 /** 752 * @returns an object containing jasmine version build info, if set. 753 */ 754 jasmine.Env.prototype.version = function () { 755 if (jasmine.version_) { 756 return jasmine.version_; 757 } else { 758 throw new Error('Version not set'); 759 } 760 }; 761 762 /** 763 * @returns string containing jasmine version build info, if set. 764 */ 765 jasmine.Env.prototype.versionString = function() { 766 if (!jasmine.version_) { 767 return "version unknown"; 768 } 769 770 var version = this.version(); 771 var versionString = version.major + "." + version.minor + "." + version.build; 772 if (version.release_candidate) { 773 versionString += ".rc" + version.release_candidate; 774 } 775 versionString += " revision " + version.revision; 776 return versionString; 777 }; 778 779 /** 780 * @returns a sequential integer starting at 0 781 */ 782 jasmine.Env.prototype.nextSpecId = function () { 783 return this.nextSpecId_++; 784 }; 785 786 /** 787 * @returns a sequential integer starting at 0 788 */ 789 jasmine.Env.prototype.nextSuiteId = function () { 790 return this.nextSuiteId_++; 791 }; 792 793 /** 794 * Register a reporter to receive status updates from Jasmine. 795 * @param {jasmine.Reporter} reporter An object which will receive status updates. 796 */ 797 jasmine.Env.prototype.addReporter = function(reporter) { 798 this.reporter.addReporter(reporter); 799 }; 800 801 jasmine.Env.prototype.execute = function() { 802 this.currentRunner_.execute(); 803 }; 804 805 jasmine.Env.prototype.describe = function(description, specDefinitions) { 806 var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); 807 808 var parentSuite = this.currentSuite; 809 if (parentSuite) { 810 parentSuite.add(suite); 811 } else { 812 this.currentRunner_.add(suite); 813 } 814 815 this.currentSuite = suite; 816 817 var declarationError = null; 818 try { 819 specDefinitions.call(suite); 820 } catch(e) { 821 declarationError = e; 822 } 823 824 if (declarationError) { 825 this.it("encountered a declaration exception", function() { 826 throw declarationError; 827 }); 828 } 829 830 this.currentSuite = parentSuite; 831 832 return suite; 833 }; 834 835 jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { 836 if (this.currentSuite) { 837 this.currentSuite.beforeEach(beforeEachFunction); 838 } else { 839 this.currentRunner_.beforeEach(beforeEachFunction); 840 } 841 }; 842 843 jasmine.Env.prototype.currentRunner = function () { 844 return this.currentRunner_; 845 }; 846 847 jasmine.Env.prototype.afterEach = function(afterEachFunction) { 848 if (this.currentSuite) { 849 this.currentSuite.afterEach(afterEachFunction); 850 } else { 851 this.currentRunner_.afterEach(afterEachFunction); 852 } 853 854 }; 855 856 jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { 857 return { 858 execute: function() { 859 } 860 }; 861 }; 862 863 jasmine.Env.prototype.it = function(description, func) { 864 var spec = new jasmine.Spec(this, this.currentSuite, description); 865 this.currentSuite.add(spec); 866 this.currentSpec = spec; 867 868 if (func) { 869 spec.runs(func); 870 } 871 872 return spec; 873 }; 874 875 jasmine.Env.prototype.xit = function(desc, func) { 876 return { 877 id: this.nextSpecId(), 878 runs: function() { 879 } 880 }; 881 }; 882 883 jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { 884 if (a.source != b.source) 885 mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); 886 887 if (a.ignoreCase != b.ignoreCase) 888 mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); 889 890 if (a.global != b.global) 891 mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); 892 893 if (a.multiline != b.multiline) 894 mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); 895 896 if (a.sticky != b.sticky) 897 mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); 898 899 return (mismatchValues.length === 0); 900 }; 901 902 jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { 903 if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { 904 return true; 905 } 906 907 a.__Jasmine_been_here_before__ = b; 908 b.__Jasmine_been_here_before__ = a; 909 910 var hasKey = function(obj, keyName) { 911 return obj !== null && obj[keyName] !== jasmine.undefined; 912 }; 913 914 for (var property in b) { 915 if (!hasKey(a, property) && hasKey(b, property)) { 916 mismatchKeys.push("expected has key '" + property + "', but missing from actual."); 917 } 918 } 919 for (property in a) { 920 if (!hasKey(b, property) && hasKey(a, property)) { 921 mismatchKeys.push("expected missing key '" + property + "', but present in actual."); 922 } 923 } 924 for (property in b) { 925 if (property == '__Jasmine_been_here_before__') continue; 926 if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { 927 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."); 928 } 929 } 930 931 if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { 932 mismatchValues.push("arrays were not the same length"); 933 } 934 935 delete a.__Jasmine_been_here_before__; 936 delete b.__Jasmine_been_here_before__; 937 return (mismatchKeys.length === 0 && mismatchValues.length === 0); 938 }; 939 940 jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { 941 mismatchKeys = mismatchKeys || []; 942 mismatchValues = mismatchValues || []; 943 944 for (var i = 0; i < this.equalityTesters_.length; i++) { 945 var equalityTester = this.equalityTesters_[i]; 946 var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); 947 if (result !== jasmine.undefined) return result; 948 } 949 950 if (a === b) return true; 951 952 if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { 953 return (a == jasmine.undefined && b == jasmine.undefined); 954 } 955 956 if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { 957 return a === b; 958 } 959 960 if (a instanceof Date && b instanceof Date) { 961 return a.getTime() == b.getTime(); 962 } 963 964 if (a.jasmineMatches) { 965 return a.jasmineMatches(b); 966 } 967 968 if (b.jasmineMatches) { 969 return b.jasmineMatches(a); 970 } 971 972 if (a instanceof jasmine.Matchers.ObjectContaining) { 973 return a.matches(b); 974 } 975 976 if (b instanceof jasmine.Matchers.ObjectContaining) { 977 return b.matches(a); 978 } 979 980 if (jasmine.isString_(a) && jasmine.isString_(b)) { 981 return (a == b); 982 } 983 984 if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { 985 return (a == b); 986 } 987 988 if (a instanceof RegExp && b instanceof RegExp) { 989 return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); 990 } 991 992 if (typeof a === "object" && typeof b === "object") { 993 return this.compareObjects_(a, b, mismatchKeys, mismatchValues); 994 } 995 996 //Straight check 997 return (a === b); 998 }; 999 1000 jasmine.Env.prototype.contains_ = function(haystack, needle) { 1001 if (jasmine.isArray_(haystack)) { 1002 for (var i = 0; i < haystack.length; i++) { 1003 if (this.equals_(haystack[i], needle)) return true; 1004 } 1005 return false; 1006 } 1007 return haystack.indexOf(needle) >= 0; 1008 }; 1009 1010 jasmine.Env.prototype.addEqualityTester = function(equalityTester) { 1011 this.equalityTesters_.push(equalityTester); 1012 }; 1013 /** No-op base class for Jasmine reporters. 1014 * 1015 * @constructor 1016 */ 1017 jasmine.Reporter = function() { 1018 }; 1019 1020 //noinspection JSUnusedLocalSymbols 1021 jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { 1022 }; 1023 1024 //noinspection JSUnusedLocalSymbols 1025 jasmine.Reporter.prototype.reportRunnerResults = function(runner) { 1026 }; 1027 1028 //noinspection JSUnusedLocalSymbols 1029 jasmine.Reporter.prototype.reportSuiteResults = function(suite) { 1030 }; 1031 1032 //noinspection JSUnusedLocalSymbols 1033 jasmine.Reporter.prototype.reportSpecStarting = function(spec) { 1034 }; 1035 1036 //noinspection JSUnusedLocalSymbols 1037 jasmine.Reporter.prototype.reportSpecResults = function(spec) { 1038 }; 1039 1040 //noinspection JSUnusedLocalSymbols 1041 jasmine.Reporter.prototype.log = function(str) { 1042 }; 1043 1044 /** 1045 * Blocks are functions with executable code that make up a spec. 1046 * 1047 * @constructor 1048 * @param {jasmine.Env} env 1049 * @param {Function} func 1050 * @param {jasmine.Spec} spec 1051 */ 1052 jasmine.Block = function(env, func, spec) { 1053 this.env = env; 1054 this.func = func; 1055 this.spec = spec; 1056 }; 1057 1058 jasmine.Block.prototype.execute = function(onComplete) { 1059 if (!jasmine.CATCH_EXCEPTIONS) { 1060 this.func.apply(this.spec); 1061 } 1062 else { 1063 try { 1064 this.func.apply(this.spec); 1065 } catch (e) { 1066 this.spec.fail(e); 1067 } 1068 } 1069 onComplete(); 1070 }; 1071 /** JavaScript API reporter. 1072 * 1073 * @constructor 1074 */ 1075 jasmine.JsApiReporter = function() { 1076 this.started = false; 1077 this.finished = false; 1078 this.suites_ = []; 1079 this.results_ = {}; 1080 }; 1081 1082 jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { 1083 this.started = true; 1084 var suites = runner.topLevelSuites(); 1085 for (var i = 0; i < suites.length; i++) { 1086 var suite = suites[i]; 1087 this.suites_.push(this.summarize_(suite)); 1088 } 1089 }; 1090 1091 jasmine.JsApiReporter.prototype.suites = function() { 1092 return this.suites_; 1093 }; 1094 1095 jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { 1096 var isSuite = suiteOrSpec instanceof jasmine.Suite; 1097 var summary = { 1098 id: suiteOrSpec.id, 1099 name: suiteOrSpec.description, 1100 type: isSuite ? 'suite' : 'spec', 1101 children: [] 1102 }; 1103 1104 if (isSuite) { 1105 var children = suiteOrSpec.children(); 1106 for (var i = 0; i < children.length; i++) { 1107 summary.children.push(this.summarize_(children[i])); 1108 } 1109 } 1110 return summary; 1111 }; 1112 1113 jasmine.JsApiReporter.prototype.results = function() { 1114 return this.results_; 1115 }; 1116 1117 jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { 1118 return this.results_[specId]; 1119 }; 1120 1121 //noinspection JSUnusedLocalSymbols 1122 jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { 1123 this.finished = true; 1124 }; 1125 1126 //noinspection JSUnusedLocalSymbols 1127 jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { 1128 }; 1129 1130 //noinspection JSUnusedLocalSymbols 1131 jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { 1132 this.results_[spec.id] = { 1133 messages: spec.results().getItems(), 1134 result: spec.results().failedCount > 0 ? "failed" : "passed" 1135 }; 1136 }; 1137 1138 //noinspection JSUnusedLocalSymbols 1139 jasmine.JsApiReporter.prototype.log = function(str) { 1140 }; 1141 1142 jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ 1143 var results = {}; 1144 for (var i = 0; i < specIds.length; i++) { 1145 var specId = specIds[i]; 1146 results[specId] = this.summarizeResult_(this.results_[specId]); 1147 } 1148 return results; 1149 }; 1150 1151 jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ 1152 var summaryMessages = []; 1153 var messagesLength = result.messages.length; 1154 for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { 1155 var resultMessage = result.messages[messageIndex]; 1156 summaryMessages.push({ 1157 text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, 1158 passed: resultMessage.passed ? resultMessage.passed() : true, 1159 type: resultMessage.type, 1160 message: resultMessage.message, 1161 trace: { 1162 stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined 1163 } 1164 }); 1165 } 1166 1167 return { 1168 result : result.result, 1169 messages : summaryMessages 1170 }; 1171 }; 1172 1173 /** 1174 * @constructor 1175 * @param {jasmine.Env} env 1176 * @param actual 1177 * @param {jasmine.Spec} spec 1178 */ 1179 jasmine.Matchers = function(env, actual, spec, opt_isNot) { 1180 this.env = env; 1181 this.actual = actual; 1182 this.spec = spec; 1183 this.isNot = opt_isNot || false; 1184 this.reportWasCalled_ = false; 1185 }; 1186 1187 // todo: @deprecated as of Jasmine 0.11, remove soon [xw] 1188 jasmine.Matchers.pp = function(str) { 1189 throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); 1190 }; 1191 1192 // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] 1193 jasmine.Matchers.prototype.report = function(result, failing_message, details) { 1194 throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); 1195 }; 1196 1197 jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { 1198 for (var methodName in prototype) { 1199 if (methodName == 'report') continue; 1200 var orig = prototype[methodName]; 1201 matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); 1202 } 1203 }; 1204 1205 jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { 1206 return function() { 1207 var matcherArgs = jasmine.util.argsToArray(arguments); 1208 var result = matcherFunction.apply(this, arguments); 1209 1210 if (this.isNot) { 1211 result = !result; 1212 } 1213 1214 if (this.reportWasCalled_) return result; 1215 1216 var message; 1217 if (!result) { 1218 if (this.message) { 1219 message = this.message.apply(this, arguments); 1220 if (jasmine.isArray_(message)) { 1221 message = message[this.isNot ? 1 : 0]; 1222 } 1223 } else { 1224 var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); 1225 message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; 1226 if (matcherArgs.length > 0) { 1227 for (var i = 0; i < matcherArgs.length; i++) { 1228 if (i > 0) message += ","; 1229 message += " " + jasmine.pp(matcherArgs[i]); 1880 // Objects with different constructors are not equivalent, but `Object`s 1881 // from different frames are. 1882 var aCtor = a.constructor, bCtor = b.constructor; 1883 if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) && 1884 isFunction(bCtor) && (bCtor instanceof bCtor))) { 1885 return false; 1886 } 1887 // Deep compare objects. 1888 for (var key in a) { 1889 if (has(a, key)) { 1890 // Count the expected number of properties. 1891 size++; 1892 // Deep compare each member. 1893 if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; } 1894 } 1895 } 1896 // Ensure that both objects contain the same number of properties. 1897 if (result) { 1898 for (key in b) { 1899 if (has(b, key) && !(size--)) { break; } 1900 } 1901 result = !size; 1902 } 1903 } 1904 // Remove the first object from the stack of traversed objects. 1905 aStack.pop(); 1906 bStack.pop(); 1907 1908 return result; 1909 1910 function has(obj, key) { 1911 return obj.hasOwnProperty(key); 1912 } 1913 1914 function isFunction(obj) { 1915 return typeof obj === 'function'; 1916 } 1917 } 1918 }; 1919 1920 getJasmineRequireObj().toBe = function() { 1921 function toBe() { 1922 return { 1923 compare: function(actual, expected) { 1924 return { 1925 pass: actual === expected 1926 }; 1927 } 1928 }; 1929 } 1930 1931 return toBe; 1932 }; 1933 1934 getJasmineRequireObj().toBeCloseTo = function() { 1935 1936 function toBeCloseTo() { 1937 return { 1938 compare: function(actual, expected, precision) { 1939 if (precision !== 0) { 1940 precision = precision || 2; 1941 } 1942 1943 return { 1944 pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2) 1945 }; 1946 } 1947 }; 1948 } 1949 1950 return toBeCloseTo; 1951 }; 1952 1953 getJasmineRequireObj().toBeDefined = function() { 1954 function toBeDefined() { 1955 return { 1956 compare: function(actual) { 1957 return { 1958 pass: (void 0 !== actual) 1959 }; 1960 } 1961 }; 1962 } 1963 1964 return toBeDefined; 1965 }; 1966 1967 getJasmineRequireObj().toBeFalsy = function() { 1968 function toBeFalsy() { 1969 return { 1970 compare: function(actual) { 1971 return { 1972 pass: !!!actual 1973 }; 1974 } 1975 }; 1976 } 1977 1978 return toBeFalsy; 1979 }; 1980 1981 getJasmineRequireObj().toBeGreaterThan = function() { 1982 1983 function toBeGreaterThan() { 1984 return { 1985 compare: function(actual, expected) { 1986 return { 1987 pass: actual > expected 1988 }; 1989 } 1990 }; 1991 } 1992 1993 return toBeGreaterThan; 1994 }; 1995 1996 1997 getJasmineRequireObj().toBeLessThan = function() { 1998 function toBeLessThan() { 1999 return { 2000 2001 compare: function(actual, expected) { 2002 return { 2003 pass: actual < expected 2004 }; 2005 } 2006 }; 2007 } 2008 2009 return toBeLessThan; 2010 }; 2011 getJasmineRequireObj().toBeNaN = function(j$) { 2012 2013 function toBeNaN() { 2014 return { 2015 compare: function(actual) { 2016 var result = { 2017 pass: (actual !== actual) 2018 }; 2019 2020 if (result.pass) { 2021 result.message = "Expected actual not to be NaN."; 2022 } else { 2023 result.message = "Expected " + j$.pp(actual) + " to be NaN."; 2024 } 2025 2026 return result; 2027 } 2028 }; 2029 } 2030 2031 return toBeNaN; 2032 }; 2033 2034 getJasmineRequireObj().toBeNull = function() { 2035 2036 function toBeNull() { 2037 return { 2038 compare: function(actual) { 2039 return { 2040 pass: actual === null 2041 }; 2042 } 2043 }; 2044 } 2045 2046 return toBeNull; 2047 }; 2048 2049 getJasmineRequireObj().toBeTruthy = function() { 2050 2051 function toBeTruthy() { 2052 return { 2053 compare: function(actual) { 2054 return { 2055 pass: !!actual 2056 }; 2057 } 2058 }; 2059 } 2060 2061 return toBeTruthy; 2062 }; 2063 2064 getJasmineRequireObj().toBeUndefined = function() { 2065 2066 function toBeUndefined() { 2067 return { 2068 compare: function(actual) { 2069 return { 2070 pass: void 0 === actual 2071 }; 2072 } 2073 }; 2074 } 2075 2076 return toBeUndefined; 2077 }; 2078 2079 getJasmineRequireObj().toContain = function() { 2080 function toContain(util, customEqualityTesters) { 2081 customEqualityTesters = customEqualityTesters || []; 2082 2083 return { 2084 compare: function(actual, expected) { 2085 2086 return { 2087 pass: util.contains(actual, expected, customEqualityTesters) 2088 }; 2089 } 2090 }; 2091 } 2092 2093 return toContain; 2094 }; 2095 2096 getJasmineRequireObj().toEqual = function() { 2097 2098 function toEqual(util, customEqualityTesters) { 2099 customEqualityTesters = customEqualityTesters || []; 2100 2101 return { 2102 compare: function(actual, expected) { 2103 var result = { 2104 pass: false 2105 }; 2106 2107 result.pass = util.equals(actual, expected, customEqualityTesters); 2108 2109 return result; 2110 } 2111 }; 2112 } 2113 2114 return toEqual; 2115 }; 2116 2117 getJasmineRequireObj().toHaveBeenCalled = function(j$) { 2118 2119 function toHaveBeenCalled() { 2120 return { 2121 compare: function(actual) { 2122 var result = {}; 2123 2124 if (!j$.isSpy(actual)) { 2125 throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); 2126 } 2127 2128 if (arguments.length > 1) { 2129 throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); 2130 } 2131 2132 result.pass = actual.calls.any(); 2133 2134 result.message = result.pass ? 2135 "Expected spy " + actual.and.identity() + " not to have been called." : 2136 "Expected spy " + actual.and.identity() + " to have been called."; 2137 2138 return result; 2139 } 2140 }; 2141 } 2142 2143 return toHaveBeenCalled; 2144 }; 2145 2146 getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { 2147 2148 function toHaveBeenCalledWith(util) { 2149 return { 2150 compare: function() { 2151 var args = Array.prototype.slice.call(arguments, 0), 2152 actual = args[0], 2153 expectedArgs = args.slice(1), 2154 result = { pass: false }; 2155 2156 if (!j$.isSpy(actual)) { 2157 throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); 2158 } 2159 2160 if (!actual.calls.any()) { 2161 result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but it was never called."; 2162 return result; 2163 } 2164 2165 if (util.contains(actual.calls.allArgs(), expectedArgs)) { 2166 result.pass = true; 2167 result.message = "Expected spy " + actual.and.identity() + " not to have been called with " + j$.pp(expectedArgs) + " but it was."; 2168 } else { 2169 result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but actual calls were " + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + "."; 2170 } 2171 2172 return result; 2173 } 2174 }; 2175 } 2176 2177 return toHaveBeenCalledWith; 2178 }; 2179 2180 getJasmineRequireObj().toMatch = function() { 2181 2182 function toMatch() { 2183 return { 2184 compare: function(actual, expected) { 2185 var regexp = new RegExp(expected); 2186 2187 return { 2188 pass: regexp.test(actual) 2189 }; 2190 } 2191 }; 2192 } 2193 2194 return toMatch; 2195 }; 2196 2197 getJasmineRequireObj().toThrow = function(j$) { 2198 2199 function toThrow(util) { 2200 return { 2201 compare: function(actual, expected) { 2202 var result = { pass: false }, 2203 threw = false, 2204 thrown; 2205 2206 if (typeof actual != "function") { 2207 throw new Error("Actual is not a Function"); 2208 } 2209 2210 try { 2211 actual(); 2212 } catch (e) { 2213 threw = true; 2214 thrown = e; 2215 } 2216 2217 if (!threw) { 2218 result.message = "Expected function to throw an exception."; 2219 return result; 2220 } 2221 2222 if (arguments.length == 1) { 2223 result.pass = true; 2224 result.message = "Expected function not to throw, but it threw " + j$.pp(thrown) + "."; 2225 2226 return result; 2227 } 2228 2229 if (util.equals(thrown, expected)) { 2230 result.pass = true; 2231 result.message = "Expected function not to throw " + j$.pp(expected) + "."; 2232 } else { 2233 result.message = "Expected function to throw " + j$.pp(expected) + ", but it threw " + j$.pp(thrown) + "."; 2234 } 2235 2236 return result; 2237 } 2238 }; 2239 } 2240 2241 return toThrow; 2242 }; 2243 2244 getJasmineRequireObj().toThrowError = function(j$) { 2245 function toThrowError (util) { 2246 return { 2247 compare: function(actual) { 2248 var threw = false, 2249 thrown, 2250 errorType, 2251 message, 2252 regexp, 2253 name, 2254 constructorName; 2255 2256 if (typeof actual != "function") { 2257 throw new Error("Actual is not a Function"); 2258 } 2259 2260 extractExpectedParams.apply(null, arguments); 2261 2262 try { 2263 actual(); 2264 } catch (e) { 2265 threw = true; 2266 thrown = e; 2267 } 2268 2269 if (!threw) { 2270 return fail("Expected function to throw an Error."); 2271 } 2272 2273 if (!(thrown instanceof Error)) { 2274 return fail("Expected function to throw an Error, but it threw " + thrown + "."); 2275 } 2276 2277 if (arguments.length == 1) { 2278 return pass("Expected function not to throw an Error, but it threw " + fnNameFor(thrown) + "."); 2279 } 2280 2281 if (errorType) { 2282 name = fnNameFor(errorType); 2283 constructorName = fnNameFor(thrown.constructor); 2284 } 2285 2286 if (errorType && message) { 2287 if (thrown.constructor == errorType && util.equals(thrown.message, message)) { 2288 return pass("Expected function not to throw " + name + " with message \"" + message + "\"."); 2289 } else { 2290 return fail("Expected function to throw " + name + " with message \"" + message + 2291 "\", but it threw " + constructorName + " with message \"" + thrown.message + "\"."); 1230 2292 } 1231 2293 } 1232 message += "."; 1233 } 1234 } 1235 var expectationResult = new jasmine.ExpectationResult({ 1236 matcherName: matcherName, 1237 passed: result, 1238 expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], 1239 actual: this.actual, 1240 message: message 1241 }); 1242 this.spec.addMatcherResult(expectationResult); 1243 return jasmine.undefined; 1244 }; 1245 }; 1246 1247 1248 1249 1250 /** 1251 * toBe: compares the actual to the expected using === 1252 * @param expected 1253 */ 1254 jasmine.Matchers.prototype.toBe = function(expected) { 1255 return this.actual === expected; 1256 }; 1257 1258 /** 1259 * toNotBe: compares the actual to the expected using !== 1260 * @param expected 1261 * @deprecated as of 1.0. Use not.toBe() instead. 1262 */ 1263 jasmine.Matchers.prototype.toNotBe = function(expected) { 1264 return this.actual !== expected; 1265 }; 1266 1267 /** 1268 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. 1269 * 1270 * @param expected 1271 */ 1272 jasmine.Matchers.prototype.toEqual = function(expected) { 1273 return this.env.equals_(this.actual, expected); 1274 }; 1275 1276 /** 1277 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual 1278 * @param expected 1279 * @deprecated as of 1.0. Use not.toEqual() instead. 1280 */ 1281 jasmine.Matchers.prototype.toNotEqual = function(expected) { 1282 return !this.env.equals_(this.actual, expected); 1283 }; 1284 1285 /** 1286 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes 1287 * a pattern or a String. 1288 * 1289 * @param expected 1290 */ 1291 jasmine.Matchers.prototype.toMatch = function(expected) { 1292 return new RegExp(expected).test(this.actual); 1293 }; 1294 1295 /** 1296 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch 1297 * @param expected 1298 * @deprecated as of 1.0. Use not.toMatch() instead. 1299 */ 1300 jasmine.Matchers.prototype.toNotMatch = function(expected) { 1301 return !(new RegExp(expected).test(this.actual)); 1302 }; 1303 1304 /** 1305 * Matcher that compares the actual to jasmine.undefined. 1306 */ 1307 jasmine.Matchers.prototype.toBeDefined = function() { 1308 return (this.actual !== jasmine.undefined); 1309 }; 1310 1311 /** 1312 * Matcher that compares the actual to jasmine.undefined. 1313 */ 1314 jasmine.Matchers.prototype.toBeUndefined = function() { 1315 return (this.actual === jasmine.undefined); 1316 }; 1317 1318 /** 1319 * Matcher that compares the actual to null. 1320 */ 1321 jasmine.Matchers.prototype.toBeNull = function() { 1322 return (this.actual === null); 1323 }; 1324 1325 /** 1326 * Matcher that compares the actual to NaN. 1327 */ 1328 jasmine.Matchers.prototype.toBeNaN = function() { 1329 this.message = function() { 1330 return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; 1331 }; 1332 1333 return (this.actual !== this.actual); 1334 }; 1335 1336 /** 1337 * Matcher that boolean not-nots the actual. 1338 */ 1339 jasmine.Matchers.prototype.toBeTruthy = function() { 1340 return !!this.actual; 1341 }; 1342 1343 1344 /** 1345 * Matcher that boolean nots the actual. 1346 */ 1347 jasmine.Matchers.prototype.toBeFalsy = function() { 1348 return !this.actual; 1349 }; 1350 1351 1352 /** 1353 * Matcher that checks to see if the actual, a Jasmine spy, was called. 1354 */ 1355 jasmine.Matchers.prototype.toHaveBeenCalled = function() { 1356 if (arguments.length > 0) { 1357 throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); 1358 } 1359 1360 if (!jasmine.isSpy(this.actual)) { 1361 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1362 } 1363 1364 this.message = function() { 1365 return [ 1366 "Expected spy " + this.actual.identity + " to have been called.", 1367 "Expected spy " + this.actual.identity + " not to have been called." 1368 ]; 1369 }; 1370 1371 return this.actual.wasCalled; 1372 }; 1373 1374 /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ 1375 jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; 1376 1377 /** 1378 * Matcher that checks to see if the actual, a Jasmine spy, was not called. 1379 * 1380 * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead 1381 */ 1382 jasmine.Matchers.prototype.wasNotCalled = function() { 1383 if (arguments.length > 0) { 1384 throw new Error('wasNotCalled does not take arguments'); 1385 } 1386 1387 if (!jasmine.isSpy(this.actual)) { 1388 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1389 } 1390 1391 this.message = function() { 1392 return [ 1393 "Expected spy " + this.actual.identity + " to not have been called.", 1394 "Expected spy " + this.actual.identity + " to have been called." 1395 ]; 1396 }; 1397 1398 return !this.actual.wasCalled; 1399 }; 1400 1401 /** 1402 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. 1403 * 1404 * @example 1405 * 1406 */ 1407 jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { 1408 var expectedArgs = jasmine.util.argsToArray(arguments); 1409 if (!jasmine.isSpy(this.actual)) { 1410 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1411 } 1412 this.message = function() { 1413 var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."; 1414 var positiveMessage = ""; 1415 if (this.actual.callCount === 0) { 1416 positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; 1417 } else { 1418 positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '') 1419 } 1420 return [positiveMessage, invertedMessage]; 1421 }; 1422 1423 return this.env.contains_(this.actual.argsForCall, expectedArgs); 1424 }; 1425 1426 /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ 1427 jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; 1428 1429 /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ 1430 jasmine.Matchers.prototype.wasNotCalledWith = function() { 1431 var expectedArgs = jasmine.util.argsToArray(arguments); 1432 if (!jasmine.isSpy(this.actual)) { 1433 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1434 } 1435 1436 this.message = function() { 1437 return [ 1438 "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", 1439 "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" 1440 ]; 1441 }; 1442 1443 return !this.env.contains_(this.actual.argsForCall, expectedArgs); 1444 }; 1445 1446 /** 1447 * Matcher that checks that the expected item is an element in the actual Array. 1448 * 1449 * @param {Object} expected 1450 */ 1451 jasmine.Matchers.prototype.toContain = function(expected) { 1452 return this.env.contains_(this.actual, expected); 1453 }; 1454 1455 /** 1456 * Matcher that checks that the expected item is NOT an element in the actual Array. 1457 * 1458 * @param {Object} expected 1459 * @deprecated as of 1.0. Use not.toContain() instead. 1460 */ 1461 jasmine.Matchers.prototype.toNotContain = function(expected) { 1462 return !this.env.contains_(this.actual, expected); 1463 }; 1464 1465 jasmine.Matchers.prototype.toBeLessThan = function(expected) { 1466 return this.actual < expected; 1467 }; 1468 1469 jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { 1470 return this.actual > expected; 1471 }; 1472 1473 /** 1474 * Matcher that checks that the expected item is equal to the actual item 1475 * up to a given level of decimal precision (default 2). 1476 * 1477 * @param {Number} expected 1478 * @param {Number} precision, as number of decimal places 1479 */ 1480 jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { 1481 if (!(precision === 0)) { 1482 precision = precision || 2; 1483 } 1484 return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2); 1485 }; 1486 1487 /** 1488 * Matcher that checks that the expected exception was thrown by the actual. 1489 * 1490 * @param {String} [expected] 1491 */ 1492 jasmine.Matchers.prototype.toThrow = function(expected) { 1493 var result = false; 1494 var exception; 1495 if (typeof this.actual != 'function') { 1496 throw new Error('Actual is not a function'); 1497 } 1498 try { 1499 this.actual(); 1500 } catch (e) { 1501 exception = e; 1502 } 1503 if (exception) { 1504 result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); 1505 } 1506 1507 var not = this.isNot ? "not " : ""; 1508 1509 this.message = function() { 1510 if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { 1511 return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); 1512 } else { 1513 return "Expected function to throw an exception."; 1514 } 1515 }; 1516 1517 return result; 1518 }; 1519 1520 jasmine.Matchers.Any = function(expectedClass) { 1521 this.expectedClass = expectedClass; 1522 }; 1523 1524 jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { 1525 if (this.expectedClass == String) { 1526 return typeof other == 'string' || other instanceof String; 1527 } 1528 1529 if (this.expectedClass == Number) { 1530 return typeof other == 'number' || other instanceof Number; 1531 } 1532 1533 if (this.expectedClass == Function) { 1534 return typeof other == 'function' || other instanceof Function; 1535 } 1536 1537 if (this.expectedClass == Object) { 1538 return typeof other == 'object'; 1539 } 1540 1541 return other instanceof this.expectedClass; 1542 }; 1543 1544 jasmine.Matchers.Any.prototype.jasmineToString = function() { 1545 return '<jasmine.any(' + this.expectedClass + ')>'; 1546 }; 1547 1548 jasmine.Matchers.ObjectContaining = function (sample) { 1549 this.sample = sample; 1550 }; 1551 1552 jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { 1553 mismatchKeys = mismatchKeys || []; 1554 mismatchValues = mismatchValues || []; 1555 1556 var env = jasmine.getEnv(); 1557 1558 var hasKey = function(obj, keyName) { 1559 return obj != null && obj[keyName] !== jasmine.undefined; 1560 }; 1561 1562 for (var property in this.sample) { 1563 if (!hasKey(other, property) && hasKey(this.sample, property)) { 1564 mismatchKeys.push("expected has key '" + property + "', but missing from actual."); 1565 } 1566 else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { 1567 mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); 1568 } 1569 } 1570 1571 return (mismatchKeys.length === 0 && mismatchValues.length === 0); 1572 }; 1573 1574 jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { 1575 return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>"; 1576 }; 1577 // Mock setTimeout, clearTimeout 1578 // Contributed by Pivotal Computer Systems, www.pivotalsf.com 1579 1580 jasmine.FakeTimer = function() { 1581 this.reset(); 1582 1583 var self = this; 1584 self.setTimeout = function(funcToCall, millis) { 1585 self.timeoutsMade++; 1586 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); 1587 return self.timeoutsMade; 1588 }; 1589 1590 self.setInterval = function(funcToCall, millis) { 1591 self.timeoutsMade++; 1592 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); 1593 return self.timeoutsMade; 1594 }; 1595 1596 self.clearTimeout = function(timeoutKey) { 1597 self.scheduledFunctions[timeoutKey] = jasmine.undefined; 1598 }; 1599 1600 self.clearInterval = function(timeoutKey) { 1601 self.scheduledFunctions[timeoutKey] = jasmine.undefined; 1602 }; 1603 1604 }; 1605 1606 jasmine.FakeTimer.prototype.reset = function() { 1607 this.timeoutsMade = 0; 1608 this.scheduledFunctions = {}; 1609 this.nowMillis = 0; 1610 }; 1611 1612 jasmine.FakeTimer.prototype.tick = function(millis) { 1613 var oldMillis = this.nowMillis; 1614 var newMillis = oldMillis + millis; 1615 this.runFunctionsWithinRange(oldMillis, newMillis); 1616 this.nowMillis = newMillis; 1617 }; 1618 1619 jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { 1620 var scheduledFunc; 1621 var funcsToRun = []; 1622 for (var timeoutKey in this.scheduledFunctions) { 1623 scheduledFunc = this.scheduledFunctions[timeoutKey]; 1624 if (scheduledFunc != jasmine.undefined && 1625 scheduledFunc.runAtMillis >= oldMillis && 1626 scheduledFunc.runAtMillis <= nowMillis) { 1627 funcsToRun.push(scheduledFunc); 1628 this.scheduledFunctions[timeoutKey] = jasmine.undefined; 1629 } 1630 } 1631 1632 if (funcsToRun.length > 0) { 1633 funcsToRun.sort(function(a, b) { 1634 return a.runAtMillis - b.runAtMillis; 1635 }); 1636 for (var i = 0; i < funcsToRun.length; ++i) { 1637 try { 1638 var funcToRun = funcsToRun[i]; 1639 this.nowMillis = funcToRun.runAtMillis; 1640 funcToRun.funcToCall(); 1641 if (funcToRun.recurring) { 1642 this.scheduleFunction(funcToRun.timeoutKey, 1643 funcToRun.funcToCall, 1644 funcToRun.millis, 1645 true); 1646 } 1647 } catch(e) { 1648 } 1649 } 1650 this.runFunctionsWithinRange(oldMillis, nowMillis); 1651 } 1652 }; 1653 1654 jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { 1655 this.scheduledFunctions[timeoutKey] = { 1656 runAtMillis: this.nowMillis + millis, 1657 funcToCall: funcToCall, 1658 recurring: recurring, 1659 timeoutKey: timeoutKey, 1660 millis: millis 1661 }; 1662 }; 1663 1664 /** 1665 * @namespace 1666 */ 1667 jasmine.Clock = { 1668 defaultFakeTimer: new jasmine.FakeTimer(), 1669 1670 reset: function() { 1671 jasmine.Clock.assertInstalled(); 1672 jasmine.Clock.defaultFakeTimer.reset(); 1673 }, 1674 1675 tick: function(millis) { 1676 jasmine.Clock.assertInstalled(); 1677 jasmine.Clock.defaultFakeTimer.tick(millis); 1678 }, 1679 1680 runFunctionsWithinRange: function(oldMillis, nowMillis) { 1681 jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); 1682 }, 1683 1684 scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { 1685 jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); 1686 }, 1687 1688 useMock: function() { 1689 if (!jasmine.Clock.isInstalled()) { 1690 var spec = jasmine.getEnv().currentSpec; 1691 spec.after(jasmine.Clock.uninstallMock); 1692 1693 jasmine.Clock.installMock(); 1694 } 1695 }, 1696 1697 installMock: function() { 1698 jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; 1699 }, 1700 1701 uninstallMock: function() { 1702 jasmine.Clock.assertInstalled(); 1703 jasmine.Clock.installed = jasmine.Clock.real; 1704 }, 1705 1706 real: { 1707 setTimeout: jasmine.getGlobal().setTimeout, 1708 clearTimeout: jasmine.getGlobal().clearTimeout, 1709 setInterval: jasmine.getGlobal().setInterval, 1710 clearInterval: jasmine.getGlobal().clearInterval 1711 }, 1712 1713 assertInstalled: function() { 1714 if (!jasmine.Clock.isInstalled()) { 1715 throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); 1716 } 1717 }, 1718 1719 isInstalled: function() { 1720 return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; 1721 }, 1722 1723 installed: null 1724 }; 1725 jasmine.Clock.installed = jasmine.Clock.real; 1726 1727 //else for IE support 1728 jasmine.getGlobal().setTimeout = function(funcToCall, millis) { 1729 if (jasmine.Clock.installed.setTimeout.apply) { 1730 return jasmine.Clock.installed.setTimeout.apply(this, arguments); 1731 } else { 1732 return jasmine.Clock.installed.setTimeout(funcToCall, millis); 1733 } 1734 }; 1735 1736 jasmine.getGlobal().setInterval = function(funcToCall, millis) { 1737 if (jasmine.Clock.installed.setInterval.apply) { 1738 return jasmine.Clock.installed.setInterval.apply(this, arguments); 1739 } else { 1740 return jasmine.Clock.installed.setInterval(funcToCall, millis); 1741 } 1742 }; 1743 1744 jasmine.getGlobal().clearTimeout = function(timeoutKey) { 1745 if (jasmine.Clock.installed.clearTimeout.apply) { 1746 return jasmine.Clock.installed.clearTimeout.apply(this, arguments); 1747 } else { 1748 return jasmine.Clock.installed.clearTimeout(timeoutKey); 1749 } 1750 }; 1751 1752 jasmine.getGlobal().clearInterval = function(timeoutKey) { 1753 if (jasmine.Clock.installed.clearTimeout.apply) { 1754 return jasmine.Clock.installed.clearInterval.apply(this, arguments); 1755 } else { 1756 return jasmine.Clock.installed.clearInterval(timeoutKey); 1757 } 1758 }; 1759 1760 /** 1761 * @constructor 1762 */ 1763 jasmine.MultiReporter = function() { 1764 this.subReporters_ = []; 1765 }; 1766 jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); 1767 1768 jasmine.MultiReporter.prototype.addReporter = function(reporter) { 1769 this.subReporters_.push(reporter); 1770 }; 1771 1772 (function() { 1773 var functionNames = [ 1774 "reportRunnerStarting", 1775 "reportRunnerResults", 1776 "reportSuiteResults", 1777 "reportSpecStarting", 1778 "reportSpecResults", 1779 "log" 1780 ]; 1781 for (var i = 0; i < functionNames.length; i++) { 1782 var functionName = functionNames[i]; 1783 jasmine.MultiReporter.prototype[functionName] = (function(functionName) { 1784 return function() { 1785 for (var j = 0; j < this.subReporters_.length; j++) { 1786 var subReporter = this.subReporters_[j]; 1787 if (subReporter[functionName]) { 1788 subReporter[functionName].apply(subReporter, arguments); 2294 2295 if (errorType && regexp) { 2296 if (thrown.constructor == errorType && regexp.test(thrown.message)) { 2297 return pass("Expected function not to throw " + name + " with message matching " + regexp + "."); 2298 } else { 2299 return fail("Expected function to throw " + name + " with message matching " + regexp + 2300 ", but it threw " + constructorName + " with message \"" + thrown.message + "\"."); 1789 2301 } 1790 2302 } 1791 }; 1792 })(functionName); 1793 } 1794 })(); 1795 /** 1796 * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults 1797 * 1798 * @constructor 1799 */ 1800 jasmine.NestedResults = function() { 1801 /** 1802 * The total count of results 1803 */ 1804 this.totalCount = 0; 1805 /** 1806 * Number of passed results 1807 */ 1808 this.passedCount = 0; 1809 /** 1810 * Number of failed results 1811 */ 1812 this.failedCount = 0; 1813 /** 1814 * Was this suite/spec skipped? 1815 */ 1816 this.skipped = false; 1817 /** 1818 * @ignore 1819 */ 1820 this.items_ = []; 1821 }; 1822 1823 /** 1824 * Roll up the result counts. 1825 * 1826 * @param result 1827 */ 1828 jasmine.NestedResults.prototype.rollupCounts = function(result) { 1829 this.totalCount += result.totalCount; 1830 this.passedCount += result.passedCount; 1831 this.failedCount += result.failedCount; 1832 }; 1833 1834 /** 1835 * Adds a log message. 1836 * @param values Array of message parts which will be concatenated later. 1837 */ 1838 jasmine.NestedResults.prototype.log = function(values) { 1839 this.items_.push(new jasmine.MessageResult(values)); 1840 }; 1841 1842 /** 1843 * Getter for the results: message & results. 1844 */ 1845 jasmine.NestedResults.prototype.getItems = function() { 1846 return this.items_; 1847 }; 1848 1849 /** 1850 * Adds a result, tracking counts (total, passed, & failed) 1851 * @param {jasmine.ExpectationResult|jasmine.NestedResults} result 1852 */ 1853 jasmine.NestedResults.prototype.addResult = function(result) { 1854 if (result.type != 'log') { 1855 if (result.items_) { 1856 this.rollupCounts(result); 1857 } else { 1858 this.totalCount++; 1859 if (result.passed()) { 1860 this.passedCount++; 1861 } else { 1862 this.failedCount++; 1863 } 1864 } 1865 } 1866 this.items_.push(result); 1867 }; 1868 1869 /** 1870 * @returns {Boolean} True if <b>everything</b> below passed 1871 */ 1872 jasmine.NestedResults.prototype.passed = function() { 1873 return this.passedCount === this.totalCount; 1874 }; 1875 /** 1876 * Base class for pretty printing for expectation results. 1877 */ 1878 jasmine.PrettyPrinter = function() { 1879 this.ppNestLevel_ = 0; 1880 }; 1881 1882 /** 1883 * Formats a value in a nice, human-readable string. 1884 * 1885 * @param value 1886 */ 1887 jasmine.PrettyPrinter.prototype.format = function(value) { 1888 this.ppNestLevel_++; 1889 try { 1890 if (value === jasmine.undefined) { 1891 this.emitScalar('undefined'); 1892 } else if (value === null) { 1893 this.emitScalar('null'); 1894 } else if (value === jasmine.getGlobal()) { 1895 this.emitScalar('<global>'); 1896 } else if (value.jasmineToString) { 1897 this.emitScalar(value.jasmineToString()); 1898 } else if (typeof value === 'string') { 1899 this.emitString(value); 1900 } else if (jasmine.isSpy(value)) { 1901 this.emitScalar("spy on " + value.identity); 1902 } else if (value instanceof RegExp) { 1903 this.emitScalar(value.toString()); 1904 } else if (typeof value === 'function') { 1905 this.emitScalar('Function'); 1906 } else if (typeof value.nodeType === 'number') { 1907 this.emitScalar('HTMLNode'); 1908 } else if (value instanceof Date) { 1909 this.emitScalar('Date(' + value + ')'); 1910 } else if (value.__Jasmine_been_here_before__) { 1911 this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>'); 1912 } else if (jasmine.isArray_(value) || typeof value == 'object') { 1913 value.__Jasmine_been_here_before__ = true; 1914 if (jasmine.isArray_(value)) { 1915 this.emitArray(value); 1916 } else { 1917 this.emitObject(value); 1918 } 1919 delete value.__Jasmine_been_here_before__; 1920 } else { 1921 this.emitScalar(value.toString()); 1922 } 1923 } finally { 1924 this.ppNestLevel_--; 1925 } 1926 }; 1927 1928 jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { 1929 for (var property in obj) { 1930 if (!obj.hasOwnProperty(property)) continue; 1931 if (property == '__Jasmine_been_here_before__') continue; 1932 fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 1933 obj.__lookupGetter__(property) !== null) : false); 1934 } 1935 }; 1936 1937 jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; 1938 jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; 1939 jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; 1940 jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; 1941 1942 jasmine.StringPrettyPrinter = function() { 1943 jasmine.PrettyPrinter.call(this); 1944 1945 this.string = ''; 1946 }; 1947 jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); 1948 1949 jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { 1950 this.append(value); 1951 }; 1952 1953 jasmine.StringPrettyPrinter.prototype.emitString = function(value) { 1954 this.append("'" + value + "'"); 1955 }; 1956 1957 jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { 1958 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { 1959 this.append("Array"); 1960 return; 1961 } 1962 1963 this.append('[ '); 1964 for (var i = 0; i < array.length; i++) { 1965 if (i > 0) { 1966 this.append(', '); 1967 } 1968 this.format(array[i]); 1969 } 1970 this.append(' ]'); 1971 }; 1972 1973 jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { 1974 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { 1975 this.append("Object"); 1976 return; 1977 } 1978 1979 var self = this; 1980 this.append('{ '); 1981 var first = true; 1982 1983 this.iterateObject(obj, function(property, isGetter) { 1984 if (first) { 1985 first = false; 1986 } else { 1987 self.append(', '); 1988 } 1989 1990 self.append(property); 1991 self.append(' : '); 1992 if (isGetter) { 1993 self.append('<getter>'); 1994 } else { 1995 self.format(obj[property]); 1996 } 1997 }); 1998 1999 this.append(' }'); 2000 }; 2001 2002 jasmine.StringPrettyPrinter.prototype.append = function(value) { 2003 this.string += value; 2004 }; 2005 jasmine.Queue = function(env) { 2006 this.env = env; 2007 2008 // parallel to blocks. each true value in this array means the block will 2009 // get executed even if we abort 2010 this.ensured = []; 2011 this.blocks = []; 2012 this.running = false; 2013 this.index = 0; 2014 this.offset = 0; 2015 this.abort = false; 2016 }; 2017 2018 jasmine.Queue.prototype.addBefore = function(block, ensure) { 2019 if (ensure === jasmine.undefined) { 2020 ensure = false; 2021 } 2022 2023 this.blocks.unshift(block); 2024 this.ensured.unshift(ensure); 2025 }; 2026 2027 jasmine.Queue.prototype.add = function(block, ensure) { 2028 if (ensure === jasmine.undefined) { 2029 ensure = false; 2030 } 2031 2032 this.blocks.push(block); 2033 this.ensured.push(ensure); 2034 }; 2035 2036 jasmine.Queue.prototype.insertNext = function(block, ensure) { 2037 if (ensure === jasmine.undefined) { 2038 ensure = false; 2039 } 2040 2041 this.ensured.splice((this.index + this.offset + 1), 0, ensure); 2042 this.blocks.splice((this.index + this.offset + 1), 0, block); 2043 this.offset++; 2044 }; 2045 2046 jasmine.Queue.prototype.start = function(onComplete) { 2047 this.running = true; 2048 this.onComplete = onComplete; 2049 this.next_(); 2050 }; 2051 2052 jasmine.Queue.prototype.isRunning = function() { 2053 return this.running; 2054 }; 2055 2056 jasmine.Queue.LOOP_DONT_RECURSE = true; 2057 2058 jasmine.Queue.prototype.next_ = function() { 2059 var self = this; 2060 var goAgain = true; 2061 2062 while (goAgain) { 2063 goAgain = false; 2064 2065 if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { 2066 var calledSynchronously = true; 2067 var completedSynchronously = false; 2068 2069 var onComplete = function () { 2070 if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { 2071 completedSynchronously = true; 2072 return; 2073 } 2074 2075 if (self.blocks[self.index].abort) { 2076 self.abort = true; 2077 } 2078 2079 self.offset = 0; 2080 self.index++; 2081 2082 var now = new Date().getTime(); 2083 if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { 2084 self.env.lastUpdate = now; 2085 self.env.setTimeout(function() { 2086 self.next_(); 2087 }, 0); 2088 } else { 2089 if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { 2090 goAgain = true; 2303 2304 if (errorType) { 2305 if (thrown.constructor == errorType) { 2306 return pass("Expected function not to throw " + name + "."); 2091 2307 } else { 2092 self.next_();2308 return fail("Expected function to throw " + name + ", but it threw " + constructorName + "."); 2093 2309 } 2094 2310 } 2095 }; 2096 self.blocks[self.index].execute(onComplete); 2097 2098 calledSynchronously = false; 2099 if (completedSynchronously) { 2100 onComplete(); 2101 } 2102 2103 } else { 2104 self.running = false; 2105 if (self.onComplete) { 2106 self.onComplete(); 2107 } 2108 } 2109 } 2110 }; 2111 2112 jasmine.Queue.prototype.results = function() { 2113 var results = new jasmine.NestedResults(); 2114 for (var i = 0; i < this.blocks.length; i++) { 2115 if (this.blocks[i].results) { 2116 results.addResult(this.blocks[i].results()); 2117 } 2118 } 2119 return results; 2120 }; 2121 2122 2123 /** 2124 * Runner 2125 * 2126 * @constructor 2127 * @param {jasmine.Env} env 2128 */ 2129 jasmine.Runner = function(env) { 2130 var self = this; 2131 self.env = env; 2132 self.queue = new jasmine.Queue(env); 2133 self.before_ = []; 2134 self.after_ = []; 2135 self.suites_ = []; 2136 }; 2137 2138 jasmine.Runner.prototype.execute = function() { 2139 var self = this; 2140 if (self.env.reporter.reportRunnerStarting) { 2141 self.env.reporter.reportRunnerStarting(this); 2142 } 2143 self.queue.start(function () { 2144 self.finishCallback(); 2145 }); 2146 }; 2147 2148 jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { 2149 beforeEachFunction.typeName = 'beforeEach'; 2150 this.before_.splice(0,0,beforeEachFunction); 2151 }; 2152 2153 jasmine.Runner.prototype.afterEach = function(afterEachFunction) { 2154 afterEachFunction.typeName = 'afterEach'; 2155 this.after_.splice(0,0,afterEachFunction); 2156 }; 2157 2158 2159 jasmine.Runner.prototype.finishCallback = function() { 2160 this.env.reporter.reportRunnerResults(this); 2161 }; 2162 2163 jasmine.Runner.prototype.addSuite = function(suite) { 2164 this.suites_.push(suite); 2165 }; 2166 2167 jasmine.Runner.prototype.add = function(block) { 2168 if (block instanceof jasmine.Suite) { 2169 this.addSuite(block); 2170 } 2171 this.queue.add(block); 2172 }; 2173 2174 jasmine.Runner.prototype.specs = function () { 2175 var suites = this.suites(); 2176 var specs = []; 2177 for (var i = 0; i < suites.length; i++) { 2178 specs = specs.concat(suites[i].specs()); 2179 } 2180 return specs; 2181 }; 2182 2183 jasmine.Runner.prototype.suites = function() { 2184 return this.suites_; 2185 }; 2186 2187 jasmine.Runner.prototype.topLevelSuites = function() { 2188 var topLevelSuites = []; 2189 for (var i = 0; i < this.suites_.length; i++) { 2190 if (!this.suites_[i].parentSuite) { 2191 topLevelSuites.push(this.suites_[i]); 2192 } 2193 } 2194 return topLevelSuites; 2195 }; 2196 2197 jasmine.Runner.prototype.results = function() { 2198 return this.queue.results(); 2199 }; 2200 /** 2201 * Internal representation of a Jasmine specification, or test. 2202 * 2203 * @constructor 2204 * @param {jasmine.Env} env 2205 * @param {jasmine.Suite} suite 2206 * @param {String} description 2207 */ 2208 jasmine.Spec = function(env, suite, description) { 2209 if (!env) { 2210 throw new Error('jasmine.Env() required'); 2211 } 2212 if (!suite) { 2213 throw new Error('jasmine.Suite() required'); 2214 } 2215 var spec = this; 2216 spec.id = env.nextSpecId ? env.nextSpecId() : null; 2217 spec.env = env; 2218 spec.suite = suite; 2219 spec.description = description; 2220 spec.queue = new jasmine.Queue(env); 2221 2222 spec.afterCallbacks = []; 2223 spec.spies_ = []; 2224 2225 spec.results_ = new jasmine.NestedResults(); 2226 spec.results_.description = description; 2227 spec.matchersClass = null; 2228 }; 2229 2230 jasmine.Spec.prototype.getFullName = function() { 2231 return this.suite.getFullName() + ' ' + this.description + '.'; 2232 }; 2233 2234 2235 jasmine.Spec.prototype.results = function() { 2236 return this.results_; 2237 }; 2238 2239 /** 2240 * All parameters are pretty-printed and concatenated together, then written to the spec's output. 2241 * 2242 * Be careful not to leave calls to <code>jasmine.log</code> in production code. 2243 */ 2244 jasmine.Spec.prototype.log = function() { 2245 return this.results_.log(arguments); 2246 }; 2247 2248 jasmine.Spec.prototype.runs = function (func) { 2249 var block = new jasmine.Block(this.env, func, this); 2250 this.addToQueue(block); 2251 return this; 2252 }; 2253 2254 jasmine.Spec.prototype.addToQueue = function (block) { 2255 if (this.queue.isRunning()) { 2256 this.queue.insertNext(block); 2257 } else { 2258 this.queue.add(block); 2259 } 2260 }; 2261 2262 /** 2263 * @param {jasmine.ExpectationResult} result 2264 */ 2265 jasmine.Spec.prototype.addMatcherResult = function(result) { 2266 this.results_.addResult(result); 2267 }; 2268 2269 jasmine.Spec.prototype.expect = function(actual) { 2270 var positive = new (this.getMatchersClass_())(this.env, actual, this); 2271 positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); 2272 return positive; 2273 }; 2274 2275 /** 2276 * Waits a fixed time period before moving to the next block. 2277 * 2278 * @deprecated Use waitsFor() instead 2279 * @param {Number} timeout milliseconds to wait 2280 */ 2281 jasmine.Spec.prototype.waits = function(timeout) { 2282 var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); 2283 this.addToQueue(waitsFunc); 2284 return this; 2285 }; 2286 2287 /** 2288 * Waits for the latchFunction to return true before proceeding to the next block. 2289 * 2290 * @param {Function} latchFunction 2291 * @param {String} optional_timeoutMessage 2292 * @param {Number} optional_timeout 2293 */ 2294 jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 2295 var latchFunction_ = null; 2296 var optional_timeoutMessage_ = null; 2297 var optional_timeout_ = null; 2298 2299 for (var i = 0; i < arguments.length; i++) { 2300 var arg = arguments[i]; 2301 switch (typeof arg) { 2302 case 'function': 2303 latchFunction_ = arg; 2304 break; 2305 case 'string': 2306 optional_timeoutMessage_ = arg; 2307 break; 2308 case 'number': 2309 optional_timeout_ = arg; 2310 break; 2311 } 2312 } 2313 2314 var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); 2315 this.addToQueue(waitsForFunc); 2316 return this; 2317 }; 2318 2319 jasmine.Spec.prototype.fail = function (e) { 2320 var expectationResult = new jasmine.ExpectationResult({ 2321 passed: false, 2322 message: e ? jasmine.util.formatException(e) : 'Exception', 2323 trace: { stack: e.stack } 2324 }); 2325 this.results_.addResult(expectationResult); 2326 }; 2327 2328 jasmine.Spec.prototype.getMatchersClass_ = function() { 2329 return this.matchersClass || this.env.matchersClass; 2330 }; 2331 2332 jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { 2333 var parent = this.getMatchersClass_(); 2334 var newMatchersClass = function() { 2335 parent.apply(this, arguments); 2336 }; 2337 jasmine.util.inherit(newMatchersClass, parent); 2338 jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); 2339 this.matchersClass = newMatchersClass; 2340 }; 2341 2342 jasmine.Spec.prototype.finishCallback = function() { 2343 this.env.reporter.reportSpecResults(this); 2344 }; 2345 2346 jasmine.Spec.prototype.finish = function(onComplete) { 2347 this.removeAllSpies(); 2348 this.finishCallback(); 2349 if (onComplete) { 2350 onComplete(); 2351 } 2352 }; 2353 2354 jasmine.Spec.prototype.after = function(doAfter) { 2355 if (this.queue.isRunning()) { 2356 this.queue.add(new jasmine.Block(this.env, doAfter, this), true); 2357 } else { 2358 this.afterCallbacks.unshift(doAfter); 2359 } 2360 }; 2361 2362 jasmine.Spec.prototype.execute = function(onComplete) { 2363 var spec = this; 2364 if (!spec.env.specFilter(spec)) { 2365 spec.results_.skipped = true; 2366 spec.finish(onComplete); 2367 return; 2368 } 2369 2370 this.env.reporter.reportSpecStarting(this); 2371 2372 spec.env.currentSpec = spec; 2373 2374 spec.addBeforesAndAftersToQueue(); 2375 2376 spec.queue.start(function () { 2377 spec.finish(onComplete); 2378 }); 2379 }; 2380 2381 jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { 2382 var runner = this.env.currentRunner(); 2383 var i; 2384 2385 for (var suite = this.suite; suite; suite = suite.parentSuite) { 2386 for (i = 0; i < suite.before_.length; i++) { 2387 this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); 2388 } 2389 } 2390 for (i = 0; i < runner.before_.length; i++) { 2391 this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); 2392 } 2393 for (i = 0; i < this.afterCallbacks.length; i++) { 2394 this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true); 2395 } 2396 for (suite = this.suite; suite; suite = suite.parentSuite) { 2397 for (i = 0; i < suite.after_.length; i++) { 2398 this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true); 2399 } 2400 } 2401 for (i = 0; i < runner.after_.length; i++) { 2402 this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true); 2403 } 2404 }; 2405 2406 jasmine.Spec.prototype.explodes = function() { 2407 throw 'explodes function should not have been called'; 2408 }; 2409 2410 jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { 2411 if (obj == jasmine.undefined) { 2412 throw "spyOn could not find an object to spy upon for " + methodName + "()"; 2413 } 2414 2415 if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { 2416 throw methodName + '() method does not exist'; 2417 } 2418 2419 if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { 2420 throw new Error(methodName + ' has already been spied upon'); 2421 } 2422 2423 var spyObj = jasmine.createSpy(methodName); 2424 2425 this.spies_.push(spyObj); 2426 spyObj.baseObj = obj; 2427 spyObj.methodName = methodName; 2428 spyObj.originalValue = obj[methodName]; 2429 2430 obj[methodName] = spyObj; 2431 2432 return spyObj; 2433 }; 2434 2435 jasmine.Spec.prototype.removeAllSpies = function() { 2436 for (var i = 0; i < this.spies_.length; i++) { 2437 var spy = this.spies_[i]; 2438 spy.baseObj[spy.methodName] = spy.originalValue; 2439 } 2440 this.spies_ = []; 2441 }; 2442 2443 /** 2444 * Internal representation of a Jasmine suite. 2445 * 2446 * @constructor 2447 * @param {jasmine.Env} env 2448 * @param {String} description 2449 * @param {Function} specDefinitions 2450 * @param {jasmine.Suite} parentSuite 2451 */ 2452 jasmine.Suite = function(env, description, specDefinitions, parentSuite) { 2453 var self = this; 2454 self.id = env.nextSuiteId ? env.nextSuiteId() : null; 2455 self.description = description; 2456 self.queue = new jasmine.Queue(env); 2457 self.parentSuite = parentSuite; 2458 self.env = env; 2459 self.before_ = []; 2460 self.after_ = []; 2461 self.children_ = []; 2462 self.suites_ = []; 2463 self.specs_ = []; 2464 }; 2465 2466 jasmine.Suite.prototype.getFullName = function() { 2467 var fullName = this.description; 2468 for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { 2469 fullName = parentSuite.description + ' ' + fullName; 2470 } 2471 return fullName; 2472 }; 2473 2474 jasmine.Suite.prototype.finish = function(onComplete) { 2475 this.env.reporter.reportSuiteResults(this); 2476 this.finished = true; 2477 if (typeof(onComplete) == 'function') { 2478 onComplete(); 2479 } 2480 }; 2481 2482 jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { 2483 beforeEachFunction.typeName = 'beforeEach'; 2484 this.before_.unshift(beforeEachFunction); 2485 }; 2486 2487 jasmine.Suite.prototype.afterEach = function(afterEachFunction) { 2488 afterEachFunction.typeName = 'afterEach'; 2489 this.after_.unshift(afterEachFunction); 2490 }; 2491 2492 jasmine.Suite.prototype.results = function() { 2493 return this.queue.results(); 2494 }; 2495 2496 jasmine.Suite.prototype.add = function(suiteOrSpec) { 2497 this.children_.push(suiteOrSpec); 2498 if (suiteOrSpec instanceof jasmine.Suite) { 2499 this.suites_.push(suiteOrSpec); 2500 this.env.currentRunner().addSuite(suiteOrSpec); 2501 } else { 2502 this.specs_.push(suiteOrSpec); 2503 } 2504 this.queue.add(suiteOrSpec); 2505 }; 2506 2507 jasmine.Suite.prototype.specs = function() { 2508 return this.specs_; 2509 }; 2510 2511 jasmine.Suite.prototype.suites = function() { 2512 return this.suites_; 2513 }; 2514 2515 jasmine.Suite.prototype.children = function() { 2516 return this.children_; 2517 }; 2518 2519 jasmine.Suite.prototype.execute = function(onComplete) { 2520 var self = this; 2521 this.queue.start(function () { 2522 self.finish(onComplete); 2523 }); 2524 }; 2525 jasmine.WaitsBlock = function(env, timeout, spec) { 2526 this.timeout = timeout; 2527 jasmine.Block.call(this, env, null, spec); 2528 }; 2529 2530 jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); 2531 2532 jasmine.WaitsBlock.prototype.execute = function (onComplete) { 2533 if (jasmine.VERBOSE) { 2534 this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); 2535 } 2536 this.env.setTimeout(function () { 2537 onComplete(); 2538 }, this.timeout); 2539 }; 2540 /** 2541 * A block which waits for some condition to become true, with timeout. 2542 * 2543 * @constructor 2544 * @extends jasmine.Block 2545 * @param {jasmine.Env} env The Jasmine environment. 2546 * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. 2547 * @param {Function} latchFunction A function which returns true when the desired condition has been met. 2548 * @param {String} message The message to display if the desired condition hasn't been met within the given time period. 2549 * @param {jasmine.Spec} spec The Jasmine spec. 2550 */ 2551 jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { 2552 this.timeout = timeout || env.defaultTimeoutInterval; 2553 this.latchFunction = latchFunction; 2554 this.message = message; 2555 this.totalTimeSpentWaitingForLatch = 0; 2556 jasmine.Block.call(this, env, null, spec); 2557 }; 2558 jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); 2559 2560 jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; 2561 2562 jasmine.WaitsForBlock.prototype.execute = function(onComplete) { 2563 if (jasmine.VERBOSE) { 2564 this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); 2565 } 2566 var latchFunctionResult; 2567 try { 2568 latchFunctionResult = this.latchFunction.apply(this.spec); 2569 } catch (e) { 2570 this.spec.fail(e); 2571 onComplete(); 2572 return; 2573 } 2574 2575 if (latchFunctionResult) { 2576 onComplete(); 2577 } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { 2578 var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); 2579 this.spec.fail({ 2580 name: 'timeout', 2581 message: message 2582 }); 2583 2584 this.abort = true; 2585 onComplete(); 2586 } else { 2587 this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; 2588 var self = this; 2589 this.env.setTimeout(function() { 2590 self.execute(onComplete); 2591 }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); 2592 } 2593 }; 2594 2595 jasmine.version_= { 2596 "major": 1, 2597 "minor": 3, 2598 "build": 1, 2599 "revision": 1354556913 2600 }; 2311 2312 if (message) { 2313 if (thrown.message == message) { 2314 return pass("Expected function not to throw an exception with message " + j$.pp(message) + "."); 2315 } else { 2316 return fail("Expected function to throw an exception with message " + j$.pp(message) + 2317 ", but it threw an exception with message " + j$.pp(thrown.message) + "."); 2318 } 2319 } 2320 2321 if (regexp) { 2322 if (regexp.test(thrown.message)) { 2323 return pass("Expected function not to throw an exception with a message matching " + j$.pp(regexp) + "."); 2324 } else { 2325 return fail("Expected function to throw an exception with a message matching " + j$.pp(regexp) + 2326 ", but it threw an exception with message " + j$.pp(thrown.message) + "."); 2327 } 2328 } 2329 2330 function fnNameFor(func) { 2331 return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1]; 2332 } 2333 2334 function pass(notMessage) { 2335 return { 2336 pass: true, 2337 message: notMessage 2338 }; 2339 } 2340 2341 function fail(message) { 2342 return { 2343 pass: false, 2344 message: message 2345 }; 2346 } 2347 2348 function extractExpectedParams() { 2349 if (arguments.length == 1) { 2350 return; 2351 } 2352 2353 if (arguments.length == 2) { 2354 var expected = arguments[1]; 2355 2356 if (expected instanceof RegExp) { 2357 regexp = expected; 2358 } else if (typeof expected == "string") { 2359 message = expected; 2360 } else if (checkForAnErrorType(expected)) { 2361 errorType = expected; 2362 } 2363 2364 if (!(errorType || message || regexp)) { 2365 throw new Error("Expected is not an Error, string, or RegExp."); 2366 } 2367 } else { 2368 if (checkForAnErrorType(arguments[1])) { 2369 errorType = arguments[1]; 2370 } else { 2371 throw new Error("Expected error type is not an Error."); 2372 } 2373 2374 if (arguments[2] instanceof RegExp) { 2375 regexp = arguments[2]; 2376 } else if (typeof arguments[2] == "string") { 2377 message = arguments[2]; 2378 } else { 2379 throw new Error("Expected error message is not a string or RegExp."); 2380 } 2381 } 2382 } 2383 2384 function checkForAnErrorType(type) { 2385 if (typeof type !== "function") { 2386 return false; 2387 } 2388 2389 var Surrogate = function() {}; 2390 Surrogate.prototype = type.prototype; 2391 return (new Surrogate()) instanceof Error; 2392 } 2393 } 2394 }; 2395 } 2396 2397 return toThrowError; 2398 }; 2399 2400 getJasmineRequireObj().version = function() { 2401 return "2.0.0"; 2402 };
Note: See TracChangeset
for help on using the changeset viewer.