├── tests ├── lib │ ├── jasmine-2.3.4 │ │ ├── jasmine_favicon.png │ │ ├── boot.js │ │ ├── console.js │ │ ├── jasmine-html.js │ │ ├── jasmine.css │ │ └── jasmine.js │ └── jasmine-jquery.js ├── SpecRunner.html └── MIT.LICENSE ├── package.json ├── examples ├── example3-display-most-recently-post.html ├── example1-display-single-post.html ├── example2-display-collections.html └── example5-singlePageApp.html ├── LICENSE ├── gulpfile.js ├── dist ├── astro-support-views.min.js └── astro-api.min.js ├── README.md └── src ├── astro-support-views.js └── astro-api.js /tests/lib/jasmine-2.3.4/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getastro/Astro/HEAD/tests/lib/jasmine-2.3.4/jasmine_favicon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-api", 3 | "version": "0.3.1", 4 | "description": "A JS library framework to render JSON content, regardless skill level.", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/getastro/Astro.git" 8 | }, 9 | "keywords": [ 10 | "Astro", 11 | "WordPress", 12 | "REST API" 13 | 14 | ], 15 | "author": "Ting Yang & Hector Jarquin", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/Ting-y/Astro/issues" 19 | }, 20 | "homepage": "https://github.com/Ting-y/Astro#readme", 21 | "devDependencies": { 22 | "gulp": "^3.9.0", 23 | "gulp-jslint": "^0.2.2", 24 | "gulp-rename": "^1.2.2", 25 | "gulp-uglify": "^1.5.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/example3-display-most-recently-post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 3 6 | 7 | 8 |
9 | 10 | 11 |
12 |
13 |

14 | 15 |
16 |
17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jasmine Spec Runner v2.3.4 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/example1-display-single-post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 1 6 | 7 | 8 |
9 | 10 |
11 |

12 |
13 |
14 |
15 | 16 |
17 |
18 |

19 | 20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2014 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, Ting Yang & Hector Jarquin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/example2-display-collections.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 |

12 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |
21 |

22 | 23 |
24 |
25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var jslint = require('gulp-jslint'); 3 | var uglify = require('gulp-uglify'); 4 | var rename = require('gulp-rename'); 5 | var files = ['src/*.js']; 6 | // build the main source into the min file 7 | gulp.task('lint', function () { 8 | 'use strict'; 9 | return gulp.src(files) 10 | .pipe(jslint({ 11 | browser: true, 12 | vars: true, 13 | for: true, 14 | white: true, 15 | newcap:true, 16 | predef: ["require", "console", "AstroWP", 17 | "ActiveXObject", "util","CustomEvent"], 18 | reporter: 'default' 19 | })) 20 | .on('error', function (error) { 21 | console.error(String(error)); 22 | }); 23 | }); 24 | 25 | gulp.task('watch', function () { 26 | 'use strict'; 27 | gulp.watch(files, ['lint']); // watching files and call the defined work flow 28 | }); 29 | 30 | // minify the js 31 | gulp.task('minify', function() { 32 | return gulp.src(files) 33 | .pipe(uglify({ 34 | preserveComments:"license", 35 | })) 36 | .pipe(rename({ 37 | suffix: '.min' 38 | })) 39 | .pipe(gulp.dest('dist')); 40 | }); 41 | 42 | gulp.task('default', ['watch']); 43 | -------------------------------------------------------------------------------- /examples/example5-singlePageApp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 |
12 |

13 | 15 |
16 |
17 |
18 | 19 |
20 |
22 |
23 |

24 |
25 |
26 |
27 |

28 |
29 |
30 |
31 | 32 | 33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /dist/astro-support-views.min.js: -------------------------------------------------------------------------------- 1 | // Astro single page app - views v0.2.0 2 | // Copyright 2016 Ting Yang and Hector Jarquin 3 | // Released under the MIT license 4 | // Last updated: February 7th, 2016 5 | // 6 | // Requirement: IE11+ 7 | // 8 | // Summary: 9 | // Turn a data-api-endpoint node to a single page application 10 | // once the item is clicked, it will render a single view 11 | // 12 | !function(t,e){function n(t){var e;t.apiElements.forEach(function(t,n){for(var r=0;r0){var o=a[a.length-1];return f.currentView=o,o}}function i(n){var r=t.querySelector(ASTRO_QUERY_DATASET.container);if(e.location.hash.length<1)null!==f.currentView&&(f.currentView.style.display="none"),l(r);else{var a=o(e.location.hash);s(a,r,n)}}function s(n,r,a){n.style.display="block",r.style.display="none",e.scrollBy(100,-e.pageYOffset);var o=e.location.hash,i=o.lastIndexOf("#"),s=o.substring(1,i);o=o.substring(i+1),a.forEach(function(t){if(t.name==s){var e=t.viewElement.querySelectorAll(ASTRO_QUERY_DATASET.property),n=u[o];A(n,e)}}),t.dispatchEvent(T)}function l(e){e.style.display="block",t.dispatchEvent(c)}function A(t,e){var n,r;for(n=0;n 29 | 30 | 31 | 32 | 33 | 34 | ``` 35 | 36 | ####Create a template to render json content 37 | 38 | **Examples** 39 | 40 | * [Display a specific post](https://github.com/Ting-y/Astro/blob/master/examples/example1-display-single-post.html) 41 | * [Display a list of post](https://github.com/Ting-y/Astro/blob/master/examples/example2-display-collections.html) 42 | * [Display the most recent post in the category](https://github.com/Ting-y/Astro/blob/master/examples/example3-display-most-recently-post.html) 43 | 44 | ####Data attributes 45 | 46 | **Summary** 47 | 48 | | Data Attribute | Required |Description | 49 | |---------------------|----------|---------------------------------------| 50 | | data-api-host | Yes | To identify the content source 51 | | data-api-endpoint | Yes | end point from API | 52 | | data-api-property | Yes | The return field you want to display| 53 | | data-api-parameters | When using query parameter | RESTful API qury parameters | 54 | | data-api-template | When displaying collection | This is required if rendering multiple posts | 55 | 56 | [Data attributes usage detail & example](https://github.com/Ting-y/Astro/wiki/Data-attributes-usage-and-explanation) 57 | 58 | ###Support: 59 | 60 | - blog on wordpress.com 61 | - self hosted(wordpress.org) with jetpack json-api plugin enabled 62 | - self hosted(wordpress.org) with WP-API version 2 enabled 63 | 64 | 65 | ###Todo: 66 | - Fix Unit tests 67 | - Accessibility 68 | 69 | ###Reference: 70 | WordPress official RESTful API endpoint 71 | [Documentaion](https://developer.wordpress.com/docs/api/) 72 | WP-API Version 2.0 endpoint 73 | [Documentaion](http://v2.wp-api.org/) 74 | 75 | ###Questions: 76 | If you have any questions about Astro, please [create a new issue](https://github.com/Ting-y/Astro/issues) or Email [Ting](mailto:ting.yatingyang@gmail.com) 77 | -------------------------------------------------------------------------------- /dist/astro-api.min.js: -------------------------------------------------------------------------------- 1 | // Astro Framework - WordPress v0.3.1 2 | // Copyright 2016 Ting Yang and Hector Jarquin 3 | // Released under the MIT license 4 | // Last updated: Feburary 15th, 2016 5 | // 6 | // Support: 7 | // WordPress.com, the official RESTful api endpoint 8 | // WordPress.org (self hosted) with Jetpack json-api plugin and WP API 9 | // 10 | // 11 | !function(e,t){"use strict";function n(t,n){n=n||{bubbles:!1,cancelable:!1,detail:void 0};var r=e.createEvent("CustomEvent");return r.initCustomEvent(t,n.bubbles,n.cancelable,n.detail),r}n.prototype=t.CustomEvent.prototype,t.CustomEvent=n}(document,window),function(e,t){"use strict";function n(){var t;return t=e.querySelectorAll(m.host),t||[]}function r(e){var t,n=[];for(t=0;t 0) { 78 | printNewline(); 79 | 80 | var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' + 81 | failureCount + ' ' + plural('failure', failureCount); 82 | 83 | if (pendingCount) { 84 | specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount); 85 | } 86 | 87 | print(specCounts); 88 | } else { 89 | print('No specs found'); 90 | } 91 | 92 | printNewline(); 93 | var seconds = timer.elapsed() / 1000; 94 | print('Finished in ' + seconds + ' ' + plural('second', seconds)); 95 | printNewline(); 96 | 97 | for(i = 0; i < failedSuites.length; i++) { 98 | suiteFailureDetails(failedSuites[i]); 99 | } 100 | 101 | onComplete(failureCount === 0); 102 | }; 103 | 104 | this.specDone = function(result) { 105 | specCount++; 106 | 107 | if (result.status == 'pending') { 108 | pendingCount++; 109 | print(colored('yellow', '*')); 110 | return; 111 | } 112 | 113 | if (result.status == 'passed') { 114 | print(colored('green', '.')); 115 | return; 116 | } 117 | 118 | if (result.status == 'failed') { 119 | failureCount++; 120 | failedSpecs.push(result); 121 | print(colored('red', 'F')); 122 | } 123 | }; 124 | 125 | this.suiteDone = function(result) { 126 | if (result.failedExpectations && result.failedExpectations.length > 0) { 127 | failureCount++; 128 | failedSuites.push(result); 129 | } 130 | }; 131 | 132 | return this; 133 | 134 | function printNewline() { 135 | print('\n'); 136 | } 137 | 138 | function colored(color, str) { 139 | return showColors ? (ansi[color] + str + ansi.none) : str; 140 | } 141 | 142 | function plural(str, count) { 143 | return count == 1 ? str : str + 's'; 144 | } 145 | 146 | function repeat(thing, times) { 147 | var arr = []; 148 | for (var i = 0; i < times; i++) { 149 | arr.push(thing); 150 | } 151 | return arr; 152 | } 153 | 154 | function indent(str, spaces) { 155 | var lines = (str || '').split('\n'); 156 | var newArr = []; 157 | for (var i = 0; i < lines.length; i++) { 158 | newArr.push(repeat(' ', spaces).join('') + lines[i]); 159 | } 160 | return newArr.join('\n'); 161 | } 162 | 163 | function specFailureDetails(result) { 164 | printNewline(); 165 | print(result.fullName); 166 | 167 | for (var i = 0; i < result.failedExpectations.length; i++) { 168 | var failedExpectation = result.failedExpectations[i]; 169 | printNewline(); 170 | print(indent(failedExpectation.message, 2)); 171 | print(indent(failedExpectation.stack, 2)); 172 | } 173 | 174 | printNewline(); 175 | } 176 | 177 | function suiteFailureDetails(result) { 178 | for (var i = 0; i < result.failedExpectations.length; i++) { 179 | printNewline(); 180 | print(colored('red', 'An error was thrown in an afterAll')); 181 | printNewline(); 182 | print(colored('red', 'AfterAll ' + result.failedExpectations[i].message)); 183 | 184 | } 185 | printNewline(); 186 | } 187 | } 188 | 189 | return ConsoleReporter; 190 | }; 191 | -------------------------------------------------------------------------------- /src/astro-support-views.js: -------------------------------------------------------------------------------- 1 | // Astro single page app - views v0.2.0 2 | // Copyright 2016 Ting Yang and Hector Jarquin 3 | // Released under the MIT license 4 | // Last updated: February 7th, 2016 5 | // 6 | // Requirement: IE11+ 7 | // 8 | // Summary: 9 | // Turn a data-api-endpoint node to a single page application 10 | // once the item is clicked, it will render a single view 11 | // 12 | (function (document, window) { 13 | // create custom event for page state 14 | var event_OnSinglePgaeView = new CustomEvent("Astro-OnSingleView"); 15 | var event_OnRepeatView = new CustomEvent("Astro-OnRepeatView"); 16 | 17 | window.ASTRO_QUERY_DATASET.view = "[data-app-view]"; // set the value 18 | window.ASTRO_QUERY_DATASET.container = "[data-app-landing]"; 19 | window.ASTRO_DATASET_ATTRIBUTE.appView = "appView"; 20 | 21 | var AstroLookUpTable = {}; 22 | 23 | var store = { 24 | currentView: null 25 | } 26 | 27 | /** 28 | * BuildAstroLoopUpTable 29 | * @summary the object holds the hash table for the key-value pair of url and post json 30 | * @param {object} AstroAPI The AstroAPI element 31 | */ 32 | function BuildAstroLoopUpTable(AstroAPI) { 33 | var articleURL; 34 | AstroAPI.apiElements.forEach(function(host, index) { 35 | // build the hash table here! 36 | for (var i = 0; i < host.jsoncontent.length; i++) { 37 | 38 | for (var j = 0; j < host.jsoncontent[i].length; j++) { 39 | // if URL property existed or link 40 | // depend on the json available property 41 | articleURL = host.jsoncontent[i][j].URL || host.jsoncontent[i][j].link; 42 | AstroLookUpTable[articleURL] = host.jsoncontent[i][j]; 43 | } 44 | } 45 | }); 46 | } 47 | 48 | /** 49 | * BluePrint 50 | * @summary This is the building plan for the views 51 | * @param {node} doc dom tree 52 | * @return {object} The object holds the avaliable views and relate elements 53 | */ 54 | function BluePrint(doc) { 55 | var appViews = []; 56 | var viewsNode = doc.querySelectorAll(ASTRO_QUERY_DATASET.view); 57 | var mainListNodes = []; // the list wpelement nodes 58 | var views = []; // the views element node. 59 | for (var i = 0; i < viewsNode.length; i++) { 60 | if (viewsNode[i].dataset[window.ASTRO_DATASET_ATTRIBUTE.endpoint]) { 61 | mainListNodes.push(viewsNode[i]); 62 | } else { 63 | views.push(viewsNode[i]); 64 | var temp = { 65 | name: viewsNode[i].dataset[window.ASTRO_DATASET_ATTRIBUTE.appView], 66 | viewElement: viewsNode[i], 67 | list: [] 68 | } 69 | appViews.push(temp); 70 | } 71 | } 72 | 73 | // return appView object 74 | for (var j = 0; j < mainListNodes.length; j++) { 75 | appViews.forEach(function (view) { 76 | if (view.name === mainListNodes[j].dataset[window.ASTRO_DATASET_ATTRIBUTE.appView]) { 77 | view.list.push(mainListNodes[j]); 78 | } 79 | }); 80 | } 81 | return appViews; 82 | } 83 | 84 | /** 85 | * InsertAnchor 86 | * @summary append the specified data-app-view value to the anchor 87 | * href = ...html/#viewname#posturl 88 | * 89 | * @param {object} appViews the blue print object 90 | */ 91 | function InsertAnchor (appViews) { 92 | for(var i = 0; i < appViews.length; i++) { 93 | for (var j = 0; j < appViews[i].list.length; j++) { 94 | var node = appViews[i].list[j]; 95 | var a = node.querySelectorAll("[data-app-anchor]"); 96 | for (var k = 0; k < a.length; k++) { 97 | a[k].setAttribute('href', "#" + node.dataset[window.ASTRO_DATASET_ATTRIBUTE.appView] + "#" 98 | + a[k].getAttribute('href')); 99 | } 100 | } 101 | } 102 | } 103 | 104 | /** 105 | * extractView 106 | * 107 | * @param {string} hash The current hash value from url 108 | * @return {node} The view node 109 | */ 110 | function extractView (hash) { 111 | var lastHashindex = hash.lastIndexOf('#'); 112 | var viewName = hash.substring(1, lastHashindex); 113 | var app_views = document.querySelectorAll("[data-app-view=" + viewName + "]"); 114 | if (app_views.length > 0) { 115 | var lastView = app_views[app_views.length - 1]; 116 | store.currentView = lastView; 117 | return lastView; 118 | } 119 | } 120 | 121 | /** 122 | * Render 123 | * @summary this will be call after the navigation state change 124 | * it will decide what to render base on the url 125 | * @param {object} bluePrint the blue print object 126 | */ 127 | function Render(bluePrint) { 128 | // this will be call when started 129 | // then decide what to do 130 | var mainView = document.querySelector(ASTRO_QUERY_DATASET.container); 131 | 132 | if (window.location.hash.length < 1) { 133 | if (store.currentView !== null) { 134 | store.currentView.style.display = "none"; 135 | } 136 | RenderMain(mainView); 137 | } else { 138 | var el = extractView(window.location.hash); 139 | RenderView(el, mainView, bluePrint); 140 | } 141 | } 142 | 143 | /** 144 | * RenderView 145 | * @summary Render the template and hide the astro list veiw 146 | * and it will dispatch an event after it render, it gives 147 | * users has oppotunities to do something when the event triggered 148 | * 149 | * @param {node} viewEl The view need to be render 150 | * @param {node} mainView the node has astro-body attribute 151 | * @param {object} bluePrint the blue print object 152 | */ 153 | function RenderView(viewEl, mainView, bluePrint) { 154 | viewEl.style.display = "block"; // show the view 155 | mainView.style.display = "none"; // hide the content in astro-body 156 | window.scrollBy(100, -window.pageYOffset); 157 | 158 | // extract the post url from current URL 159 | var urlTemp = window.location.hash; 160 | var index = urlTemp.lastIndexOf('#'); 161 | var viewName = urlTemp.substring(1, index); 162 | urlTemp = urlTemp.substring(index + 1); 163 | 164 | bluePrint.forEach(function (view) { 165 | if (view.name == viewName) { 166 | var templates = view.viewElement.querySelectorAll(ASTRO_QUERY_DATASET.property); 167 | var postJson = AstroLookUpTable[urlTemp]; 168 | RenderSinglePost(postJson, templates); 169 | } 170 | }); 171 | 172 | document.dispatchEvent(event_OnSinglePgaeView); 173 | } 174 | 175 | /** 176 | * RenderMain 177 | * 178 | * @param {node} mainView The node has astro-body 179 | */ 180 | function RenderMain(mainView) { 181 | mainView.style.display = "block"; 182 | document.dispatchEvent(event_OnRepeatView); 183 | } 184 | 185 | /** 186 | * RenderSinglePost 187 | * 188 | * @param {json object} json post json object 189 | * @param {nodelist} properties The nodes that contains [data-api-property] 190 | */ 191 | function RenderSinglePost(json, properties) { 192 | var i, content; 193 | for ( i = 0; i < properties.length; i += 1) { 194 | content = AstroAPI.Util.ExtractJsonValueByKey(properties[i].dataset[ASTRO_DATASET_ATTRIBUTE.properties], json); 195 | if (properties[i].tagName === "IMG") { 196 | properties[i].setAttribute("src", 197 | content); 198 | } else if (properties[i].tagName === "A") { 199 | properties[i].setAttribute("href", 200 | content); 201 | } else { 202 | properties[i].innerHTML = content; 203 | } 204 | } 205 | } 206 | 207 | document.addEventListener('AstroAPI-render', function (e) { 208 | if (!window.AstroAPI) { 209 | console.error("astro-wp-element.js is not found"); 210 | return; 211 | } 212 | 213 | // execute the magic 214 | BuildAstroLoopUpTable(AstroAPI); 215 | var bluePrint = BluePrint(document); 216 | InsertAnchor(bluePrint); 217 | Render(bluePrint); 218 | // when the navigation changed 219 | window.addEventListener('hashchange', function() { 220 | Render(bluePrint); 221 | }); 222 | }); 223 | }) (document, window); 224 | 225 | -------------------------------------------------------------------------------- /src/astro-api.js: -------------------------------------------------------------------------------- 1 | // Astro Framework - WordPress v0.3.1 2 | // Copyright 2016 Ting Yang and Hector Jarquin 3 | // Released under the MIT license 4 | // Last updated: Feburary 15th, 2016 5 | // 6 | // Support: 7 | // WordPress.com, the official RESTful api endpoint 8 | // WordPress.org (self hosted) with Jetpack json-api plugin and WP API 9 | // 10 | // 11 | 12 | // custom event polyfill for IE9 - IE10 13 | (function (document, window) { 14 | 'use strict'; 15 | function CustomEvent (event, params) { 16 | params = params || { bubbles: false, cancelable: false, detail: undefined }; 17 | var evt = document.createEvent('CustomEvent'); 18 | evt.initCustomEvent(event, params.bubbles, 19 | params.cancelable, params.detail); 20 | return evt; 21 | } 22 | CustomEvent.prototype = window.CustomEvent.prototype; 23 | window.CustomEvent = CustomEvent; 24 | })(document, window); 25 | 26 | (function (document, window) { 27 | 'use strict'; 28 | 29 | 30 | /** 31 | * Define the "const" variables 32 | * 33 | */ 34 | var event_FinishRequest = new CustomEvent("AstroAPI-render"); 35 | 36 | // Astro defined attribute name 37 | var ASTRO_QUERY_DATASET = { 38 | host: "[data-api-host]", 39 | endpoint: "[data-api-endpoint]", 40 | property: "[data-api-property]" 41 | }; 42 | 43 | var ASTRO_DATASET_ATTRIBUTE = { 44 | host: 'apiHost', 45 | endpoint: 'apiEndpoint', 46 | parameters: 'apiParameters', 47 | template: 'apiTemplate', 48 | properties: 'apiProperty', 49 | singlePage: 'partialView' // will be use in other 50 | }; 51 | 52 | var TEMPLATE_TYPE = /repeat|single/; 53 | 54 | var ERROR_MESSAGE = { 55 | template: "Not valid template type, the program will set the template to Single" 56 | }; 57 | 58 | /** 59 | * The global value that will insert into window object 60 | * { 61 | * apiElements: [ 62 | * { 63 | * element: [] <- NodeList 64 | * url: string <- the blog url 65 | * jsoncontent: [] <- the posts 66 | * } 67 | * ] 68 | * } 69 | * 70 | */ 71 | 72 | var AstroAPI = {}; 73 | /** 74 | * Util object contains helper functions 75 | * 76 | */ 77 | var Util = { 78 | /** 79 | * GetContentFromWordpress 80 | * 81 | * @param {string} url the api data endpoint 82 | * @param {function} callback return 2 params, err & json data 83 | */ 84 | GetContentFromWordpress: function (url, callback) { 85 | var xmlhttp; 86 | if (window.XMLHttpRequest) { 87 | // code for IE7+, Firefox, Chrome, Opera, Safari 88 | xmlhttp = new XMLHttpRequest(); 89 | } else {// code for IE6, IE5 90 | xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 91 | } 92 | 93 | xmlhttp.onreadystatechange = function () { 94 | if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { 95 | // JSON.parse will crash when has unexpected response 'json' 96 | callback(null, JSON.parse(xmlhttp.responseText)); 97 | } 98 | }; 99 | xmlhttp.open("GET", url, true); 100 | xmlhttp.send(); 101 | }, 102 | ExtractJsonValueByKey: function (param, json) { 103 | var p = param; 104 | var keys = p.split("."); 105 | 106 | for (var i = 0; i < keys.length; i+=1) { 107 | json = json[keys[i]]; 108 | 109 | } 110 | 111 | return json; 112 | 113 | } 114 | }; 115 | 116 | /** 117 | * getHosts 118 | * 119 | * @return {NodeList} return nodes that contains [data-api-host] attributes 120 | */ 121 | function getHosts () { 122 | var apiHosts; 123 | apiHosts = document.querySelectorAll(ASTRO_QUERY_DATASET.host); 124 | return apiHosts || []; 125 | } 126 | 127 | 128 | 129 | /** 130 | * getElements 131 | * 132 | * @param {nodeList} apiHosts All nodes that contains [data-api-host] attribute 133 | * @return {object} new custom object, see line 66 134 | */ 135 | function getElements (apiHosts) { 136 | var apiElements = []; 137 | 138 | var i; 139 | for (i = 0; i < apiHosts.length; i+=1) { 140 | apiElements.push( 141 | { 142 | 'url': apiHosts[i].dataset[ASTRO_DATASET_ATTRIBUTE.host], 143 | 'domElements': apiHosts[i].querySelectorAll(ASTRO_QUERY_DATASET.endpoint), 144 | 'jsoncontent': [] 145 | } 146 | ); 147 | } 148 | return apiElements; 149 | } 150 | 151 | 152 | 153 | 154 | // Element module 155 | /** 156 | * Element 157 | * 158 | * @param {string} url the blog URL from wordpress 159 | * @param {dom element} rawElement The dom element that has [data-api-endpoint] attribute 160 | * @return {{ 161 | * url The request url 162 | * template What template going to build for this element 163 | * childNode The child nodes 164 | * endPoint The endpoint of the request 165 | * elementNode The current element node 166 | * properties The childNodes that contains [data-api-property] attribute 167 | * }} some public functions 168 | */ 169 | function Element (url, rawElement) { 170 | 171 | var sourceURL = url; 172 | 173 | var dataset = rawElement.dataset; 174 | 175 | function elementType () { 176 | var elementType; 177 | if (sourceURL.search(/public-api.wordpress.com/)) { 178 | elementType = "jetpack"; 179 | } else { 180 | elementType = "wpapi"; 181 | } 182 | return elementType; 183 | } 184 | 185 | function template () { 186 | if (!dataset[ASTRO_DATASET_ATTRIBUTE.template]) { 187 | return 'single'; 188 | } 189 | if (dataset[ASTRO_DATASET_ATTRIBUTE.template].search(TEMPLATE_TYPE) !== -1) { 190 | return dataset[ASTRO_DATASET_ATTRIBUTE.template]; 191 | } 192 | 193 | //if template is not defined, we assume it means render single item 194 | } 195 | 196 | 197 | function options() { 198 | var optionAttribute = dataset[ASTRO_DATASET_ATTRIBUTE.parameters] || null; 199 | return optionAttribute; 200 | } 201 | 202 | function endPoint() { 203 | var type = dataset[ASTRO_DATASET_ATTRIBUTE.endpoint] || null; 204 | return type; 205 | } 206 | 207 | function requestURL () { 208 | var path = ''; 209 | var opts = options(); 210 | // apend a / to URL 211 | if (sourceURL.slice(-1) !== "/") { 212 | sourceURL= sourceURL + "/"; 213 | } 214 | 215 | path += sourceURL + endPoint(); 216 | 217 | if (opts !== null) { 218 | path += '?' + opts; 219 | } 220 | return path; 221 | } 222 | 223 | 224 | function childnodes () { 225 | return rawElement.querySelector('*'); 226 | } 227 | 228 | function parentNode () { 229 | return rawElement; 230 | } 231 | 232 | function properties () { 233 | return rawElement.querySelectorAll(ASTRO_QUERY_DATASET.property); 234 | } 235 | return { 236 | // public function 237 | url : requestURL, 238 | template: template, 239 | endPoint: endPoint, 240 | childnodes: childnodes, 241 | elementNode: parentNode, 242 | properties: properties, 243 | elementType: elementType 244 | }; 245 | } 246 | 247 | /** 248 | * getElementAmt 249 | * @summary This will return the # of ajax call is needed 250 | * for aync in Fetch(), need to dispatch an event 251 | * after all ajax call 252 | * 253 | * @param {object} element AstroAPI 254 | */ 255 | function getElementAmt(element) { 256 | var counter = 0; 257 | 258 | for (var i = 0; i < element.apiElements.length; i++) { 259 | counter += element.apiElements[i].domElements.length; 260 | } 261 | return counter; 262 | } 263 | /** 264 | * Init 265 | * @summary Gather the nessesery information from the nodes 266 | * and build the object see line 61, the object will 267 | * be use in Fetch() and Build() 268 | * 269 | * 270 | * 271 | */ 272 | function Init() { 273 | // find Hosts 274 | var hosts = getHosts(); 275 | var elements = getElements(hosts); 276 | AstroAPI.apiElements = elements; 277 | } 278 | 279 | 280 | /** 281 | * Fetch 282 | * @summary A step after Init(), once it got called, it will 283 | * loop throught AstroAPI.apiElements and do the ajax call 284 | * for each api-element, then it will cache the json content to 285 | * AstroAPI.apiElements.jsoncontent 286 | * 287 | * @param {function} callback Build the dom tree after retrive the json content from wordpress 288 | */ 289 | function Fetch(callback) { 290 | // build element requirement 291 | var elCounter = 0; // need to use for track when all ajax call are finished 292 | var i, e; 293 | var elAmt = getElementAmt(AstroAPI); 294 | AstroAPI.apiElements.forEach(function(apiElement) { 295 | for (i = 0; i < apiElement.domElements.length; i+=1) { 296 | e = Element(apiElement.url, apiElement.domElements[i]); 297 | // closure 298 | (function (e, apiElement) { 299 | Util.GetContentFromWordpress(e.url(), function (err, data) { 300 | // cache the content 301 | if (err) { 302 | callback(e, data); 303 | } 304 | if (Array.isArray(data)) { 305 | apiElement.jsoncontent.push(data); 306 | } else { 307 | if (!data.posts) { 308 | apiElement.jsoncontent.push([data]); 309 | } else { 310 | apiElement.jsoncontent.push(data.posts); 311 | } 312 | } 313 | // sorry for the messy code 314 | // promises is not fully compatible in every browswer 315 | // this nested code is ugly, but... 316 | // this will be rewrite soon 317 | // 318 | callback(e, data); // i know, this block of code smell 319 | elCounter += 1; 320 | if(elCounter === elAmt) { 321 | document.dispatchEvent(event_FinishRequest); 322 | } 323 | }); 324 | })(e, apiElement); 325 | } 326 | }); 327 | } 328 | 329 | /** 330 | * Build 331 | * @summary a callback for Fetch(), it will build the html if the 332 | * api element template is list 333 | * 334 | * @param {object} element an instance of Element 335 | * @param {json} jsonContent content that receive from wordpress 336 | */ 337 | function Build(element, jsonContent) { 338 | var items; 339 | if (element.template() === 'repeat') { 340 | // copy the nodes 341 | var nodes = element.childnodes(); 342 | var i, virtual; 343 | var items = jsonContent[element.endPoint()] || jsonContent; 344 | for (i = 1; i < items.length; i+= 1) { 345 | virtual = nodes.cloneNode(true); 346 | element.elementNode().appendChild(virtual); 347 | } 348 | } else { 349 | items = jsonContent; 350 | } 351 | 352 | Render(element, items); 353 | } 354 | /** 355 | * Render 356 | * @summary Decision maker for render type base on the received json 357 | * 358 | * @param {object} element instance of Element 359 | * @param {json object} json json object that received from WordPress 360 | */ 361 | function Render(element, json) { 362 | var nodes, properties; 363 | var content; 364 | var renderType; 365 | 366 | nodes = element.elementNode(); 367 | properties = nodes.querySelectorAll(ASTRO_QUERY_DATASET.property); 368 | 369 | if (element.template() === 'single') { 370 | if (element.elementType() === 'jetpack') { 371 | if (json.posts) { 372 | json = json[element.endPoint()][0]; 373 | } 374 | RenderSinglePost(json, properties); 375 | } 376 | } else if (element.template() === 'repeat') { 377 | RenderList(element, json); 378 | } 379 | } 380 | 381 | /** 382 | * RenderList 383 | * 384 | * @param {object} element instance of Element 385 | * @param {json object} items json objects that received from wordpress 386 | */ 387 | function RenderList(element, items) { 388 | var nodes, i, properties; 389 | // if the json with .posts is from .com 390 | // if the json type is array, its from .org 391 | 392 | for (i = 0; i < items.length; i+= 1) { 393 | // assign the coresponse html to the post json 394 | nodes = element.elementNode().children[i]; 395 | properties = nodes.querySelectorAll(ASTRO_QUERY_DATASET.property); 396 | // this will insert the json to html inner html 397 | RenderSinglePost(items[i], properties); 398 | } 399 | } 400 | 401 | /** 402 | * RenderSinglePost 403 | * 404 | * @param {json object} json post json object 405 | * @param {nodelist} properties The nodes that contains [data-api-property] 406 | */ 407 | function RenderSinglePost(json, properties) { 408 | var i, content; 409 | for ( i = 0; i < properties.length; i += 1) { 410 | content = Util.ExtractJsonValueByKey(properties[i].dataset[ASTRO_DATASET_ATTRIBUTE.properties], json); 411 | if (properties[i].tagName === "IMG") { 412 | properties[i].setAttribute("src", 413 | content); 414 | } else if (properties[i].tagName === "A") { 415 | properties[i].setAttribute("href", 416 | content); 417 | } else { 418 | properties[i].innerHTML = content; 419 | } 420 | } 421 | } 422 | 423 | 424 | /** 425 | * Start execute the magic 426 | * 427 | */ 428 | Init(); 429 | 430 | 431 | Fetch(function (element, jsonContent) { 432 | Build(element, jsonContent); 433 | }); 434 | 435 | 436 | /* 437 | * Explosure the object to public 438 | * 439 | */ 440 | // for other other usage 441 | AstroAPI.Util = Util; 442 | // put this to public 443 | window.AstroAPI = AstroAPI; 444 | window.ASTRO_QUERY_DATASET = ASTRO_QUERY_DATASET; 445 | window.ASTRO_DATASET_ATTRIBUTE = ASTRO_DATASET_ATTRIBUTE; 446 | 447 | 448 | }(document, window)); 449 | -------------------------------------------------------------------------------- /tests/lib/jasmine-2.3.4/jasmine-html.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2015 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 | onThrowExpectationsClick = options.onThrowExpectationsClick || function() {}, 44 | addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, 45 | timer = options.timer || noopTimer, 46 | results = [], 47 | specsExecuted = 0, 48 | failureCount = 0, 49 | pendingSpecCount = 0, 50 | htmlReporterMain, 51 | symbols, 52 | failedSuites = []; 53 | 54 | this.initialize = function() { 55 | clearPrior(); 56 | htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'}, 57 | createDom('div', {className: 'banner'}, 58 | createDom('a', {className: 'title', href: 'http://jasmine.github.io/', target: '_blank'}), 59 | createDom('span', {className: 'version'}, j$.version) 60 | ), 61 | createDom('ul', {className: 'symbol-summary'}), 62 | createDom('div', {className: 'alert'}), 63 | createDom('div', {className: 'results'}, 64 | createDom('div', {className: 'failures'}) 65 | ) 66 | ); 67 | getContainer().appendChild(htmlReporterMain); 68 | 69 | symbols = find('.symbol-summary'); 70 | }; 71 | 72 | var totalSpecsDefined; 73 | this.jasmineStarted = function(options) { 74 | totalSpecsDefined = options.totalSpecsDefined || 0; 75 | timer.start(); 76 | }; 77 | 78 | var summary = createDom('div', {className: 'summary'}); 79 | 80 | var topResults = new j$.ResultsNode({}, '', null), 81 | currentParent = topResults; 82 | 83 | this.suiteStarted = function(result) { 84 | currentParent.addChild(result, 'suite'); 85 | currentParent = currentParent.last(); 86 | }; 87 | 88 | this.suiteDone = function(result) { 89 | if (result.status == 'failed') { 90 | failedSuites.push(result); 91 | } 92 | 93 | if (currentParent == topResults) { 94 | return; 95 | } 96 | 97 | currentParent = currentParent.parent; 98 | }; 99 | 100 | this.specStarted = function(result) { 101 | currentParent.addChild(result, 'spec'); 102 | }; 103 | 104 | var failures = []; 105 | this.specDone = function(result) { 106 | if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') { 107 | console.error('Spec \'' + result.fullName + '\' has no expectations.'); 108 | } 109 | 110 | if (result.status != 'disabled') { 111 | specsExecuted++; 112 | } 113 | 114 | symbols.appendChild(createDom('li', { 115 | className: noExpectations(result) ? 'empty' : result.status, 116 | id: 'spec_' + result.id, 117 | title: result.fullName 118 | } 119 | )); 120 | 121 | if (result.status == 'failed') { 122 | failureCount++; 123 | 124 | var failure = 125 | createDom('div', {className: 'spec-detail failed'}, 126 | createDom('div', {className: 'description'}, 127 | createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName) 128 | ), 129 | createDom('div', {className: 'messages'}) 130 | ); 131 | var messages = failure.childNodes[1]; 132 | 133 | for (var i = 0; i < result.failedExpectations.length; i++) { 134 | var expectation = result.failedExpectations[i]; 135 | messages.appendChild(createDom('div', {className: 'result-message'}, expectation.message)); 136 | messages.appendChild(createDom('div', {className: 'stack-trace'}, expectation.stack)); 137 | } 138 | 139 | failures.push(failure); 140 | } 141 | 142 | if (result.status == 'pending') { 143 | pendingSpecCount++; 144 | } 145 | }; 146 | 147 | this.jasmineDone = function() { 148 | var banner = find('.banner'); 149 | var alert = find('.alert'); 150 | alert.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); 151 | 152 | banner.appendChild( 153 | createDom('div', { className: 'run-options' }, 154 | createDom('span', { className: 'trigger' }, 'Options'), 155 | createDom('div', { className: 'payload' }, 156 | createDom('div', { className: 'exceptions' }, 157 | createDom('input', { 158 | className: 'raise', 159 | id: 'raise-exceptions', 160 | type: 'checkbox' 161 | }), 162 | createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions')), 163 | createDom('div', { className: 'throw-failures' }, 164 | createDom('input', { 165 | className: 'throw', 166 | id: 'throw-failures', 167 | type: 'checkbox' 168 | }), 169 | createDom('label', { className: 'label', 'for': 'throw-failures' }, 'stop spec on expectation failure')) 170 | ) 171 | )); 172 | 173 | var raiseCheckbox = find('#raise-exceptions'); 174 | 175 | raiseCheckbox.checked = !env.catchingExceptions(); 176 | raiseCheckbox.onclick = onRaiseExceptionsClick; 177 | 178 | var throwCheckbox = find('#throw-failures'); 179 | throwCheckbox.checked = env.throwingExpectationFailures(); 180 | throwCheckbox.onclick = onThrowExpectationsClick; 181 | 182 | var optionsMenu = find('.run-options'), 183 | optionsTrigger = optionsMenu.querySelector('.trigger'), 184 | optionsPayload = optionsMenu.querySelector('.payload'), 185 | isOpen = /\bopen\b/; 186 | 187 | optionsTrigger.onclick = function() { 188 | if (isOpen.test(optionsPayload.className)) { 189 | optionsPayload.className = optionsPayload.className.replace(isOpen, ''); 190 | } else { 191 | optionsPayload.className += ' open'; 192 | } 193 | }; 194 | 195 | if (specsExecuted < totalSpecsDefined) { 196 | var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; 197 | alert.appendChild( 198 | createDom('span', {className: 'bar skipped'}, 199 | createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage) 200 | ) 201 | ); 202 | } 203 | var statusBarMessage = ''; 204 | var statusBarClassName = 'bar '; 205 | 206 | if (totalSpecsDefined > 0) { 207 | statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount); 208 | if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); } 209 | statusBarClassName += (failureCount > 0) ? 'failed' : 'passed'; 210 | } else { 211 | statusBarClassName += 'skipped'; 212 | statusBarMessage += 'No specs found'; 213 | } 214 | 215 | alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage)); 216 | 217 | for(i = 0; i < failedSuites.length; i++) { 218 | var failedSuite = failedSuites[i]; 219 | for(var j = 0; j < failedSuite.failedExpectations.length; j++) { 220 | var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message; 221 | var errorBarClassName = 'bar errored'; 222 | alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage)); 223 | } 224 | } 225 | 226 | var results = find('.results'); 227 | results.appendChild(summary); 228 | 229 | summaryList(topResults, summary); 230 | 231 | function summaryList(resultsTree, domParent) { 232 | var specListNode; 233 | for (var i = 0; i < resultsTree.children.length; i++) { 234 | var resultNode = resultsTree.children[i]; 235 | if (resultNode.type == 'suite') { 236 | var suiteListNode = createDom('ul', {className: 'suite', id: 'suite-' + resultNode.result.id}, 237 | createDom('li', {className: 'suite-detail'}, 238 | createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description) 239 | ) 240 | ); 241 | 242 | summaryList(resultNode, suiteListNode); 243 | domParent.appendChild(suiteListNode); 244 | } 245 | if (resultNode.type == 'spec') { 246 | if (domParent.getAttribute('class') != 'specs') { 247 | specListNode = createDom('ul', {className: 'specs'}); 248 | domParent.appendChild(specListNode); 249 | } 250 | var specDescription = resultNode.result.description; 251 | if(noExpectations(resultNode.result)) { 252 | specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; 253 | } 254 | if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { 255 | specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; 256 | } 257 | specListNode.appendChild( 258 | createDom('li', { 259 | className: resultNode.result.status, 260 | id: 'spec-' + resultNode.result.id 261 | }, 262 | createDom('a', {href: specHref(resultNode.result)}, specDescription) 263 | ) 264 | ); 265 | } 266 | } 267 | } 268 | 269 | if (failures.length) { 270 | alert.appendChild( 271 | createDom('span', {className: 'menu bar spec-list'}, 272 | createDom('span', {}, 'Spec List | '), 273 | createDom('a', {className: 'failures-menu', href: '#'}, 'Failures'))); 274 | alert.appendChild( 275 | createDom('span', {className: 'menu bar failure-list'}, 276 | createDom('a', {className: 'spec-list-menu', href: '#'}, 'Spec List'), 277 | createDom('span', {}, ' | Failures '))); 278 | 279 | find('.failures-menu').onclick = function() { 280 | setMenuModeTo('failure-list'); 281 | }; 282 | find('.spec-list-menu').onclick = function() { 283 | setMenuModeTo('spec-list'); 284 | }; 285 | 286 | setMenuModeTo('failure-list'); 287 | 288 | var failureNode = find('.failures'); 289 | for (var i = 0; i < failures.length; i++) { 290 | failureNode.appendChild(failures[i]); 291 | } 292 | } 293 | }; 294 | 295 | return this; 296 | 297 | function find(selector) { 298 | return getContainer().querySelector('.jasmine_html-reporter ' + selector); 299 | } 300 | 301 | function clearPrior() { 302 | // return the reporter 303 | var oldReporter = find(''); 304 | 305 | if(oldReporter) { 306 | getContainer().removeChild(oldReporter); 307 | } 308 | } 309 | 310 | function createDom(type, attrs, childrenVarArgs) { 311 | var el = createElement(type); 312 | 313 | for (var i = 2; i < arguments.length; i++) { 314 | var child = arguments[i]; 315 | 316 | if (typeof child === 'string') { 317 | el.appendChild(createTextNode(child)); 318 | } else { 319 | if (child) { 320 | el.appendChild(child); 321 | } 322 | } 323 | } 324 | 325 | for (var attr in attrs) { 326 | if (attr == 'className') { 327 | el[attr] = attrs[attr]; 328 | } else { 329 | el.setAttribute(attr, attrs[attr]); 330 | } 331 | } 332 | 333 | return el; 334 | } 335 | 336 | function pluralize(singular, count) { 337 | var word = (count == 1 ? singular : singular + 's'); 338 | 339 | return '' + count + ' ' + word; 340 | } 341 | 342 | function specHref(result) { 343 | return addToExistingQueryString('spec', result.fullName); 344 | } 345 | 346 | function defaultQueryString(key, value) { 347 | return '?' + key + '=' + value; 348 | } 349 | 350 | function setMenuModeTo(mode) { 351 | htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); 352 | } 353 | 354 | function noExpectations(result) { 355 | return (result.failedExpectations.length + result.passedExpectations.length) === 0 && 356 | result.status === 'passed'; 357 | } 358 | } 359 | 360 | return HtmlReporter; 361 | }; 362 | 363 | jasmineRequire.HtmlSpecFilter = function() { 364 | function HtmlSpecFilter(options) { 365 | var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); 366 | var filterPattern = new RegExp(filterString); 367 | 368 | this.matches = function(specName) { 369 | return filterPattern.test(specName); 370 | }; 371 | } 372 | 373 | return HtmlSpecFilter; 374 | }; 375 | 376 | jasmineRequire.ResultsNode = function() { 377 | function ResultsNode(result, type, parent) { 378 | this.result = result; 379 | this.type = type; 380 | this.parent = parent; 381 | 382 | this.children = []; 383 | 384 | this.addChild = function(result, type) { 385 | this.children.push(new ResultsNode(result, type, this)); 386 | }; 387 | 388 | this.last = function() { 389 | return this.children[this.children.length - 1]; 390 | }; 391 | } 392 | 393 | return ResultsNode; 394 | }; 395 | 396 | jasmineRequire.QueryString = function() { 397 | function QueryString(options) { 398 | 399 | this.navigateWithNewParam = function(key, value) { 400 | options.getWindowLocation().search = this.fullStringWithNewParam(key, value); 401 | }; 402 | 403 | this.fullStringWithNewParam = function(key, value) { 404 | var paramMap = queryStringToParamMap(); 405 | paramMap[key] = value; 406 | return toQueryString(paramMap); 407 | }; 408 | 409 | this.getParam = function(key) { 410 | return queryStringToParamMap()[key]; 411 | }; 412 | 413 | return this; 414 | 415 | function toQueryString(paramMap) { 416 | var qStrPairs = []; 417 | for (var prop in paramMap) { 418 | qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])); 419 | } 420 | return '?' + qStrPairs.join('&'); 421 | } 422 | 423 | function queryStringToParamMap() { 424 | var paramStr = options.getWindowLocation().search.substring(1), 425 | params = [], 426 | paramMap = {}; 427 | 428 | if (paramStr.length > 0) { 429 | params = paramStr.split('&'); 430 | for (var i = 0; i < params.length; i++) { 431 | var p = params[i].split('='); 432 | var value = decodeURIComponent(p[1]); 433 | if (value === 'true' || value === 'false') { 434 | value = JSON.parse(value); 435 | } 436 | paramMap[decodeURIComponent(p[0])] = value; 437 | } 438 | } 439 | 440 | return paramMap; 441 | } 442 | 443 | } 444 | 445 | return QueryString; 446 | }; 447 | -------------------------------------------------------------------------------- /tests/lib/jasmine-2.3.4/jasmine.css: -------------------------------------------------------------------------------- 1 | body { overflow-y: scroll; } 2 | 3 | .jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; } 4 | .jasmine_html-reporter a { text-decoration: none; } 5 | .jasmine_html-reporter a:hover { text-decoration: underline; } 6 | .jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; } 7 | .jasmine_html-reporter .banner, .jasmine_html-reporter .symbol-summary, .jasmine_html-reporter .summary, .jasmine_html-reporter .result-message, .jasmine_html-reporter .spec .description, .jasmine_html-reporter .spec-detail .description, .jasmine_html-reporter .alert .bar, .jasmine_html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; } 8 | .jasmine_html-reporter .banner { position: relative; } 9 | .jasmine_html-reporter .banner .title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } 10 | .jasmine_html-reporter .banner .version { margin-left: 14px; position: relative; top: 6px; } 11 | .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } 12 | .jasmine_html-reporter .version { color: #aaa; } 13 | .jasmine_html-reporter .banner { margin-top: 14px; } 14 | .jasmine_html-reporter .duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; } 15 | .jasmine_html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } 16 | .jasmine_html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; } 17 | .jasmine_html-reporter .symbol-summary li.passed { font-size: 14px; } 18 | .jasmine_html-reporter .symbol-summary li.passed:before { color: #007069; content: "\02022"; } 19 | .jasmine_html-reporter .symbol-summary li.failed { line-height: 9px; } 20 | .jasmine_html-reporter .symbol-summary li.failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; } 21 | .jasmine_html-reporter .symbol-summary li.disabled { font-size: 14px; } 22 | .jasmine_html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } 23 | .jasmine_html-reporter .symbol-summary li.pending { line-height: 17px; } 24 | .jasmine_html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } 25 | .jasmine_html-reporter .symbol-summary li.empty { font-size: 14px; } 26 | .jasmine_html-reporter .symbol-summary li.empty:before { color: #ba9d37; content: "\02022"; } 27 | .jasmine_html-reporter .run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; } 28 | .jasmine_html-reporter .run-options .trigger { cursor: pointer; padding: 8px 16px; } 29 | .jasmine_html-reporter .run-options .payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; } 30 | .jasmine_html-reporter .run-options .payload.open { display: block; } 31 | .jasmine_html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 32 | .jasmine_html-reporter .bar.failed { background-color: #ca3a11; } 33 | .jasmine_html-reporter .bar.passed { background-color: #007069; } 34 | .jasmine_html-reporter .bar.skipped { background-color: #bababa; } 35 | .jasmine_html-reporter .bar.errored { background-color: #ca3a11; } 36 | .jasmine_html-reporter .bar.menu { background-color: #fff; color: #aaa; } 37 | .jasmine_html-reporter .bar.menu a { color: #333; } 38 | .jasmine_html-reporter .bar a { color: white; } 39 | .jasmine_html-reporter.spec-list .bar.menu.failure-list, .jasmine_html-reporter.spec-list .results .failures { display: none; } 40 | .jasmine_html-reporter.failure-list .bar.menu.spec-list, .jasmine_html-reporter.failure-list .summary { display: none; } 41 | .jasmine_html-reporter .results { margin-top: 14px; } 42 | .jasmine_html-reporter .summary { margin-top: 14px; } 43 | .jasmine_html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } 44 | .jasmine_html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } 45 | .jasmine_html-reporter .summary li.passed a { color: #007069; } 46 | .jasmine_html-reporter .summary li.failed a { color: #ca3a11; } 47 | .jasmine_html-reporter .summary li.empty a { color: #ba9d37; } 48 | .jasmine_html-reporter .summary li.pending a { color: #ba9d37; } 49 | .jasmine_html-reporter .summary li.disabled a { color: #bababa; } 50 | .jasmine_html-reporter .description + .suite { margin-top: 0; } 51 | .jasmine_html-reporter .suite { margin-top: 14px; } 52 | .jasmine_html-reporter .suite a { color: #333; } 53 | .jasmine_html-reporter .failures .spec-detail { margin-bottom: 28px; } 54 | .jasmine_html-reporter .failures .spec-detail .description { background-color: #ca3a11; } 55 | .jasmine_html-reporter .failures .spec-detail .description a { color: white; } 56 | .jasmine_html-reporter .result-message { padding-top: 14px; color: #333; white-space: pre; } 57 | .jasmine_html-reporter .result-message span.result { display: block; } 58 | .jasmine_html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; } 59 | -------------------------------------------------------------------------------- /tests/lib/jasmine-jquery.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Jasmine-jQuery: a set of jQuery helpers for Jasmine tests. 3 | 4 | Version 2.1.0 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 | */ 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 $('
').attr(attributesToSet) 94 | } 95 | 96 | jasmine.Fixtures.prototype.createContainer_ = function (html) { 97 | var container = $('
') 98 | .attr('id', this.containerId) 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) 110 | } 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 | dataType: 'html', 129 | success: function (data, status, $xhr) { 130 | htmlText = $xhr.responseText 131 | } 132 | }).fail(function ($xhr, status, err) { 133 | throw new Error('Fixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') 134 | }) 135 | 136 | var scripts = $($.parseHTML(htmlText, true)).find('script[src]') || []; 137 | 138 | scripts.each(function(){ 139 | $.ajax({ 140 | async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded 141 | cache: false, 142 | dataType: 'script', 143 | url: $(this).attr('src'), 144 | success: function (data, status, $xhr) { 145 | htmlText += '' 146 | }, 147 | error: function ($xhr, status, err) { 148 | throw new Error('Script could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') 149 | } 150 | }); 151 | }) 152 | 153 | self.fixturesCache_[relativeUrl] = htmlText; 154 | } 155 | 156 | jasmine.Fixtures.prototype.makeFixtureUrl_ = function (relativeUrl){ 157 | return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl 158 | } 159 | 160 | jasmine.Fixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { 161 | return this[methodName].apply(this, passedArguments) 162 | } 163 | 164 | 165 | jasmine.StyleFixtures = function () { 166 | this.fixturesCache_ = {} 167 | this.fixturesNodes_ = [] 168 | this.fixturesPath = 'spec/javascripts/fixtures' 169 | } 170 | 171 | jasmine.StyleFixtures.prototype.set = function (css) { 172 | this.cleanUp() 173 | this.createStyle_(css) 174 | } 175 | 176 | jasmine.StyleFixtures.prototype.appendSet = function (css) { 177 | this.createStyle_(css) 178 | } 179 | 180 | jasmine.StyleFixtures.prototype.preload = function () { 181 | this.read_.apply(this, arguments) 182 | } 183 | 184 | jasmine.StyleFixtures.prototype.load = function () { 185 | this.cleanUp() 186 | this.createStyle_(this.read_.apply(this, arguments)) 187 | } 188 | 189 | jasmine.StyleFixtures.prototype.appendLoad = function () { 190 | this.createStyle_(this.read_.apply(this, arguments)) 191 | } 192 | 193 | jasmine.StyleFixtures.prototype.cleanUp = function () { 194 | while(this.fixturesNodes_.length) { 195 | this.fixturesNodes_.pop().remove() 196 | } 197 | } 198 | 199 | jasmine.StyleFixtures.prototype.createStyle_ = function (html) { 200 | var styleText = $('
').html(html).text() 201 | , style = $('') 202 | 203 | this.fixturesNodes_.push(style) 204 | $('head').append(style) 205 | } 206 | 207 | jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache 208 | jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read 209 | jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_ 210 | jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_ 211 | jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_ 212 | jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_ 213 | 214 | jasmine.getJSONFixtures = function () { 215 | return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures() 216 | } 217 | 218 | jasmine.JSONFixtures = function () { 219 | this.fixturesCache_ = {} 220 | this.fixturesPath = 'spec/javascripts/fixtures/json' 221 | } 222 | 223 | jasmine.JSONFixtures.prototype.load = function () { 224 | this.read.apply(this, arguments) 225 | return this.fixturesCache_ 226 | } 227 | 228 | jasmine.JSONFixtures.prototype.read = function () { 229 | var fixtureUrls = arguments 230 | 231 | for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { 232 | this.getFixtureData_(fixtureUrls[urlIndex]) 233 | } 234 | 235 | return this.fixturesCache_ 236 | } 237 | 238 | jasmine.JSONFixtures.prototype.clearCache = function () { 239 | this.fixturesCache_ = {} 240 | } 241 | 242 | jasmine.JSONFixtures.prototype.getFixtureData_ = function (url) { 243 | if (!this.fixturesCache_[url]) this.loadFixtureIntoCache_(url) 244 | return this.fixturesCache_[url] 245 | } 246 | 247 | jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { 248 | var self = this 249 | , url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl 250 | 251 | $.ajax({ 252 | async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded 253 | cache: false, 254 | dataType: 'json', 255 | url: url, 256 | success: function (data) { 257 | self.fixturesCache_[relativeUrl] = data 258 | }, 259 | error: function ($xhr, status, err) { 260 | throw new Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') 261 | } 262 | }) 263 | } 264 | 265 | jasmine.JSONFixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { 266 | return this[methodName].apply(this, passedArguments) 267 | } 268 | 269 | jasmine.jQuery = function () {} 270 | 271 | jasmine.jQuery.browserTagCaseIndependentHtml = function (html) { 272 | return $('
').append(html).html() 273 | } 274 | 275 | jasmine.jQuery.elementToString = function (element) { 276 | return $(element).map(function () { return this.outerHTML; }).toArray().join(', ') 277 | } 278 | 279 | var data = { 280 | spiedEvents: {} 281 | , handlers: [] 282 | } 283 | 284 | jasmine.jQuery.events = { 285 | spyOn: function (selector, eventName) { 286 | var handler = function (e) { 287 | var calls = (typeof data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] !== 'undefined') ? data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0 288 | data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = { 289 | args: jasmine.util.argsToArray(arguments), 290 | calls: ++calls 291 | } 292 | } 293 | 294 | $(selector).on(eventName, handler) 295 | data.handlers.push(handler) 296 | 297 | return { 298 | selector: selector, 299 | eventName: eventName, 300 | handler: handler, 301 | reset: function (){ 302 | delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] 303 | }, 304 | calls: { 305 | count: function () { 306 | return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ? 307 | data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0; 308 | }, 309 | any: function () { 310 | return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ? 311 | !!data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : false; 312 | } 313 | } 314 | } 315 | }, 316 | 317 | args: function (selector, eventName) { 318 | var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].args 319 | 320 | if (!actualArgs) { 321 | throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent." 322 | } 323 | 324 | return actualArgs 325 | }, 326 | 327 | wasTriggered: function (selector, eventName) { 328 | return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]) 329 | }, 330 | 331 | wasTriggeredWith: function (selector, eventName, expectedArgs, util, customEqualityTesters) { 332 | var actualArgs = jasmine.jQuery.events.args(selector, eventName).slice(1) 333 | 334 | if (Object.prototype.toString.call(expectedArgs) !== '[object Array]') 335 | actualArgs = actualArgs[0] 336 | 337 | return util.equals(actualArgs, expectedArgs, customEqualityTesters) 338 | }, 339 | 340 | wasPrevented: function (selector, eventName) { 341 | var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] 342 | , args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args 343 | , e = args ? args[0] : undefined 344 | 345 | return e && e.isDefaultPrevented() 346 | }, 347 | 348 | wasStopped: function (selector, eventName) { 349 | var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] 350 | , args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args 351 | , e = args ? args[0] : undefined 352 | 353 | return e && e.isPropagationStopped() 354 | }, 355 | 356 | cleanUp: function () { 357 | data.spiedEvents = {} 358 | data.handlers = [] 359 | } 360 | } 361 | 362 | var hasProperty = function (actualValue, expectedValue) { 363 | if (expectedValue === undefined) 364 | return actualValue !== undefined 365 | 366 | return actualValue === expectedValue 367 | } 368 | 369 | beforeEach(function () { 370 | jasmine.addMatchers({ 371 | toHaveClass: function () { 372 | return { 373 | compare: function (actual, className) { 374 | return { pass: $(actual).hasClass(className) } 375 | } 376 | } 377 | }, 378 | 379 | toHaveCss: function () { 380 | return { 381 | compare: function (actual, css) { 382 | for (var prop in css){ 383 | var value = css[prop] 384 | // see issue #147 on gh 385 | ;if (value === 'auto' && $(actual).get(0).style[prop] === 'auto') continue 386 | if ($(actual).css(prop) !== value) return { pass: false } 387 | } 388 | return { pass: true } 389 | } 390 | } 391 | }, 392 | 393 | toBeVisible: function () { 394 | return { 395 | compare: function (actual) { 396 | return { pass: $(actual).is(':visible') } 397 | } 398 | } 399 | }, 400 | 401 | toBeHidden: function () { 402 | return { 403 | compare: function (actual) { 404 | return { pass: $(actual).is(':hidden') } 405 | } 406 | } 407 | }, 408 | 409 | toBeSelected: function () { 410 | return { 411 | compare: function (actual) { 412 | return { pass: $(actual).is(':selected') } 413 | } 414 | } 415 | }, 416 | 417 | toBeChecked: function () { 418 | return { 419 | compare: function (actual) { 420 | return { pass: $(actual).is(':checked') } 421 | } 422 | } 423 | }, 424 | 425 | toBeEmpty: function () { 426 | return { 427 | compare: function (actual) { 428 | return { pass: $(actual).is(':empty') } 429 | } 430 | } 431 | }, 432 | 433 | toBeInDOM: function () { 434 | return { 435 | compare: function (actual) { 436 | return { pass: $.contains(document.documentElement, $(actual)[0]) } 437 | } 438 | } 439 | }, 440 | 441 | toExist: function () { 442 | return { 443 | compare: function (actual) { 444 | return { pass: $(actual).length } 445 | } 446 | } 447 | }, 448 | 449 | toHaveLength: function () { 450 | return { 451 | compare: function (actual, length) { 452 | return { pass: $(actual).length === length } 453 | } 454 | } 455 | }, 456 | 457 | toHaveAttr: function () { 458 | return { 459 | compare: function (actual, attributeName, expectedAttributeValue) { 460 | return { pass: hasProperty($(actual).attr(attributeName), expectedAttributeValue) } 461 | } 462 | } 463 | }, 464 | 465 | toHaveProp: function () { 466 | return { 467 | compare: function (actual, propertyName, expectedPropertyValue) { 468 | return { pass: hasProperty($(actual).prop(propertyName), expectedPropertyValue) } 469 | } 470 | } 471 | }, 472 | 473 | toHaveId: function () { 474 | return { 475 | compare: function (actual, id) { 476 | return { pass: $(actual).attr('id') == id } 477 | } 478 | } 479 | }, 480 | 481 | toHaveHtml: function () { 482 | return { 483 | compare: function (actual, html) { 484 | return { pass: $(actual).html() == jasmine.jQuery.browserTagCaseIndependentHtml(html) } 485 | } 486 | } 487 | }, 488 | 489 | toContainHtml: function () { 490 | return { 491 | compare: function (actual, html) { 492 | var actualHtml = $(actual).html() 493 | , expectedHtml = jasmine.jQuery.browserTagCaseIndependentHtml(html) 494 | 495 | return { pass: (actualHtml.indexOf(expectedHtml) >= 0) } 496 | } 497 | } 498 | }, 499 | 500 | toHaveText: function () { 501 | return { 502 | compare: function (actual, text) { 503 | var actualText = $(actual).text() 504 | var trimmedText = $.trim(actualText) 505 | 506 | if (text && $.isFunction(text.test)) { 507 | return { pass: text.test(actualText) || text.test(trimmedText) } 508 | } else { 509 | return { pass: (actualText == text || trimmedText == text) } 510 | } 511 | } 512 | } 513 | }, 514 | 515 | toContainText: function () { 516 | return { 517 | compare: function (actual, text) { 518 | var trimmedText = $.trim($(actual).text()) 519 | 520 | if (text && $.isFunction(text.test)) { 521 | return { pass: text.test(trimmedText) } 522 | } else { 523 | return { pass: trimmedText.indexOf(text) != -1 } 524 | } 525 | } 526 | } 527 | }, 528 | 529 | toHaveValue: function () { 530 | return { 531 | compare: function (actual, value) { 532 | return { pass: $(actual).val() === value } 533 | } 534 | } 535 | }, 536 | 537 | toHaveData: function () { 538 | return { 539 | compare: function (actual, key, expectedValue) { 540 | return { pass: hasProperty($(actual).data(key), expectedValue) } 541 | } 542 | } 543 | }, 544 | 545 | toContainElement: function () { 546 | return { 547 | compare: function (actual, selector) { 548 | return { pass: $(actual).find(selector).length } 549 | } 550 | } 551 | }, 552 | 553 | toBeMatchedBy: function () { 554 | return { 555 | compare: function (actual, selector) { 556 | return { pass: $(actual).filter(selector).length } 557 | } 558 | } 559 | }, 560 | 561 | toBeDisabled: function () { 562 | return { 563 | compare: function (actual, selector) { 564 | return { pass: $(actual).is(':disabled') } 565 | } 566 | } 567 | }, 568 | 569 | toBeFocused: function (selector) { 570 | return { 571 | compare: function (actual, selector) { 572 | return { pass: $(actual)[0] === $(actual)[0].ownerDocument.activeElement } 573 | } 574 | } 575 | }, 576 | 577 | toHandle: function () { 578 | return { 579 | compare: function (actual, event) { 580 | if ( !actual || actual.length === 0 ) return { pass: false }; 581 | var events = $._data($(actual).get(0), "events") 582 | 583 | if (!events || !event || typeof event !== "string") { 584 | return { pass: false } 585 | } 586 | 587 | var namespaces = event.split(".") 588 | , eventType = namespaces.shift() 589 | , sortedNamespaces = namespaces.slice(0).sort() 590 | , namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") 591 | 592 | if (events[eventType] && namespaces.length) { 593 | for (var i = 0; i < events[eventType].length; i++) { 594 | var namespace = events[eventType][i].namespace 595 | 596 | if (namespaceRegExp.test(namespace)) 597 | return { pass: true } 598 | } 599 | } else { 600 | return { pass: (events[eventType] && events[eventType].length > 0) } 601 | } 602 | 603 | return { pass: false } 604 | } 605 | } 606 | }, 607 | 608 | toHandleWith: function () { 609 | return { 610 | compare: function (actual, eventName, eventHandler) { 611 | if ( !actual || actual.length === 0 ) return { pass: false }; 612 | var normalizedEventName = eventName.split('.')[0] 613 | , stack = $._data($(actual).get(0), "events")[normalizedEventName] 614 | 615 | for (var i = 0; i < stack.length; i++) { 616 | if (stack[i].handler == eventHandler) return { pass: true } 617 | } 618 | 619 | return { pass: false } 620 | } 621 | } 622 | }, 623 | 624 | toHaveBeenTriggeredOn: function () { 625 | return { 626 | compare: function (actual, selector) { 627 | var result = { pass: jasmine.jQuery.events.wasTriggered(selector, actual) } 628 | 629 | result.message = result.pass ? 630 | "Expected event " + $(actual) + " not to have been triggered on " + selector : 631 | "Expected event " + $(actual) + " to have been triggered on " + selector 632 | 633 | return result; 634 | } 635 | } 636 | }, 637 | 638 | toHaveBeenTriggered: function (){ 639 | return { 640 | compare: function (actual) { 641 | var eventName = actual.eventName 642 | , selector = actual.selector 643 | , result = { pass: jasmine.jQuery.events.wasTriggered(selector, eventName) } 644 | 645 | result.message = result.pass ? 646 | "Expected event " + eventName + " not to have been triggered on " + selector : 647 | "Expected event " + eventName + " to have been triggered on " + selector 648 | 649 | return result 650 | } 651 | } 652 | }, 653 | 654 | toHaveBeenTriggeredOnAndWith: function (j$, customEqualityTesters) { 655 | return { 656 | compare: function (actual, selector, expectedArgs) { 657 | var wasTriggered = jasmine.jQuery.events.wasTriggered(selector, actual) 658 | , result = { pass: wasTriggered && jasmine.jQuery.events.wasTriggeredWith(selector, actual, expectedArgs, j$, customEqualityTesters) } 659 | 660 | if (wasTriggered) { 661 | var actualArgs = jasmine.jQuery.events.args(selector, actual, expectedArgs)[1] 662 | result.message = result.pass ? 663 | "Expected event " + actual + " not to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) : 664 | "Expected event " + actual + " to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) 665 | 666 | } else { 667 | // todo check on this 668 | result.message = result.pass ? 669 | "Expected event " + actual + " not to have been triggered on " + selector : 670 | "Expected event " + actual + " to have been triggered on " + selector 671 | } 672 | 673 | return result 674 | } 675 | } 676 | }, 677 | 678 | toHaveBeenPreventedOn: function () { 679 | return { 680 | compare: function (actual, selector) { 681 | var result = { pass: jasmine.jQuery.events.wasPrevented(selector, actual) } 682 | 683 | result.message = result.pass ? 684 | "Expected event " + actual + " not to have been prevented on " + selector : 685 | "Expected event " + actual + " to have been prevented on " + selector 686 | 687 | return result 688 | } 689 | } 690 | }, 691 | 692 | toHaveBeenPrevented: function () { 693 | return { 694 | compare: function (actual) { 695 | var eventName = actual.eventName 696 | , selector = actual.selector 697 | , result = { pass: jasmine.jQuery.events.wasPrevented(selector, eventName) } 698 | 699 | result.message = result.pass ? 700 | "Expected event " + eventName + " not to have been prevented on " + selector : 701 | "Expected event " + eventName + " to have been prevented on " + selector 702 | 703 | return result 704 | } 705 | } 706 | }, 707 | 708 | toHaveBeenStoppedOn: function () { 709 | return { 710 | compare: function (actual, selector) { 711 | var result = { pass: jasmine.jQuery.events.wasStopped(selector, actual) } 712 | 713 | result.message = result.pass ? 714 | "Expected event " + actual + " not to have been stopped on " + selector : 715 | "Expected event " + actual + " to have been stopped on " + selector 716 | 717 | return result; 718 | } 719 | } 720 | }, 721 | 722 | toHaveBeenStopped: function () { 723 | return { 724 | compare: function (actual) { 725 | var eventName = actual.eventName 726 | , selector = actual.selector 727 | , result = { pass: jasmine.jQuery.events.wasStopped(selector, eventName) } 728 | 729 | result.message = result.pass ? 730 | "Expected event " + eventName + " not to have been stopped on " + selector : 731 | "Expected event " + eventName + " to have been stopped on " + selector 732 | 733 | return result 734 | } 735 | } 736 | } 737 | }) 738 | 739 | jasmine.getEnv().addCustomEqualityTester(function(a, b) { 740 | if (a && b) { 741 | if (a instanceof $ || jasmine.isDomNode(a)) { 742 | var $a = $(a) 743 | 744 | if (b instanceof $) 745 | return $a.length == b.length && a.is(b) 746 | 747 | return $a.is(b); 748 | } 749 | 750 | if (b instanceof $ || jasmine.isDomNode(b)) { 751 | var $b = $(b) 752 | 753 | if (a instanceof $) 754 | return a.length == $b.length && $b.is(a) 755 | 756 | return $(b).is(a); 757 | } 758 | } 759 | }) 760 | 761 | jasmine.getEnv().addCustomEqualityTester(function (a, b) { 762 | if (a instanceof $ && b instanceof $ && a.size() == b.size()) 763 | return a.is(b) 764 | }) 765 | }) 766 | 767 | afterEach(function () { 768 | jasmine.getFixtures().cleanUp() 769 | jasmine.getStyleFixtures().cleanUp() 770 | jasmine.jQuery.events.cleanUp() 771 | }) 772 | 773 | window.readFixtures = function () { 774 | return jasmine.getFixtures().proxyCallTo_('read', arguments) 775 | } 776 | 777 | window.preloadFixtures = function () { 778 | jasmine.getFixtures().proxyCallTo_('preload', arguments) 779 | } 780 | 781 | window.loadFixtures = function () { 782 | jasmine.getFixtures().proxyCallTo_('load', arguments) 783 | } 784 | 785 | window.appendLoadFixtures = function () { 786 | jasmine.getFixtures().proxyCallTo_('appendLoad', arguments) 787 | } 788 | 789 | window.setFixtures = function (html) { 790 | return jasmine.getFixtures().proxyCallTo_('set', arguments) 791 | } 792 | 793 | window.appendSetFixtures = function () { 794 | jasmine.getFixtures().proxyCallTo_('appendSet', arguments) 795 | } 796 | 797 | window.sandbox = function (attributes) { 798 | return jasmine.getFixtures().sandbox(attributes) 799 | } 800 | 801 | window.spyOnEvent = function (selector, eventName) { 802 | return jasmine.jQuery.events.spyOn(selector, eventName) 803 | } 804 | 805 | window.preloadStyleFixtures = function () { 806 | jasmine.getStyleFixtures().proxyCallTo_('preload', arguments) 807 | } 808 | 809 | window.loadStyleFixtures = function () { 810 | jasmine.getStyleFixtures().proxyCallTo_('load', arguments) 811 | } 812 | 813 | window.appendLoadStyleFixtures = function () { 814 | jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments) 815 | } 816 | 817 | window.setStyleFixtures = function (html) { 818 | jasmine.getStyleFixtures().proxyCallTo_('set', arguments) 819 | } 820 | 821 | window.appendSetStyleFixtures = function (html) { 822 | jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments) 823 | } 824 | 825 | window.loadJSONFixtures = function () { 826 | return jasmine.getJSONFixtures().proxyCallTo_('load', arguments) 827 | } 828 | 829 | window.getJSONFixture = function (url) { 830 | return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url] 831 | } 832 | }(window, window.jasmine, window.jQuery); 833 | -------------------------------------------------------------------------------- /tests/lib/jasmine-2.3.4/jasmine.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2015 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 | var getJasmineRequireObj = (function (jasmineGlobal) { 24 | var jasmineRequire; 25 | 26 | if (typeof module !== 'undefined' && module.exports) { 27 | jasmineGlobal = global; 28 | jasmineRequire = exports; 29 | } else { 30 | if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') { 31 | jasmineGlobal = window; 32 | } 33 | jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {}; 34 | } 35 | 36 | function getJasmineRequire() { 37 | return jasmineRequire; 38 | } 39 | 40 | getJasmineRequire().core = function(jRequire) { 41 | var j$ = {}; 42 | 43 | jRequire.base(j$, jasmineGlobal); 44 | j$.util = jRequire.util(); 45 | j$.errors = jRequire.errors(); 46 | j$.Any = jRequire.Any(j$); 47 | j$.Anything = jRequire.Anything(j$); 48 | j$.CallTracker = jRequire.CallTracker(); 49 | j$.MockDate = jRequire.MockDate(); 50 | j$.Clock = jRequire.Clock(); 51 | j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); 52 | j$.Env = jRequire.Env(j$); 53 | j$.ExceptionFormatter = jRequire.ExceptionFormatter(); 54 | j$.Expectation = jRequire.Expectation(); 55 | j$.buildExpectationResult = jRequire.buildExpectationResult(); 56 | j$.JsApiReporter = jRequire.JsApiReporter(); 57 | j$.matchersUtil = jRequire.matchersUtil(j$); 58 | j$.ObjectContaining = jRequire.ObjectContaining(j$); 59 | j$.ArrayContaining = jRequire.ArrayContaining(j$); 60 | j$.pp = jRequire.pp(j$); 61 | j$.QueueRunner = jRequire.QueueRunner(j$); 62 | j$.ReportDispatcher = jRequire.ReportDispatcher(); 63 | j$.Spec = jRequire.Spec(j$); 64 | j$.SpyRegistry = jRequire.SpyRegistry(j$); 65 | j$.SpyStrategy = jRequire.SpyStrategy(); 66 | j$.StringMatching = jRequire.StringMatching(j$); 67 | j$.Suite = jRequire.Suite(j$); 68 | j$.Timer = jRequire.Timer(); 69 | j$.TreeProcessor = jRequire.TreeProcessor(); 70 | j$.version = jRequire.version(); 71 | 72 | j$.matchers = jRequire.requireMatchers(jRequire, j$); 73 | 74 | return j$; 75 | }; 76 | 77 | return getJasmineRequire; 78 | })(this); 79 | 80 | getJasmineRequireObj().requireMatchers = function(jRequire, j$) { 81 | var availableMatchers = [ 82 | 'toBe', 83 | 'toBeCloseTo', 84 | 'toBeDefined', 85 | 'toBeFalsy', 86 | 'toBeGreaterThan', 87 | 'toBeLessThan', 88 | 'toBeNaN', 89 | 'toBeNull', 90 | 'toBeTruthy', 91 | 'toBeUndefined', 92 | 'toContain', 93 | 'toEqual', 94 | 'toHaveBeenCalled', 95 | 'toHaveBeenCalledWith', 96 | 'toMatch', 97 | 'toThrow', 98 | 'toThrowError' 99 | ], 100 | matchers = {}; 101 | 102 | for (var i = 0; i < availableMatchers.length; i++) { 103 | var name = availableMatchers[i]; 104 | matchers[name] = jRequire[name](j$); 105 | } 106 | 107 | return matchers; 108 | }; 109 | 110 | getJasmineRequireObj().base = function(j$, jasmineGlobal) { 111 | j$.unimplementedMethod_ = function() { 112 | throw new Error('unimplemented method'); 113 | }; 114 | 115 | j$.MAX_PRETTY_PRINT_DEPTH = 40; 116 | j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100; 117 | j$.DEFAULT_TIMEOUT_INTERVAL = 5000; 118 | 119 | j$.getGlobal = function() { 120 | return jasmineGlobal; 121 | }; 122 | 123 | j$.getEnv = function(options) { 124 | var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options); 125 | //jasmine. singletons in here (setTimeout blah blah). 126 | return env; 127 | }; 128 | 129 | j$.isArray_ = function(value) { 130 | return j$.isA_('Array', value); 131 | }; 132 | 133 | j$.isString_ = function(value) { 134 | return j$.isA_('String', value); 135 | }; 136 | 137 | j$.isNumber_ = function(value) { 138 | return j$.isA_('Number', value); 139 | }; 140 | 141 | j$.isA_ = function(typeName, value) { 142 | return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; 143 | }; 144 | 145 | j$.isDomNode = function(obj) { 146 | return obj.nodeType > 0; 147 | }; 148 | 149 | j$.fnNameFor = function(func) { 150 | return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1]; 151 | }; 152 | 153 | j$.any = function(clazz) { 154 | return new j$.Any(clazz); 155 | }; 156 | 157 | j$.anything = function() { 158 | return new j$.Anything(); 159 | }; 160 | 161 | j$.objectContaining = function(sample) { 162 | return new j$.ObjectContaining(sample); 163 | }; 164 | 165 | j$.stringMatching = function(expected) { 166 | return new j$.StringMatching(expected); 167 | }; 168 | 169 | j$.arrayContaining = function(sample) { 170 | return new j$.ArrayContaining(sample); 171 | }; 172 | 173 | j$.createSpy = function(name, originalFn) { 174 | 175 | var spyStrategy = new j$.SpyStrategy({ 176 | name: name, 177 | fn: originalFn, 178 | getSpy: function() { return spy; } 179 | }), 180 | callTracker = new j$.CallTracker(), 181 | spy = function() { 182 | var callData = { 183 | object: this, 184 | args: Array.prototype.slice.apply(arguments) 185 | }; 186 | 187 | callTracker.track(callData); 188 | var returnValue = spyStrategy.exec.apply(this, arguments); 189 | callData.returnValue = returnValue; 190 | 191 | return returnValue; 192 | }; 193 | 194 | for (var prop in originalFn) { 195 | if (prop === 'and' || prop === 'calls') { 196 | throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon'); 197 | } 198 | 199 | spy[prop] = originalFn[prop]; 200 | } 201 | 202 | spy.and = spyStrategy; 203 | spy.calls = callTracker; 204 | 205 | return spy; 206 | }; 207 | 208 | j$.isSpy = function(putativeSpy) { 209 | if (!putativeSpy) { 210 | return false; 211 | } 212 | return putativeSpy.and instanceof j$.SpyStrategy && 213 | putativeSpy.calls instanceof j$.CallTracker; 214 | }; 215 | 216 | j$.createSpyObj = function(baseName, methodNames) { 217 | if (j$.isArray_(baseName) && j$.util.isUndefined(methodNames)) { 218 | methodNames = baseName; 219 | baseName = 'unknown'; 220 | } 221 | 222 | if (!j$.isArray_(methodNames) || methodNames.length === 0) { 223 | throw 'createSpyObj requires a non-empty array of method names to create spies for'; 224 | } 225 | var obj = {}; 226 | for (var i = 0; i < methodNames.length; i++) { 227 | obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]); 228 | } 229 | return obj; 230 | }; 231 | }; 232 | 233 | getJasmineRequireObj().util = function() { 234 | 235 | var util = {}; 236 | 237 | util.inherit = function(childClass, parentClass) { 238 | var Subclass = function() { 239 | }; 240 | Subclass.prototype = parentClass.prototype; 241 | childClass.prototype = new Subclass(); 242 | }; 243 | 244 | util.htmlEscape = function(str) { 245 | if (!str) { 246 | return str; 247 | } 248 | return str.replace(/&/g, '&') 249 | .replace(//g, '>'); 251 | }; 252 | 253 | util.argsToArray = function(args) { 254 | var arrayOfArgs = []; 255 | for (var i = 0; i < args.length; i++) { 256 | arrayOfArgs.push(args[i]); 257 | } 258 | return arrayOfArgs; 259 | }; 260 | 261 | util.isUndefined = function(obj) { 262 | return obj === void 0; 263 | }; 264 | 265 | util.arrayContains = function(array, search) { 266 | var i = array.length; 267 | while (i--) { 268 | if (array[i] === search) { 269 | return true; 270 | } 271 | } 272 | return false; 273 | }; 274 | 275 | util.clone = function(obj) { 276 | if (Object.prototype.toString.apply(obj) === '[object Array]') { 277 | return obj.slice(); 278 | } 279 | 280 | var cloned = {}; 281 | for (var prop in obj) { 282 | if (obj.hasOwnProperty(prop)) { 283 | cloned[prop] = obj[prop]; 284 | } 285 | } 286 | 287 | return cloned; 288 | }; 289 | 290 | return util; 291 | }; 292 | 293 | getJasmineRequireObj().Spec = function(j$) { 294 | function Spec(attrs) { 295 | this.expectationFactory = attrs.expectationFactory; 296 | this.resultCallback = attrs.resultCallback || function() {}; 297 | this.id = attrs.id; 298 | this.description = attrs.description || ''; 299 | this.queueableFn = attrs.queueableFn; 300 | this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; }; 301 | this.userContext = attrs.userContext || function() { return {}; }; 302 | this.onStart = attrs.onStart || function() {}; 303 | this.getSpecName = attrs.getSpecName || function() { return ''; }; 304 | this.expectationResultFactory = attrs.expectationResultFactory || function() { }; 305 | this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; 306 | this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; 307 | this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; 308 | 309 | if (!this.queueableFn.fn) { 310 | this.pend(); 311 | } 312 | 313 | this.result = { 314 | id: this.id, 315 | description: this.description, 316 | fullName: this.getFullName(), 317 | failedExpectations: [], 318 | passedExpectations: [], 319 | pendingReason: '' 320 | }; 321 | } 322 | 323 | Spec.prototype.addExpectationResult = function(passed, data, isError) { 324 | var expectationResult = this.expectationResultFactory(data); 325 | if (passed) { 326 | this.result.passedExpectations.push(expectationResult); 327 | } else { 328 | this.result.failedExpectations.push(expectationResult); 329 | 330 | if (this.throwOnExpectationFailure && !isError) { 331 | throw new j$.errors.ExpectationFailed(); 332 | } 333 | } 334 | }; 335 | 336 | Spec.prototype.expect = function(actual) { 337 | return this.expectationFactory(actual, this); 338 | }; 339 | 340 | Spec.prototype.execute = function(onComplete, enabled) { 341 | var self = this; 342 | 343 | this.onStart(this); 344 | 345 | if (!this.isExecutable() || this.markedPending || enabled === false) { 346 | complete(enabled); 347 | return; 348 | } 349 | 350 | var fns = this.beforeAndAfterFns(); 351 | var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters); 352 | 353 | this.queueRunnerFactory({ 354 | queueableFns: allFns, 355 | onException: function() { self.onException.apply(self, arguments); }, 356 | onComplete: complete, 357 | userContext: this.userContext() 358 | }); 359 | 360 | function complete(enabledAgain) { 361 | self.result.status = self.status(enabledAgain); 362 | self.resultCallback(self.result); 363 | 364 | if (onComplete) { 365 | onComplete(); 366 | } 367 | } 368 | }; 369 | 370 | Spec.prototype.onException = function onException(e) { 371 | if (Spec.isPendingSpecException(e)) { 372 | this.pend(extractCustomPendingMessage(e)); 373 | return; 374 | } 375 | 376 | if (e instanceof j$.errors.ExpectationFailed) { 377 | return; 378 | } 379 | 380 | this.addExpectationResult(false, { 381 | matcherName: '', 382 | passed: false, 383 | expected: '', 384 | actual: '', 385 | error: e 386 | }, true); 387 | }; 388 | 389 | Spec.prototype.disable = function() { 390 | this.disabled = true; 391 | }; 392 | 393 | Spec.prototype.pend = function(message) { 394 | this.markedPending = true; 395 | if (message) { 396 | this.result.pendingReason = message; 397 | } 398 | }; 399 | 400 | Spec.prototype.getResult = function() { 401 | this.result.status = this.status(); 402 | return this.result; 403 | }; 404 | 405 | Spec.prototype.status = function(enabled) { 406 | if (this.disabled || enabled === false) { 407 | return 'disabled'; 408 | } 409 | 410 | if (this.markedPending) { 411 | return 'pending'; 412 | } 413 | 414 | if (this.result.failedExpectations.length > 0) { 415 | return 'failed'; 416 | } else { 417 | return 'passed'; 418 | } 419 | }; 420 | 421 | Spec.prototype.isExecutable = function() { 422 | return !this.disabled; 423 | }; 424 | 425 | Spec.prototype.getFullName = function() { 426 | return this.getSpecName(this); 427 | }; 428 | 429 | var extractCustomPendingMessage = function(e) { 430 | var fullMessage = e.toString(), 431 | boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), 432 | boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length; 433 | 434 | return fullMessage.substr(boilerplateEnd); 435 | }; 436 | 437 | Spec.pendingSpecExceptionMessage = '=> marked Pending'; 438 | 439 | Spec.isPendingSpecException = function(e) { 440 | return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1); 441 | }; 442 | 443 | return Spec; 444 | }; 445 | 446 | if (typeof window == void 0 && typeof exports == 'object') { 447 | exports.Spec = jasmineRequire.Spec; 448 | } 449 | 450 | getJasmineRequireObj().Env = function(j$) { 451 | function Env(options) { 452 | options = options || {}; 453 | 454 | var self = this; 455 | var global = options.global || j$.getGlobal(); 456 | 457 | var totalSpecsDefined = 0; 458 | 459 | var catchExceptions = true; 460 | 461 | var realSetTimeout = j$.getGlobal().setTimeout; 462 | var realClearTimeout = j$.getGlobal().clearTimeout; 463 | this.clock = new j$.Clock(global, function () { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global)); 464 | 465 | var runnableLookupTable = {}; 466 | var runnableResources = {}; 467 | 468 | var currentSpec = null; 469 | var currentlyExecutingSuites = []; 470 | var currentDeclarationSuite = null; 471 | var throwOnExpectationFailure = false; 472 | 473 | var currentSuite = function() { 474 | return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; 475 | }; 476 | 477 | var currentRunnable = function() { 478 | return currentSpec || currentSuite(); 479 | }; 480 | 481 | var reporter = new j$.ReportDispatcher([ 482 | 'jasmineStarted', 483 | 'jasmineDone', 484 | 'suiteStarted', 485 | 'suiteDone', 486 | 'specStarted', 487 | 'specDone' 488 | ]); 489 | 490 | this.specFilter = function() { 491 | return true; 492 | }; 493 | 494 | this.addCustomEqualityTester = function(tester) { 495 | if(!currentRunnable()) { 496 | throw new Error('Custom Equalities must be added in a before function or a spec'); 497 | } 498 | runnableResources[currentRunnable().id].customEqualityTesters.push(tester); 499 | }; 500 | 501 | this.addMatchers = function(matchersToAdd) { 502 | if(!currentRunnable()) { 503 | throw new Error('Matchers must be added in a before function or a spec'); 504 | } 505 | var customMatchers = runnableResources[currentRunnable().id].customMatchers; 506 | for (var matcherName in matchersToAdd) { 507 | customMatchers[matcherName] = matchersToAdd[matcherName]; 508 | } 509 | }; 510 | 511 | j$.Expectation.addCoreMatchers(j$.matchers); 512 | 513 | var nextSpecId = 0; 514 | var getNextSpecId = function() { 515 | return 'spec' + nextSpecId++; 516 | }; 517 | 518 | var nextSuiteId = 0; 519 | var getNextSuiteId = function() { 520 | return 'suite' + nextSuiteId++; 521 | }; 522 | 523 | var expectationFactory = function(actual, spec) { 524 | return j$.Expectation.Factory({ 525 | util: j$.matchersUtil, 526 | customEqualityTesters: runnableResources[spec.id].customEqualityTesters, 527 | customMatchers: runnableResources[spec.id].customMatchers, 528 | actual: actual, 529 | addExpectationResult: addExpectationResult 530 | }); 531 | 532 | function addExpectationResult(passed, result) { 533 | return spec.addExpectationResult(passed, result); 534 | } 535 | }; 536 | 537 | var defaultResourcesForRunnable = function(id, parentRunnableId) { 538 | var resources = {spies: [], customEqualityTesters: [], customMatchers: {}}; 539 | 540 | if(runnableResources[parentRunnableId]){ 541 | resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters); 542 | resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers); 543 | } 544 | 545 | runnableResources[id] = resources; 546 | }; 547 | 548 | var clearResourcesForRunnable = function(id) { 549 | spyRegistry.clearSpies(); 550 | delete runnableResources[id]; 551 | }; 552 | 553 | var beforeAndAfterFns = function(suite) { 554 | return function() { 555 | var befores = [], 556 | afters = []; 557 | 558 | while(suite) { 559 | befores = befores.concat(suite.beforeFns); 560 | afters = afters.concat(suite.afterFns); 561 | 562 | suite = suite.parentSuite; 563 | } 564 | 565 | return { 566 | befores: befores.reverse(), 567 | afters: afters 568 | }; 569 | }; 570 | }; 571 | 572 | var getSpecName = function(spec, suite) { 573 | return suite.getFullName() + ' ' + spec.description; 574 | }; 575 | 576 | // TODO: we may just be able to pass in the fn instead of wrapping here 577 | var buildExpectationResult = j$.buildExpectationResult, 578 | exceptionFormatter = new j$.ExceptionFormatter(), 579 | expectationResultFactory = function(attrs) { 580 | attrs.messageFormatter = exceptionFormatter.message; 581 | attrs.stackFormatter = exceptionFormatter.stack; 582 | 583 | return buildExpectationResult(attrs); 584 | }; 585 | 586 | // TODO: fix this naming, and here's where the value comes in 587 | this.catchExceptions = function(value) { 588 | catchExceptions = !!value; 589 | return catchExceptions; 590 | }; 591 | 592 | this.catchingExceptions = function() { 593 | return catchExceptions; 594 | }; 595 | 596 | var maximumSpecCallbackDepth = 20; 597 | var currentSpecCallbackDepth = 0; 598 | 599 | function clearStack(fn) { 600 | currentSpecCallbackDepth++; 601 | if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) { 602 | currentSpecCallbackDepth = 0; 603 | realSetTimeout(fn, 0); 604 | } else { 605 | fn(); 606 | } 607 | } 608 | 609 | var catchException = function(e) { 610 | return j$.Spec.isPendingSpecException(e) || catchExceptions; 611 | }; 612 | 613 | this.throwOnExpectationFailure = function(value) { 614 | throwOnExpectationFailure = !!value; 615 | }; 616 | 617 | this.throwingExpectationFailures = function() { 618 | return throwOnExpectationFailure; 619 | }; 620 | 621 | var queueRunnerFactory = function(options) { 622 | options.catchException = catchException; 623 | options.clearStack = options.clearStack || clearStack; 624 | options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; 625 | options.fail = self.fail; 626 | 627 | new j$.QueueRunner(options).execute(); 628 | }; 629 | 630 | var topSuite = new j$.Suite({ 631 | env: this, 632 | id: getNextSuiteId(), 633 | description: 'Jasmine__TopLevel__Suite', 634 | queueRunner: queueRunnerFactory 635 | }); 636 | runnableLookupTable[topSuite.id] = topSuite; 637 | defaultResourcesForRunnable(topSuite.id); 638 | currentDeclarationSuite = topSuite; 639 | 640 | this.topSuite = function() { 641 | return topSuite; 642 | }; 643 | 644 | this.execute = function(runnablesToRun) { 645 | if(!runnablesToRun) { 646 | if (focusedRunnables.length) { 647 | runnablesToRun = focusedRunnables; 648 | } else { 649 | runnablesToRun = [topSuite.id]; 650 | } 651 | } 652 | var processor = new j$.TreeProcessor({ 653 | tree: topSuite, 654 | runnableIds: runnablesToRun, 655 | queueRunnerFactory: queueRunnerFactory, 656 | nodeStart: function(suite) { 657 | currentlyExecutingSuites.push(suite); 658 | defaultResourcesForRunnable(suite.id, suite.parentSuite.id); 659 | reporter.suiteStarted(suite.result); 660 | }, 661 | nodeComplete: function(suite, result) { 662 | if (!suite.disabled) { 663 | clearResourcesForRunnable(suite.id); 664 | } 665 | currentlyExecutingSuites.pop(); 666 | reporter.suiteDone(result); 667 | } 668 | }); 669 | 670 | if(!processor.processTree().valid) { 671 | throw new Error('Invalid order: would cause a beforeAll or afterAll to be run multiple times'); 672 | } 673 | 674 | reporter.jasmineStarted({ 675 | totalSpecsDefined: totalSpecsDefined 676 | }); 677 | 678 | processor.execute(reporter.jasmineDone); 679 | }; 680 | 681 | this.addReporter = function(reporterToAdd) { 682 | reporter.addReporter(reporterToAdd); 683 | }; 684 | 685 | var spyRegistry = new j$.SpyRegistry({currentSpies: function() { 686 | if(!currentRunnable()) { 687 | throw new Error('Spies must be created in a before function or a spec'); 688 | } 689 | return runnableResources[currentRunnable().id].spies; 690 | }}); 691 | 692 | this.spyOn = function() { 693 | return spyRegistry.spyOn.apply(spyRegistry, arguments); 694 | }; 695 | 696 | var suiteFactory = function(description) { 697 | var suite = new j$.Suite({ 698 | env: self, 699 | id: getNextSuiteId(), 700 | description: description, 701 | parentSuite: currentDeclarationSuite, 702 | expectationFactory: expectationFactory, 703 | expectationResultFactory: expectationResultFactory, 704 | throwOnExpectationFailure: throwOnExpectationFailure 705 | }); 706 | 707 | runnableLookupTable[suite.id] = suite; 708 | return suite; 709 | }; 710 | 711 | this.describe = function(description, specDefinitions) { 712 | var suite = suiteFactory(description); 713 | addSpecsToSuite(suite, specDefinitions); 714 | return suite; 715 | }; 716 | 717 | this.xdescribe = function(description, specDefinitions) { 718 | var suite = this.describe(description, specDefinitions); 719 | suite.disable(); 720 | return suite; 721 | }; 722 | 723 | var focusedRunnables = []; 724 | 725 | this.fdescribe = function(description, specDefinitions) { 726 | var suite = suiteFactory(description); 727 | suite.isFocused = true; 728 | 729 | focusedRunnables.push(suite.id); 730 | unfocusAncestor(); 731 | addSpecsToSuite(suite, specDefinitions); 732 | 733 | return suite; 734 | }; 735 | 736 | function addSpecsToSuite(suite, specDefinitions) { 737 | var parentSuite = currentDeclarationSuite; 738 | parentSuite.addChild(suite); 739 | currentDeclarationSuite = suite; 740 | 741 | var declarationError = null; 742 | try { 743 | specDefinitions.call(suite); 744 | } catch (e) { 745 | declarationError = e; 746 | } 747 | 748 | if (declarationError) { 749 | self.it('encountered a declaration exception', function() { 750 | throw declarationError; 751 | }); 752 | } 753 | 754 | currentDeclarationSuite = parentSuite; 755 | } 756 | 757 | function findFocusedAncestor(suite) { 758 | while (suite) { 759 | if (suite.isFocused) { 760 | return suite.id; 761 | } 762 | suite = suite.parentSuite; 763 | } 764 | 765 | return null; 766 | } 767 | 768 | function unfocusAncestor() { 769 | var focusedAncestor = findFocusedAncestor(currentDeclarationSuite); 770 | if (focusedAncestor) { 771 | for (var i = 0; i < focusedRunnables.length; i++) { 772 | if (focusedRunnables[i] === focusedAncestor) { 773 | focusedRunnables.splice(i, 1); 774 | break; 775 | } 776 | } 777 | } 778 | } 779 | 780 | var specFactory = function(description, fn, suite, timeout) { 781 | totalSpecsDefined++; 782 | var spec = new j$.Spec({ 783 | id: getNextSpecId(), 784 | beforeAndAfterFns: beforeAndAfterFns(suite), 785 | expectationFactory: expectationFactory, 786 | resultCallback: specResultCallback, 787 | getSpecName: function(spec) { 788 | return getSpecName(spec, suite); 789 | }, 790 | onStart: specStarted, 791 | description: description, 792 | expectationResultFactory: expectationResultFactory, 793 | queueRunnerFactory: queueRunnerFactory, 794 | userContext: function() { return suite.clonedSharedUserContext(); }, 795 | queueableFn: { 796 | fn: fn, 797 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } 798 | }, 799 | throwOnExpectationFailure: throwOnExpectationFailure 800 | }); 801 | 802 | runnableLookupTable[spec.id] = spec; 803 | 804 | if (!self.specFilter(spec)) { 805 | spec.disable(); 806 | } 807 | 808 | return spec; 809 | 810 | function specResultCallback(result) { 811 | clearResourcesForRunnable(spec.id); 812 | currentSpec = null; 813 | reporter.specDone(result); 814 | } 815 | 816 | function specStarted(spec) { 817 | currentSpec = spec; 818 | defaultResourcesForRunnable(spec.id, suite.id); 819 | reporter.specStarted(spec.result); 820 | } 821 | }; 822 | 823 | this.it = function(description, fn, timeout) { 824 | var spec = specFactory(description, fn, currentDeclarationSuite, timeout); 825 | currentDeclarationSuite.addChild(spec); 826 | return spec; 827 | }; 828 | 829 | this.xit = function() { 830 | var spec = this.it.apply(this, arguments); 831 | spec.pend(); 832 | return spec; 833 | }; 834 | 835 | this.fit = function(){ 836 | var spec = this.it.apply(this, arguments); 837 | 838 | focusedRunnables.push(spec.id); 839 | unfocusAncestor(); 840 | return spec; 841 | }; 842 | 843 | this.expect = function(actual) { 844 | if (!currentRunnable()) { 845 | throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out'); 846 | } 847 | 848 | return currentRunnable().expect(actual); 849 | }; 850 | 851 | this.beforeEach = function(beforeEachFunction, timeout) { 852 | currentDeclarationSuite.beforeEach({ 853 | fn: beforeEachFunction, 854 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } 855 | }); 856 | }; 857 | 858 | this.beforeAll = function(beforeAllFunction, timeout) { 859 | currentDeclarationSuite.beforeAll({ 860 | fn: beforeAllFunction, 861 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } 862 | }); 863 | }; 864 | 865 | this.afterEach = function(afterEachFunction, timeout) { 866 | currentDeclarationSuite.afterEach({ 867 | fn: afterEachFunction, 868 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } 869 | }); 870 | }; 871 | 872 | this.afterAll = function(afterAllFunction, timeout) { 873 | currentDeclarationSuite.afterAll({ 874 | fn: afterAllFunction, 875 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } 876 | }); 877 | }; 878 | 879 | this.pending = function(message) { 880 | var fullMessage = j$.Spec.pendingSpecExceptionMessage; 881 | if(message) { 882 | fullMessage += message; 883 | } 884 | throw fullMessage; 885 | }; 886 | 887 | this.fail = function(error) { 888 | var message = 'Failed'; 889 | if (error) { 890 | message += ': '; 891 | message += error.message || error; 892 | } 893 | 894 | currentRunnable().addExpectationResult(false, { 895 | matcherName: '', 896 | passed: false, 897 | expected: '', 898 | actual: '', 899 | message: message, 900 | error: error && error.message ? error : null 901 | }); 902 | }; 903 | } 904 | 905 | return Env; 906 | }; 907 | 908 | getJasmineRequireObj().JsApiReporter = function() { 909 | 910 | var noopTimer = { 911 | start: function(){}, 912 | elapsed: function(){ return 0; } 913 | }; 914 | 915 | function JsApiReporter(options) { 916 | var timer = options.timer || noopTimer, 917 | status = 'loaded'; 918 | 919 | this.started = false; 920 | this.finished = false; 921 | 922 | this.jasmineStarted = function() { 923 | this.started = true; 924 | status = 'started'; 925 | timer.start(); 926 | }; 927 | 928 | var executionTime; 929 | 930 | this.jasmineDone = function() { 931 | this.finished = true; 932 | executionTime = timer.elapsed(); 933 | status = 'done'; 934 | }; 935 | 936 | this.status = function() { 937 | return status; 938 | }; 939 | 940 | var suites = [], 941 | suites_hash = {}; 942 | 943 | this.suiteStarted = function(result) { 944 | suites_hash[result.id] = result; 945 | }; 946 | 947 | this.suiteDone = function(result) { 948 | storeSuite(result); 949 | }; 950 | 951 | this.suiteResults = function(index, length) { 952 | return suites.slice(index, index + length); 953 | }; 954 | 955 | function storeSuite(result) { 956 | suites.push(result); 957 | suites_hash[result.id] = result; 958 | } 959 | 960 | this.suites = function() { 961 | return suites_hash; 962 | }; 963 | 964 | var specs = []; 965 | 966 | this.specDone = function(result) { 967 | specs.push(result); 968 | }; 969 | 970 | this.specResults = function(index, length) { 971 | return specs.slice(index, index + length); 972 | }; 973 | 974 | this.specs = function() { 975 | return specs; 976 | }; 977 | 978 | this.executionTime = function() { 979 | return executionTime; 980 | }; 981 | 982 | } 983 | 984 | return JsApiReporter; 985 | }; 986 | 987 | getJasmineRequireObj().CallTracker = function() { 988 | 989 | function CallTracker() { 990 | var calls = []; 991 | 992 | this.track = function(context) { 993 | calls.push(context); 994 | }; 995 | 996 | this.any = function() { 997 | return !!calls.length; 998 | }; 999 | 1000 | this.count = function() { 1001 | return calls.length; 1002 | }; 1003 | 1004 | this.argsFor = function(index) { 1005 | var call = calls[index]; 1006 | return call ? call.args : []; 1007 | }; 1008 | 1009 | this.all = function() { 1010 | return calls; 1011 | }; 1012 | 1013 | this.allArgs = function() { 1014 | var callArgs = []; 1015 | for(var i = 0; i < calls.length; i++){ 1016 | callArgs.push(calls[i].args); 1017 | } 1018 | 1019 | return callArgs; 1020 | }; 1021 | 1022 | this.first = function() { 1023 | return calls[0]; 1024 | }; 1025 | 1026 | this.mostRecent = function() { 1027 | return calls[calls.length - 1]; 1028 | }; 1029 | 1030 | this.reset = function() { 1031 | calls = []; 1032 | }; 1033 | } 1034 | 1035 | return CallTracker; 1036 | }; 1037 | 1038 | getJasmineRequireObj().Clock = function() { 1039 | function Clock(global, delayedFunctionSchedulerFactory, mockDate) { 1040 | var self = this, 1041 | realTimingFunctions = { 1042 | setTimeout: global.setTimeout, 1043 | clearTimeout: global.clearTimeout, 1044 | setInterval: global.setInterval, 1045 | clearInterval: global.clearInterval 1046 | }, 1047 | fakeTimingFunctions = { 1048 | setTimeout: setTimeout, 1049 | clearTimeout: clearTimeout, 1050 | setInterval: setInterval, 1051 | clearInterval: clearInterval 1052 | }, 1053 | installed = false, 1054 | delayedFunctionScheduler, 1055 | timer; 1056 | 1057 | 1058 | self.install = function() { 1059 | if(!originalTimingFunctionsIntact()) { 1060 | throw new Error('Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?'); 1061 | } 1062 | replace(global, fakeTimingFunctions); 1063 | timer = fakeTimingFunctions; 1064 | delayedFunctionScheduler = delayedFunctionSchedulerFactory(); 1065 | installed = true; 1066 | 1067 | return self; 1068 | }; 1069 | 1070 | self.uninstall = function() { 1071 | delayedFunctionScheduler = null; 1072 | mockDate.uninstall(); 1073 | replace(global, realTimingFunctions); 1074 | 1075 | timer = realTimingFunctions; 1076 | installed = false; 1077 | }; 1078 | 1079 | self.withMock = function(closure) { 1080 | this.install(); 1081 | try { 1082 | closure(); 1083 | } finally { 1084 | this.uninstall(); 1085 | } 1086 | }; 1087 | 1088 | self.mockDate = function(initialDate) { 1089 | mockDate.install(initialDate); 1090 | }; 1091 | 1092 | self.setTimeout = function(fn, delay, params) { 1093 | if (legacyIE()) { 1094 | if (arguments.length > 2) { 1095 | throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill'); 1096 | } 1097 | return timer.setTimeout(fn, delay); 1098 | } 1099 | return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]); 1100 | }; 1101 | 1102 | self.setInterval = function(fn, delay, params) { 1103 | if (legacyIE()) { 1104 | if (arguments.length > 2) { 1105 | throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill'); 1106 | } 1107 | return timer.setInterval(fn, delay); 1108 | } 1109 | return Function.prototype.apply.apply(timer.setInterval, [global, arguments]); 1110 | }; 1111 | 1112 | self.clearTimeout = function(id) { 1113 | return Function.prototype.call.apply(timer.clearTimeout, [global, id]); 1114 | }; 1115 | 1116 | self.clearInterval = function(id) { 1117 | return Function.prototype.call.apply(timer.clearInterval, [global, id]); 1118 | }; 1119 | 1120 | self.tick = function(millis) { 1121 | if (installed) { 1122 | mockDate.tick(millis); 1123 | delayedFunctionScheduler.tick(millis); 1124 | } else { 1125 | throw new Error('Mock clock is not installed, use jasmine.clock().install()'); 1126 | } 1127 | }; 1128 | 1129 | return self; 1130 | 1131 | function originalTimingFunctionsIntact() { 1132 | return global.setTimeout === realTimingFunctions.setTimeout && 1133 | global.clearTimeout === realTimingFunctions.clearTimeout && 1134 | global.setInterval === realTimingFunctions.setInterval && 1135 | global.clearInterval === realTimingFunctions.clearInterval; 1136 | } 1137 | 1138 | function legacyIE() { 1139 | //if these methods are polyfilled, apply will be present 1140 | return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply; 1141 | } 1142 | 1143 | function replace(dest, source) { 1144 | for (var prop in source) { 1145 | dest[prop] = source[prop]; 1146 | } 1147 | } 1148 | 1149 | function setTimeout(fn, delay) { 1150 | return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); 1151 | } 1152 | 1153 | function clearTimeout(id) { 1154 | return delayedFunctionScheduler.removeFunctionWithId(id); 1155 | } 1156 | 1157 | function setInterval(fn, interval) { 1158 | return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); 1159 | } 1160 | 1161 | function clearInterval(id) { 1162 | return delayedFunctionScheduler.removeFunctionWithId(id); 1163 | } 1164 | 1165 | function argSlice(argsObj, n) { 1166 | return Array.prototype.slice.call(argsObj, n); 1167 | } 1168 | } 1169 | 1170 | return Clock; 1171 | }; 1172 | 1173 | getJasmineRequireObj().DelayedFunctionScheduler = function() { 1174 | function DelayedFunctionScheduler() { 1175 | var self = this; 1176 | var scheduledLookup = []; 1177 | var scheduledFunctions = {}; 1178 | var currentTime = 0; 1179 | var delayedFnCount = 0; 1180 | 1181 | self.tick = function(millis) { 1182 | millis = millis || 0; 1183 | var endTime = currentTime + millis; 1184 | 1185 | runScheduledFunctions(endTime); 1186 | currentTime = endTime; 1187 | }; 1188 | 1189 | self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) { 1190 | var f; 1191 | if (typeof(funcToCall) === 'string') { 1192 | /* jshint evil: true */ 1193 | f = function() { return eval(funcToCall); }; 1194 | /* jshint evil: false */ 1195 | } else { 1196 | f = funcToCall; 1197 | } 1198 | 1199 | millis = millis || 0; 1200 | timeoutKey = timeoutKey || ++delayedFnCount; 1201 | runAtMillis = runAtMillis || (currentTime + millis); 1202 | 1203 | var funcToSchedule = { 1204 | runAtMillis: runAtMillis, 1205 | funcToCall: f, 1206 | recurring: recurring, 1207 | params: params, 1208 | timeoutKey: timeoutKey, 1209 | millis: millis 1210 | }; 1211 | 1212 | if (runAtMillis in scheduledFunctions) { 1213 | scheduledFunctions[runAtMillis].push(funcToSchedule); 1214 | } else { 1215 | scheduledFunctions[runAtMillis] = [funcToSchedule]; 1216 | scheduledLookup.push(runAtMillis); 1217 | scheduledLookup.sort(function (a, b) { 1218 | return a - b; 1219 | }); 1220 | } 1221 | 1222 | return timeoutKey; 1223 | }; 1224 | 1225 | self.removeFunctionWithId = function(timeoutKey) { 1226 | for (var runAtMillis in scheduledFunctions) { 1227 | var funcs = scheduledFunctions[runAtMillis]; 1228 | var i = indexOfFirstToPass(funcs, function (func) { 1229 | return func.timeoutKey === timeoutKey; 1230 | }); 1231 | 1232 | if (i > -1) { 1233 | if (funcs.length === 1) { 1234 | delete scheduledFunctions[runAtMillis]; 1235 | deleteFromLookup(runAtMillis); 1236 | } else { 1237 | funcs.splice(i, 1); 1238 | } 1239 | 1240 | // intervals get rescheduled when executed, so there's never more 1241 | // than a single scheduled function with a given timeoutKey 1242 | break; 1243 | } 1244 | } 1245 | }; 1246 | 1247 | return self; 1248 | 1249 | function indexOfFirstToPass(array, testFn) { 1250 | var index = -1; 1251 | 1252 | for (var i = 0; i < array.length; ++i) { 1253 | if (testFn(array[i])) { 1254 | index = i; 1255 | break; 1256 | } 1257 | } 1258 | 1259 | return index; 1260 | } 1261 | 1262 | function deleteFromLookup(key) { 1263 | var value = Number(key); 1264 | var i = indexOfFirstToPass(scheduledLookup, function (millis) { 1265 | return millis === value; 1266 | }); 1267 | 1268 | if (i > -1) { 1269 | scheduledLookup.splice(i, 1); 1270 | } 1271 | } 1272 | 1273 | function reschedule(scheduledFn) { 1274 | self.scheduleFunction(scheduledFn.funcToCall, 1275 | scheduledFn.millis, 1276 | scheduledFn.params, 1277 | true, 1278 | scheduledFn.timeoutKey, 1279 | scheduledFn.runAtMillis + scheduledFn.millis); 1280 | } 1281 | 1282 | function forEachFunction(funcsToRun, callback) { 1283 | for (var i = 0; i < funcsToRun.length; ++i) { 1284 | callback(funcsToRun[i]); 1285 | } 1286 | } 1287 | 1288 | function runScheduledFunctions(endTime) { 1289 | if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { 1290 | return; 1291 | } 1292 | 1293 | do { 1294 | currentTime = scheduledLookup.shift(); 1295 | 1296 | var funcsToRun = scheduledFunctions[currentTime]; 1297 | delete scheduledFunctions[currentTime]; 1298 | 1299 | forEachFunction(funcsToRun, function(funcToRun) { 1300 | if (funcToRun.recurring) { 1301 | reschedule(funcToRun); 1302 | } 1303 | }); 1304 | 1305 | forEachFunction(funcsToRun, function(funcToRun) { 1306 | funcToRun.funcToCall.apply(null, funcToRun.params || []); 1307 | }); 1308 | } while (scheduledLookup.length > 0 && 1309 | // checking first if we're out of time prevents setTimeout(0) 1310 | // scheduled in a funcToRun from forcing an extra iteration 1311 | currentTime !== endTime && 1312 | scheduledLookup[0] <= endTime); 1313 | } 1314 | } 1315 | 1316 | return DelayedFunctionScheduler; 1317 | }; 1318 | 1319 | getJasmineRequireObj().ExceptionFormatter = function() { 1320 | function ExceptionFormatter() { 1321 | this.message = function(error) { 1322 | var message = ''; 1323 | 1324 | if (error.name && error.message) { 1325 | message += error.name + ': ' + error.message; 1326 | } else { 1327 | message += error.toString() + ' thrown'; 1328 | } 1329 | 1330 | if (error.fileName || error.sourceURL) { 1331 | message += ' in ' + (error.fileName || error.sourceURL); 1332 | } 1333 | 1334 | if (error.line || error.lineNumber) { 1335 | message += ' (line ' + (error.line || error.lineNumber) + ')'; 1336 | } 1337 | 1338 | return message; 1339 | }; 1340 | 1341 | this.stack = function(error) { 1342 | return error ? error.stack : null; 1343 | }; 1344 | } 1345 | 1346 | return ExceptionFormatter; 1347 | }; 1348 | 1349 | getJasmineRequireObj().Expectation = function() { 1350 | 1351 | function Expectation(options) { 1352 | this.util = options.util || { buildFailureMessage: function() {} }; 1353 | this.customEqualityTesters = options.customEqualityTesters || []; 1354 | this.actual = options.actual; 1355 | this.addExpectationResult = options.addExpectationResult || function(){}; 1356 | this.isNot = options.isNot; 1357 | 1358 | var customMatchers = options.customMatchers || {}; 1359 | for (var matcherName in customMatchers) { 1360 | this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]); 1361 | } 1362 | } 1363 | 1364 | Expectation.prototype.wrapCompare = function(name, matcherFactory) { 1365 | return function() { 1366 | var args = Array.prototype.slice.call(arguments, 0), 1367 | expected = args.slice(0), 1368 | message = ''; 1369 | 1370 | args.unshift(this.actual); 1371 | 1372 | var matcher = matcherFactory(this.util, this.customEqualityTesters), 1373 | matcherCompare = matcher.compare; 1374 | 1375 | function defaultNegativeCompare() { 1376 | var result = matcher.compare.apply(null, args); 1377 | result.pass = !result.pass; 1378 | return result; 1379 | } 1380 | 1381 | if (this.isNot) { 1382 | matcherCompare = matcher.negativeCompare || defaultNegativeCompare; 1383 | } 1384 | 1385 | var result = matcherCompare.apply(null, args); 1386 | 1387 | if (!result.pass) { 1388 | if (!result.message) { 1389 | args.unshift(this.isNot); 1390 | args.unshift(name); 1391 | message = this.util.buildFailureMessage.apply(null, args); 1392 | } else { 1393 | if (Object.prototype.toString.apply(result.message) === '[object Function]') { 1394 | message = result.message(); 1395 | } else { 1396 | message = result.message; 1397 | } 1398 | } 1399 | } 1400 | 1401 | if (expected.length == 1) { 1402 | expected = expected[0]; 1403 | } 1404 | 1405 | // TODO: how many of these params are needed? 1406 | this.addExpectationResult( 1407 | result.pass, 1408 | { 1409 | matcherName: name, 1410 | passed: result.pass, 1411 | message: message, 1412 | actual: this.actual, 1413 | expected: expected // TODO: this may need to be arrayified/sliced 1414 | } 1415 | ); 1416 | }; 1417 | }; 1418 | 1419 | Expectation.addCoreMatchers = function(matchers) { 1420 | var prototype = Expectation.prototype; 1421 | for (var matcherName in matchers) { 1422 | var matcher = matchers[matcherName]; 1423 | prototype[matcherName] = prototype.wrapCompare(matcherName, matcher); 1424 | } 1425 | }; 1426 | 1427 | Expectation.Factory = function(options) { 1428 | options = options || {}; 1429 | 1430 | var expect = new Expectation(options); 1431 | 1432 | // TODO: this would be nice as its own Object - NegativeExpectation 1433 | // TODO: copy instead of mutate options 1434 | options.isNot = true; 1435 | expect.not = new Expectation(options); 1436 | 1437 | return expect; 1438 | }; 1439 | 1440 | return Expectation; 1441 | }; 1442 | 1443 | //TODO: expectation result may make more sense as a presentation of an expectation. 1444 | getJasmineRequireObj().buildExpectationResult = function() { 1445 | function buildExpectationResult(options) { 1446 | var messageFormatter = options.messageFormatter || function() {}, 1447 | stackFormatter = options.stackFormatter || function() {}; 1448 | 1449 | var result = { 1450 | matcherName: options.matcherName, 1451 | message: message(), 1452 | stack: stack(), 1453 | passed: options.passed 1454 | }; 1455 | 1456 | if(!result.passed) { 1457 | result.expected = options.expected; 1458 | result.actual = options.actual; 1459 | } 1460 | 1461 | return result; 1462 | 1463 | function message() { 1464 | if (options.passed) { 1465 | return 'Passed.'; 1466 | } else if (options.message) { 1467 | return options.message; 1468 | } else if (options.error) { 1469 | return messageFormatter(options.error); 1470 | } 1471 | return ''; 1472 | } 1473 | 1474 | function stack() { 1475 | if (options.passed) { 1476 | return ''; 1477 | } 1478 | 1479 | var error = options.error; 1480 | if (!error) { 1481 | try { 1482 | throw new Error(message()); 1483 | } catch (e) { 1484 | error = e; 1485 | } 1486 | } 1487 | return stackFormatter(error); 1488 | } 1489 | } 1490 | 1491 | return buildExpectationResult; 1492 | }; 1493 | 1494 | getJasmineRequireObj().MockDate = function() { 1495 | function MockDate(global) { 1496 | var self = this; 1497 | var currentTime = 0; 1498 | 1499 | if (!global || !global.Date) { 1500 | self.install = function() {}; 1501 | self.tick = function() {}; 1502 | self.uninstall = function() {}; 1503 | return self; 1504 | } 1505 | 1506 | var GlobalDate = global.Date; 1507 | 1508 | self.install = function(mockDate) { 1509 | if (mockDate instanceof GlobalDate) { 1510 | currentTime = mockDate.getTime(); 1511 | } else { 1512 | currentTime = new GlobalDate().getTime(); 1513 | } 1514 | 1515 | global.Date = FakeDate; 1516 | }; 1517 | 1518 | self.tick = function(millis) { 1519 | millis = millis || 0; 1520 | currentTime = currentTime + millis; 1521 | }; 1522 | 1523 | self.uninstall = function() { 1524 | currentTime = 0; 1525 | global.Date = GlobalDate; 1526 | }; 1527 | 1528 | createDateProperties(); 1529 | 1530 | return self; 1531 | 1532 | function FakeDate() { 1533 | switch(arguments.length) { 1534 | case 0: 1535 | return new GlobalDate(currentTime); 1536 | case 1: 1537 | return new GlobalDate(arguments[0]); 1538 | case 2: 1539 | return new GlobalDate(arguments[0], arguments[1]); 1540 | case 3: 1541 | return new GlobalDate(arguments[0], arguments[1], arguments[2]); 1542 | case 4: 1543 | return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]); 1544 | case 5: 1545 | return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], 1546 | arguments[4]); 1547 | case 6: 1548 | return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], 1549 | arguments[4], arguments[5]); 1550 | default: 1551 | return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], 1552 | arguments[4], arguments[5], arguments[6]); 1553 | } 1554 | } 1555 | 1556 | function createDateProperties() { 1557 | FakeDate.prototype = GlobalDate.prototype; 1558 | 1559 | FakeDate.now = function() { 1560 | if (GlobalDate.now) { 1561 | return currentTime; 1562 | } else { 1563 | throw new Error('Browser does not support Date.now()'); 1564 | } 1565 | }; 1566 | 1567 | FakeDate.toSource = GlobalDate.toSource; 1568 | FakeDate.toString = GlobalDate.toString; 1569 | FakeDate.parse = GlobalDate.parse; 1570 | FakeDate.UTC = GlobalDate.UTC; 1571 | } 1572 | } 1573 | 1574 | return MockDate; 1575 | }; 1576 | 1577 | getJasmineRequireObj().pp = function(j$) { 1578 | 1579 | function PrettyPrinter() { 1580 | this.ppNestLevel_ = 0; 1581 | this.seen = []; 1582 | } 1583 | 1584 | PrettyPrinter.prototype.format = function(value) { 1585 | this.ppNestLevel_++; 1586 | try { 1587 | if (j$.util.isUndefined(value)) { 1588 | this.emitScalar('undefined'); 1589 | } else if (value === null) { 1590 | this.emitScalar('null'); 1591 | } else if (value === 0 && 1/value === -Infinity) { 1592 | this.emitScalar('-0'); 1593 | } else if (value === j$.getGlobal()) { 1594 | this.emitScalar(''); 1595 | } else if (value.jasmineToString) { 1596 | this.emitScalar(value.jasmineToString()); 1597 | } else if (typeof value === 'string') { 1598 | this.emitString(value); 1599 | } else if (j$.isSpy(value)) { 1600 | this.emitScalar('spy on ' + value.and.identity()); 1601 | } else if (value instanceof RegExp) { 1602 | this.emitScalar(value.toString()); 1603 | } else if (typeof value === 'function') { 1604 | this.emitScalar('Function'); 1605 | } else if (typeof value.nodeType === 'number') { 1606 | this.emitScalar('HTMLNode'); 1607 | } else if (value instanceof Date) { 1608 | this.emitScalar('Date(' + value + ')'); 1609 | } else if (j$.util.arrayContains(this.seen, value)) { 1610 | this.emitScalar(''); 1611 | } else if (j$.isArray_(value) || j$.isA_('Object', value)) { 1612 | this.seen.push(value); 1613 | if (j$.isArray_(value)) { 1614 | this.emitArray(value); 1615 | } else { 1616 | this.emitObject(value); 1617 | } 1618 | this.seen.pop(); 1619 | } else { 1620 | this.emitScalar(value.toString()); 1621 | } 1622 | } finally { 1623 | this.ppNestLevel_--; 1624 | } 1625 | }; 1626 | 1627 | PrettyPrinter.prototype.iterateObject = function(obj, fn) { 1628 | for (var property in obj) { 1629 | if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; } 1630 | fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) && 1631 | obj.__lookupGetter__(property) !== null) : false); 1632 | } 1633 | }; 1634 | 1635 | PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; 1636 | PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; 1637 | PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; 1638 | PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; 1639 | 1640 | function StringPrettyPrinter() { 1641 | PrettyPrinter.call(this); 1642 | 1643 | this.string = ''; 1644 | } 1645 | 1646 | j$.util.inherit(StringPrettyPrinter, PrettyPrinter); 1647 | 1648 | StringPrettyPrinter.prototype.emitScalar = function(value) { 1649 | this.append(value); 1650 | }; 1651 | 1652 | StringPrettyPrinter.prototype.emitString = function(value) { 1653 | this.append('\'' + value + '\''); 1654 | }; 1655 | 1656 | StringPrettyPrinter.prototype.emitArray = function(array) { 1657 | if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { 1658 | this.append('Array'); 1659 | return; 1660 | } 1661 | var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); 1662 | this.append('[ '); 1663 | for (var i = 0; i < length; i++) { 1664 | if (i > 0) { 1665 | this.append(', '); 1666 | } 1667 | this.format(array[i]); 1668 | } 1669 | if(array.length > length){ 1670 | this.append(', ...'); 1671 | } 1672 | 1673 | var self = this; 1674 | var first = array.length === 0; 1675 | this.iterateObject(array, function(property, isGetter) { 1676 | if (property.match(/^\d+$/)) { 1677 | return; 1678 | } 1679 | 1680 | if (first) { 1681 | first = false; 1682 | } else { 1683 | self.append(', '); 1684 | } 1685 | 1686 | self.formatProperty(array, property, isGetter); 1687 | }); 1688 | 1689 | this.append(' ]'); 1690 | }; 1691 | 1692 | StringPrettyPrinter.prototype.emitObject = function(obj) { 1693 | var constructorName = obj.constructor ? j$.fnNameFor(obj.constructor) : 'null'; 1694 | this.append(constructorName); 1695 | 1696 | if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { 1697 | return; 1698 | } 1699 | 1700 | var self = this; 1701 | this.append('({ '); 1702 | var first = true; 1703 | 1704 | this.iterateObject(obj, function(property, isGetter) { 1705 | if (first) { 1706 | first = false; 1707 | } else { 1708 | self.append(', '); 1709 | } 1710 | 1711 | self.formatProperty(obj, property, isGetter); 1712 | }); 1713 | 1714 | this.append(' })'); 1715 | }; 1716 | 1717 | StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { 1718 | this.append(property); 1719 | this.append(': '); 1720 | if (isGetter) { 1721 | this.append(''); 1722 | } else { 1723 | this.format(obj[property]); 1724 | } 1725 | }; 1726 | 1727 | StringPrettyPrinter.prototype.append = function(value) { 1728 | this.string += value; 1729 | }; 1730 | 1731 | return function(value) { 1732 | var stringPrettyPrinter = new StringPrettyPrinter(); 1733 | stringPrettyPrinter.format(value); 1734 | return stringPrettyPrinter.string; 1735 | }; 1736 | }; 1737 | 1738 | getJasmineRequireObj().QueueRunner = function(j$) { 1739 | 1740 | function once(fn) { 1741 | var called = false; 1742 | return function() { 1743 | if (!called) { 1744 | called = true; 1745 | fn(); 1746 | } 1747 | }; 1748 | } 1749 | 1750 | function QueueRunner(attrs) { 1751 | this.queueableFns = attrs.queueableFns || []; 1752 | this.onComplete = attrs.onComplete || function() {}; 1753 | this.clearStack = attrs.clearStack || function(fn) {fn();}; 1754 | this.onException = attrs.onException || function() {}; 1755 | this.catchException = attrs.catchException || function() { return true; }; 1756 | this.userContext = attrs.userContext || {}; 1757 | this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; 1758 | this.fail = attrs.fail || function() {}; 1759 | } 1760 | 1761 | QueueRunner.prototype.execute = function() { 1762 | this.run(this.queueableFns, 0); 1763 | }; 1764 | 1765 | QueueRunner.prototype.run = function(queueableFns, recursiveIndex) { 1766 | var length = queueableFns.length, 1767 | self = this, 1768 | iterativeIndex; 1769 | 1770 | 1771 | for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) { 1772 | var queueableFn = queueableFns[iterativeIndex]; 1773 | if (queueableFn.fn.length > 0) { 1774 | attemptAsync(queueableFn); 1775 | return; 1776 | } else { 1777 | attemptSync(queueableFn); 1778 | } 1779 | } 1780 | 1781 | var runnerDone = iterativeIndex >= length; 1782 | 1783 | if (runnerDone) { 1784 | this.clearStack(this.onComplete); 1785 | } 1786 | 1787 | function attemptSync(queueableFn) { 1788 | try { 1789 | queueableFn.fn.call(self.userContext); 1790 | } catch (e) { 1791 | handleException(e, queueableFn); 1792 | } 1793 | } 1794 | 1795 | function attemptAsync(queueableFn) { 1796 | var clearTimeout = function () { 1797 | Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]); 1798 | }, 1799 | next = once(function () { 1800 | clearTimeout(timeoutId); 1801 | self.run(queueableFns, iterativeIndex + 1); 1802 | }), 1803 | timeoutId; 1804 | 1805 | next.fail = function() { 1806 | self.fail.apply(null, arguments); 1807 | next(); 1808 | }; 1809 | 1810 | if (queueableFn.timeout) { 1811 | timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() { 1812 | var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'); 1813 | onException(error, queueableFn); 1814 | next(); 1815 | }, queueableFn.timeout()]]); 1816 | } 1817 | 1818 | try { 1819 | queueableFn.fn.call(self.userContext, next); 1820 | } catch (e) { 1821 | handleException(e, queueableFn); 1822 | next(); 1823 | } 1824 | } 1825 | 1826 | function onException(e, queueableFn) { 1827 | self.onException(e); 1828 | } 1829 | 1830 | function handleException(e, queueableFn) { 1831 | onException(e, queueableFn); 1832 | if (!self.catchException(e)) { 1833 | //TODO: set a var when we catch an exception and 1834 | //use a finally block to close the loop in a nice way.. 1835 | throw e; 1836 | } 1837 | } 1838 | }; 1839 | 1840 | return QueueRunner; 1841 | }; 1842 | 1843 | getJasmineRequireObj().ReportDispatcher = function() { 1844 | function ReportDispatcher(methods) { 1845 | 1846 | var dispatchedMethods = methods || []; 1847 | 1848 | for (var i = 0; i < dispatchedMethods.length; i++) { 1849 | var method = dispatchedMethods[i]; 1850 | this[method] = (function(m) { 1851 | return function() { 1852 | dispatch(m, arguments); 1853 | }; 1854 | }(method)); 1855 | } 1856 | 1857 | var reporters = []; 1858 | 1859 | this.addReporter = function(reporter) { 1860 | reporters.push(reporter); 1861 | }; 1862 | 1863 | return this; 1864 | 1865 | function dispatch(method, args) { 1866 | for (var i = 0; i < reporters.length; i++) { 1867 | var reporter = reporters[i]; 1868 | if (reporter[method]) { 1869 | reporter[method].apply(reporter, args); 1870 | } 1871 | } 1872 | } 1873 | } 1874 | 1875 | return ReportDispatcher; 1876 | }; 1877 | 1878 | 1879 | getJasmineRequireObj().SpyRegistry = function(j$) { 1880 | 1881 | function SpyRegistry(options) { 1882 | options = options || {}; 1883 | var currentSpies = options.currentSpies || function() { return []; }; 1884 | 1885 | this.spyOn = function(obj, methodName) { 1886 | if (j$.util.isUndefined(obj)) { 1887 | throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()'); 1888 | } 1889 | 1890 | if (j$.util.isUndefined(methodName)) { 1891 | throw new Error('No method name supplied'); 1892 | } 1893 | 1894 | if (j$.util.isUndefined(obj[methodName])) { 1895 | throw new Error(methodName + '() method does not exist'); 1896 | } 1897 | 1898 | if (obj[methodName] && j$.isSpy(obj[methodName])) { 1899 | //TODO?: should this return the current spy? Downside: may cause user confusion about spy state 1900 | throw new Error(methodName + ' has already been spied upon'); 1901 | } 1902 | 1903 | var spy = j$.createSpy(methodName, obj[methodName]); 1904 | 1905 | currentSpies().push({ 1906 | spy: spy, 1907 | baseObj: obj, 1908 | methodName: methodName, 1909 | originalValue: obj[methodName] 1910 | }); 1911 | 1912 | obj[methodName] = spy; 1913 | 1914 | return spy; 1915 | }; 1916 | 1917 | this.clearSpies = function() { 1918 | var spies = currentSpies(); 1919 | for (var i = 0; i < spies.length; i++) { 1920 | var spyEntry = spies[i]; 1921 | spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; 1922 | } 1923 | }; 1924 | } 1925 | 1926 | return SpyRegistry; 1927 | }; 1928 | 1929 | getJasmineRequireObj().SpyStrategy = function() { 1930 | 1931 | function SpyStrategy(options) { 1932 | options = options || {}; 1933 | 1934 | var identity = options.name || 'unknown', 1935 | originalFn = options.fn || function() {}, 1936 | getSpy = options.getSpy || function() {}, 1937 | plan = function() {}; 1938 | 1939 | this.identity = function() { 1940 | return identity; 1941 | }; 1942 | 1943 | this.exec = function() { 1944 | return plan.apply(this, arguments); 1945 | }; 1946 | 1947 | this.callThrough = function() { 1948 | plan = originalFn; 1949 | return getSpy(); 1950 | }; 1951 | 1952 | this.returnValue = function(value) { 1953 | plan = function() { 1954 | return value; 1955 | }; 1956 | return getSpy(); 1957 | }; 1958 | 1959 | this.returnValues = function() { 1960 | var values = Array.prototype.slice.call(arguments); 1961 | plan = function () { 1962 | return values.shift(); 1963 | }; 1964 | return getSpy(); 1965 | }; 1966 | 1967 | this.throwError = function(something) { 1968 | var error = (something instanceof Error) ? something : new Error(something); 1969 | plan = function() { 1970 | throw error; 1971 | }; 1972 | return getSpy(); 1973 | }; 1974 | 1975 | this.callFake = function(fn) { 1976 | plan = fn; 1977 | return getSpy(); 1978 | }; 1979 | 1980 | this.stub = function(fn) { 1981 | plan = function() {}; 1982 | return getSpy(); 1983 | }; 1984 | } 1985 | 1986 | return SpyStrategy; 1987 | }; 1988 | 1989 | getJasmineRequireObj().Suite = function(j$) { 1990 | function Suite(attrs) { 1991 | this.env = attrs.env; 1992 | this.id = attrs.id; 1993 | this.parentSuite = attrs.parentSuite; 1994 | this.description = attrs.description; 1995 | this.expectationFactory = attrs.expectationFactory; 1996 | this.expectationResultFactory = attrs.expectationResultFactory; 1997 | this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; 1998 | 1999 | this.beforeFns = []; 2000 | this.afterFns = []; 2001 | this.beforeAllFns = []; 2002 | this.afterAllFns = []; 2003 | this.disabled = false; 2004 | 2005 | this.children = []; 2006 | 2007 | this.result = { 2008 | id: this.id, 2009 | description: this.description, 2010 | fullName: this.getFullName(), 2011 | failedExpectations: [] 2012 | }; 2013 | } 2014 | 2015 | Suite.prototype.expect = function(actual) { 2016 | return this.expectationFactory(actual, this); 2017 | }; 2018 | 2019 | Suite.prototype.getFullName = function() { 2020 | var fullName = this.description; 2021 | for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { 2022 | if (parentSuite.parentSuite) { 2023 | fullName = parentSuite.description + ' ' + fullName; 2024 | } 2025 | } 2026 | return fullName; 2027 | }; 2028 | 2029 | Suite.prototype.disable = function() { 2030 | this.disabled = true; 2031 | }; 2032 | 2033 | Suite.prototype.beforeEach = function(fn) { 2034 | this.beforeFns.unshift(fn); 2035 | }; 2036 | 2037 | Suite.prototype.beforeAll = function(fn) { 2038 | this.beforeAllFns.push(fn); 2039 | }; 2040 | 2041 | Suite.prototype.afterEach = function(fn) { 2042 | this.afterFns.unshift(fn); 2043 | }; 2044 | 2045 | Suite.prototype.afterAll = function(fn) { 2046 | this.afterAllFns.push(fn); 2047 | }; 2048 | 2049 | Suite.prototype.addChild = function(child) { 2050 | this.children.push(child); 2051 | }; 2052 | 2053 | Suite.prototype.status = function() { 2054 | if (this.disabled) { 2055 | return 'disabled'; 2056 | } 2057 | 2058 | if (this.result.failedExpectations.length > 0) { 2059 | return 'failed'; 2060 | } else { 2061 | return 'finished'; 2062 | } 2063 | }; 2064 | 2065 | Suite.prototype.isExecutable = function() { 2066 | return !this.disabled; 2067 | }; 2068 | 2069 | Suite.prototype.canBeReentered = function() { 2070 | return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; 2071 | }; 2072 | 2073 | Suite.prototype.getResult = function() { 2074 | this.result.status = this.status(); 2075 | return this.result; 2076 | }; 2077 | 2078 | Suite.prototype.sharedUserContext = function() { 2079 | if (!this.sharedContext) { 2080 | this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {}; 2081 | } 2082 | 2083 | return this.sharedContext; 2084 | }; 2085 | 2086 | Suite.prototype.clonedSharedUserContext = function() { 2087 | return clone(this.sharedUserContext()); 2088 | }; 2089 | 2090 | Suite.prototype.onException = function() { 2091 | if (arguments[0] instanceof j$.errors.ExpectationFailed) { 2092 | return; 2093 | } 2094 | 2095 | if(isAfterAll(this.children)) { 2096 | var data = { 2097 | matcherName: '', 2098 | passed: false, 2099 | expected: '', 2100 | actual: '', 2101 | error: arguments[0] 2102 | }; 2103 | this.result.failedExpectations.push(this.expectationResultFactory(data)); 2104 | } else { 2105 | for (var i = 0; i < this.children.length; i++) { 2106 | var child = this.children[i]; 2107 | child.onException.apply(child, arguments); 2108 | } 2109 | } 2110 | }; 2111 | 2112 | Suite.prototype.addExpectationResult = function () { 2113 | if(isAfterAll(this.children) && isFailure(arguments)){ 2114 | var data = arguments[1]; 2115 | this.result.failedExpectations.push(this.expectationResultFactory(data)); 2116 | if(this.throwOnExpectationFailure) { 2117 | throw new j$.errors.ExpectationFailed(); 2118 | } 2119 | } else { 2120 | for (var i = 0; i < this.children.length; i++) { 2121 | var child = this.children[i]; 2122 | try { 2123 | child.addExpectationResult.apply(child, arguments); 2124 | } catch(e) { 2125 | // keep going 2126 | } 2127 | } 2128 | } 2129 | }; 2130 | 2131 | function isAfterAll(children) { 2132 | return children && children[0].result.status; 2133 | } 2134 | 2135 | function isFailure(args) { 2136 | return !args[0]; 2137 | } 2138 | 2139 | function clone(obj) { 2140 | var clonedObj = {}; 2141 | for (var prop in obj) { 2142 | if (obj.hasOwnProperty(prop)) { 2143 | clonedObj[prop] = obj[prop]; 2144 | } 2145 | } 2146 | 2147 | return clonedObj; 2148 | } 2149 | 2150 | return Suite; 2151 | }; 2152 | 2153 | if (typeof window == void 0 && typeof exports == 'object') { 2154 | exports.Suite = jasmineRequire.Suite; 2155 | } 2156 | 2157 | getJasmineRequireObj().Timer = function() { 2158 | var defaultNow = (function(Date) { 2159 | return function() { return new Date().getTime(); }; 2160 | })(Date); 2161 | 2162 | function Timer(options) { 2163 | options = options || {}; 2164 | 2165 | var now = options.now || defaultNow, 2166 | startTime; 2167 | 2168 | this.start = function() { 2169 | startTime = now(); 2170 | }; 2171 | 2172 | this.elapsed = function() { 2173 | return now() - startTime; 2174 | }; 2175 | } 2176 | 2177 | return Timer; 2178 | }; 2179 | 2180 | getJasmineRequireObj().TreeProcessor = function() { 2181 | function TreeProcessor(attrs) { 2182 | var tree = attrs.tree, 2183 | runnableIds = attrs.runnableIds, 2184 | queueRunnerFactory = attrs.queueRunnerFactory, 2185 | nodeStart = attrs.nodeStart || function() {}, 2186 | nodeComplete = attrs.nodeComplete || function() {}, 2187 | stats = { valid: true }, 2188 | processed = false, 2189 | defaultMin = Infinity, 2190 | defaultMax = 1 - Infinity; 2191 | 2192 | this.processTree = function() { 2193 | processNode(tree, false); 2194 | processed = true; 2195 | return stats; 2196 | }; 2197 | 2198 | this.execute = function(done) { 2199 | if (!processed) { 2200 | this.processTree(); 2201 | } 2202 | 2203 | if (!stats.valid) { 2204 | throw 'invalid order'; 2205 | } 2206 | 2207 | var childFns = wrapChildren(tree, 0); 2208 | 2209 | queueRunnerFactory({ 2210 | queueableFns: childFns, 2211 | userContext: tree.sharedUserContext(), 2212 | onException: function() { 2213 | tree.onException.apply(tree, arguments); 2214 | }, 2215 | onComplete: done 2216 | }); 2217 | }; 2218 | 2219 | function runnableIndex(id) { 2220 | for (var i = 0; i < runnableIds.length; i++) { 2221 | if (runnableIds[i] === id) { 2222 | return i; 2223 | } 2224 | } 2225 | } 2226 | 2227 | function processNode(node, parentEnabled) { 2228 | var executableIndex = runnableIndex(node.id); 2229 | 2230 | if (executableIndex !== undefined) { 2231 | parentEnabled = true; 2232 | } 2233 | 2234 | parentEnabled = parentEnabled && node.isExecutable(); 2235 | 2236 | if (!node.children) { 2237 | stats[node.id] = { 2238 | executable: parentEnabled && node.isExecutable(), 2239 | segments: [{ 2240 | index: 0, 2241 | owner: node, 2242 | nodes: [node], 2243 | min: startingMin(executableIndex), 2244 | max: startingMax(executableIndex) 2245 | }] 2246 | }; 2247 | } else { 2248 | var hasExecutableChild = false; 2249 | 2250 | for (var i = 0; i < node.children.length; i++) { 2251 | var child = node.children[i]; 2252 | 2253 | processNode(child, parentEnabled); 2254 | 2255 | if (!stats.valid) { 2256 | return; 2257 | } 2258 | 2259 | var childStats = stats[child.id]; 2260 | 2261 | hasExecutableChild = hasExecutableChild || childStats.executable; 2262 | } 2263 | 2264 | stats[node.id] = { 2265 | executable: hasExecutableChild 2266 | }; 2267 | 2268 | segmentChildren(node, stats[node.id], executableIndex); 2269 | 2270 | if (!node.canBeReentered() && stats[node.id].segments.length > 1) { 2271 | stats = { valid: false }; 2272 | } 2273 | } 2274 | } 2275 | 2276 | function startingMin(executableIndex) { 2277 | return executableIndex === undefined ? defaultMin : executableIndex; 2278 | } 2279 | 2280 | function startingMax(executableIndex) { 2281 | return executableIndex === undefined ? defaultMax : executableIndex; 2282 | } 2283 | 2284 | function segmentChildren(node, nodeStats, executableIndex) { 2285 | var currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) }, 2286 | result = [currentSegment], 2287 | lastMax = defaultMax, 2288 | orderedChildSegments = orderChildSegments(node.children); 2289 | 2290 | function isSegmentBoundary(minIndex) { 2291 | return lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1; 2292 | } 2293 | 2294 | for (var i = 0; i < orderedChildSegments.length; i++) { 2295 | var childSegment = orderedChildSegments[i], 2296 | maxIndex = childSegment.max, 2297 | minIndex = childSegment.min; 2298 | 2299 | if (isSegmentBoundary(minIndex)) { 2300 | currentSegment = {index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax}; 2301 | result.push(currentSegment); 2302 | } 2303 | 2304 | currentSegment.nodes.push(childSegment); 2305 | currentSegment.min = Math.min(currentSegment.min, minIndex); 2306 | currentSegment.max = Math.max(currentSegment.max, maxIndex); 2307 | lastMax = maxIndex; 2308 | } 2309 | 2310 | nodeStats.segments = result; 2311 | } 2312 | 2313 | function orderChildSegments(children) { 2314 | var specifiedOrder = [], 2315 | unspecifiedOrder = []; 2316 | 2317 | for (var i = 0; i < children.length; i++) { 2318 | var child = children[i], 2319 | segments = stats[child.id].segments; 2320 | 2321 | for (var j = 0; j < segments.length; j++) { 2322 | var seg = segments[j]; 2323 | 2324 | if (seg.min === defaultMin) { 2325 | unspecifiedOrder.push(seg); 2326 | } else { 2327 | specifiedOrder.push(seg); 2328 | } 2329 | } 2330 | } 2331 | 2332 | specifiedOrder.sort(function(a, b) { 2333 | return a.min - b.min; 2334 | }); 2335 | 2336 | return specifiedOrder.concat(unspecifiedOrder); 2337 | } 2338 | 2339 | function executeNode(node, segmentNumber) { 2340 | if (node.children) { 2341 | return { 2342 | fn: function(done) { 2343 | nodeStart(node); 2344 | 2345 | queueRunnerFactory({ 2346 | onComplete: function() { 2347 | nodeComplete(node, node.getResult()); 2348 | done(); 2349 | }, 2350 | queueableFns: wrapChildren(node, segmentNumber), 2351 | userContext: node.sharedUserContext(), 2352 | onException: function() { 2353 | node.onException.apply(node, arguments); 2354 | } 2355 | }); 2356 | } 2357 | }; 2358 | } else { 2359 | return { 2360 | fn: function(done) { node.execute(done, stats[node.id].executable); } 2361 | }; 2362 | } 2363 | } 2364 | 2365 | function wrapChildren(node, segmentNumber) { 2366 | var result = [], 2367 | segmentChildren = stats[node.id].segments[segmentNumber].nodes; 2368 | 2369 | for (var i = 0; i < segmentChildren.length; i++) { 2370 | result.push(executeNode(segmentChildren[i].owner, segmentChildren[i].index)); 2371 | } 2372 | 2373 | if (!stats[node.id].executable) { 2374 | return result; 2375 | } 2376 | 2377 | return node.beforeAllFns.concat(result).concat(node.afterAllFns); 2378 | } 2379 | } 2380 | 2381 | return TreeProcessor; 2382 | }; 2383 | 2384 | getJasmineRequireObj().Any = function(j$) { 2385 | 2386 | function Any(expectedObject) { 2387 | this.expectedObject = expectedObject; 2388 | } 2389 | 2390 | Any.prototype.asymmetricMatch = function(other) { 2391 | if (this.expectedObject == String) { 2392 | return typeof other == 'string' || other instanceof String; 2393 | } 2394 | 2395 | if (this.expectedObject == Number) { 2396 | return typeof other == 'number' || other instanceof Number; 2397 | } 2398 | 2399 | if (this.expectedObject == Function) { 2400 | return typeof other == 'function' || other instanceof Function; 2401 | } 2402 | 2403 | if (this.expectedObject == Object) { 2404 | return typeof other == 'object'; 2405 | } 2406 | 2407 | if (this.expectedObject == Boolean) { 2408 | return typeof other == 'boolean'; 2409 | } 2410 | 2411 | return other instanceof this.expectedObject; 2412 | }; 2413 | 2414 | Any.prototype.jasmineToString = function() { 2415 | return ''; 2416 | }; 2417 | 2418 | return Any; 2419 | }; 2420 | 2421 | getJasmineRequireObj().Anything = function(j$) { 2422 | 2423 | function Anything() {} 2424 | 2425 | Anything.prototype.asymmetricMatch = function(other) { 2426 | return !j$.util.isUndefined(other) && other !== null; 2427 | }; 2428 | 2429 | Anything.prototype.jasmineToString = function() { 2430 | return ''; 2431 | }; 2432 | 2433 | return Anything; 2434 | }; 2435 | 2436 | getJasmineRequireObj().ArrayContaining = function(j$) { 2437 | function ArrayContaining(sample) { 2438 | this.sample = sample; 2439 | } 2440 | 2441 | ArrayContaining.prototype.asymmetricMatch = function(other) { 2442 | var className = Object.prototype.toString.call(this.sample); 2443 | if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); } 2444 | 2445 | for (var i = 0; i < this.sample.length; i++) { 2446 | var item = this.sample[i]; 2447 | if (!j$.matchersUtil.contains(other, item)) { 2448 | return false; 2449 | } 2450 | } 2451 | 2452 | return true; 2453 | }; 2454 | 2455 | ArrayContaining.prototype.jasmineToString = function () { 2456 | return ''; 2457 | }; 2458 | 2459 | return ArrayContaining; 2460 | }; 2461 | 2462 | getJasmineRequireObj().ObjectContaining = function(j$) { 2463 | 2464 | function ObjectContaining(sample) { 2465 | this.sample = sample; 2466 | } 2467 | 2468 | function getPrototype(obj) { 2469 | if (Object.getPrototypeOf) { 2470 | return Object.getPrototypeOf(obj); 2471 | } 2472 | 2473 | if (obj.constructor.prototype == obj) { 2474 | return null; 2475 | } 2476 | 2477 | return obj.constructor.prototype; 2478 | } 2479 | 2480 | function hasProperty(obj, property) { 2481 | if (!obj) { 2482 | return false; 2483 | } 2484 | 2485 | if (Object.prototype.hasOwnProperty.call(obj, property)) { 2486 | return true; 2487 | } 2488 | 2489 | return hasProperty(getPrototype(obj), property); 2490 | } 2491 | 2492 | ObjectContaining.prototype.asymmetricMatch = function(other) { 2493 | if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } 2494 | 2495 | for (var property in this.sample) { 2496 | if (!hasProperty(other, property) || 2497 | !j$.matchersUtil.equals(this.sample[property], other[property])) { 2498 | return false; 2499 | } 2500 | } 2501 | 2502 | return true; 2503 | }; 2504 | 2505 | ObjectContaining.prototype.jasmineToString = function() { 2506 | return ''; 2507 | }; 2508 | 2509 | return ObjectContaining; 2510 | }; 2511 | 2512 | getJasmineRequireObj().StringMatching = function(j$) { 2513 | 2514 | function StringMatching(expected) { 2515 | if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { 2516 | throw new Error('Expected is not a String or a RegExp'); 2517 | } 2518 | 2519 | this.regexp = new RegExp(expected); 2520 | } 2521 | 2522 | StringMatching.prototype.asymmetricMatch = function(other) { 2523 | return this.regexp.test(other); 2524 | }; 2525 | 2526 | StringMatching.prototype.jasmineToString = function() { 2527 | return ''; 2528 | }; 2529 | 2530 | return StringMatching; 2531 | }; 2532 | 2533 | getJasmineRequireObj().errors = function() { 2534 | function ExpectationFailed() {} 2535 | 2536 | ExpectationFailed.prototype = new Error(); 2537 | ExpectationFailed.prototype.constructor = ExpectationFailed; 2538 | 2539 | return { 2540 | ExpectationFailed: ExpectationFailed 2541 | }; 2542 | }; 2543 | getJasmineRequireObj().matchersUtil = function(j$) { 2544 | // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? 2545 | 2546 | return { 2547 | equals: function(a, b, customTesters) { 2548 | customTesters = customTesters || []; 2549 | 2550 | return eq(a, b, [], [], customTesters); 2551 | }, 2552 | 2553 | contains: function(haystack, needle, customTesters) { 2554 | customTesters = customTesters || []; 2555 | 2556 | if ((Object.prototype.toString.apply(haystack) === '[object Array]') || 2557 | (!!haystack && !haystack.indexOf)) 2558 | { 2559 | for (var i = 0; i < haystack.length; i++) { 2560 | if (eq(haystack[i], needle, [], [], customTesters)) { 2561 | return true; 2562 | } 2563 | } 2564 | return false; 2565 | } 2566 | 2567 | return !!haystack && haystack.indexOf(needle) >= 0; 2568 | }, 2569 | 2570 | buildFailureMessage: function() { 2571 | var args = Array.prototype.slice.call(arguments, 0), 2572 | matcherName = args[0], 2573 | isNot = args[1], 2574 | actual = args[2], 2575 | expected = args.slice(3), 2576 | englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); 2577 | 2578 | var message = 'Expected ' + 2579 | j$.pp(actual) + 2580 | (isNot ? ' not ' : ' ') + 2581 | englishyPredicate; 2582 | 2583 | if (expected.length > 0) { 2584 | for (var i = 0; i < expected.length; i++) { 2585 | if (i > 0) { 2586 | message += ','; 2587 | } 2588 | message += ' ' + j$.pp(expected[i]); 2589 | } 2590 | } 2591 | 2592 | return message + '.'; 2593 | } 2594 | }; 2595 | 2596 | function isAsymmetric(obj) { 2597 | return obj && j$.isA_('Function', obj.asymmetricMatch); 2598 | } 2599 | 2600 | function asymmetricMatch(a, b) { 2601 | var asymmetricA = isAsymmetric(a), 2602 | asymmetricB = isAsymmetric(b); 2603 | 2604 | if (asymmetricA && asymmetricB) { 2605 | return undefined; 2606 | } 2607 | 2608 | if (asymmetricA) { 2609 | return a.asymmetricMatch(b); 2610 | } 2611 | 2612 | if (asymmetricB) { 2613 | return b.asymmetricMatch(a); 2614 | } 2615 | } 2616 | 2617 | // Equality function lovingly adapted from isEqual in 2618 | // [Underscore](http://underscorejs.org) 2619 | function eq(a, b, aStack, bStack, customTesters) { 2620 | var result = true; 2621 | 2622 | var asymmetricResult = asymmetricMatch(a, b); 2623 | if (!j$.util.isUndefined(asymmetricResult)) { 2624 | return asymmetricResult; 2625 | } 2626 | 2627 | for (var i = 0; i < customTesters.length; i++) { 2628 | var customTesterResult = customTesters[i](a, b); 2629 | if (!j$.util.isUndefined(customTesterResult)) { 2630 | return customTesterResult; 2631 | } 2632 | } 2633 | 2634 | if (a instanceof Error && b instanceof Error) { 2635 | return a.message == b.message; 2636 | } 2637 | 2638 | // Identical objects are equal. `0 === -0`, but they aren't identical. 2639 | // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). 2640 | if (a === b) { return a !== 0 || 1 / a == 1 / b; } 2641 | // A strict comparison is necessary because `null == undefined`. 2642 | if (a === null || b === null) { return a === b; } 2643 | var className = Object.prototype.toString.call(a); 2644 | if (className != Object.prototype.toString.call(b)) { return false; } 2645 | switch (className) { 2646 | // Strings, numbers, dates, and booleans are compared by value. 2647 | case '[object String]': 2648 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 2649 | // equivalent to `new String("5")`. 2650 | return a == String(b); 2651 | case '[object Number]': 2652 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for 2653 | // other numeric values. 2654 | return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); 2655 | case '[object Date]': 2656 | case '[object Boolean]': 2657 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their 2658 | // millisecond representations. Note that invalid dates with millisecond representations 2659 | // of `NaN` are not equivalent. 2660 | return +a == +b; 2661 | // RegExps are compared by their source patterns and flags. 2662 | case '[object RegExp]': 2663 | return a.source == b.source && 2664 | a.global == b.global && 2665 | a.multiline == b.multiline && 2666 | a.ignoreCase == b.ignoreCase; 2667 | } 2668 | if (typeof a != 'object' || typeof b != 'object') { return false; } 2669 | 2670 | var aIsDomNode = j$.isDomNode(a); 2671 | var bIsDomNode = j$.isDomNode(b); 2672 | if (aIsDomNode && bIsDomNode) { 2673 | // At first try to use DOM3 method isEqualNode 2674 | if (a.isEqualNode) { 2675 | return a.isEqualNode(b); 2676 | } 2677 | // IE8 doesn't support isEqualNode, try to use outerHTML && innerText 2678 | var aIsElement = a instanceof Element; 2679 | var bIsElement = b instanceof Element; 2680 | if (aIsElement && bIsElement) { 2681 | return a.outerHTML == b.outerHTML; 2682 | } 2683 | if (aIsElement || bIsElement) { 2684 | return false; 2685 | } 2686 | return a.innerText == b.innerText && a.textContent == b.textContent; 2687 | } 2688 | if (aIsDomNode || bIsDomNode) { 2689 | return false; 2690 | } 2691 | 2692 | // Assume equality for cyclic structures. The algorithm for detecting cyclic 2693 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 2694 | var length = aStack.length; 2695 | while (length--) { 2696 | // Linear search. Performance is inversely proportional to the number of 2697 | // unique nested structures. 2698 | if (aStack[length] == a) { return bStack[length] == b; } 2699 | } 2700 | // Add the first object to the stack of traversed objects. 2701 | aStack.push(a); 2702 | bStack.push(b); 2703 | var size = 0; 2704 | // Recursively compare objects and arrays. 2705 | // Compare array lengths to determine if a deep comparison is necessary. 2706 | if (className == '[object Array]' && a.length !== b.length) { 2707 | result = false; 2708 | } 2709 | 2710 | if (result) { 2711 | // Objects with different constructors are not equivalent, but `Object`s 2712 | // or `Array`s from different frames are. 2713 | if (className !== '[object Array]') { 2714 | var aCtor = a.constructor, bCtor = b.constructor; 2715 | if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor && 2716 | isFunction(bCtor) && bCtor instanceof bCtor)) { 2717 | return false; 2718 | } 2719 | } 2720 | // Deep compare objects. 2721 | for (var key in a) { 2722 | if (has(a, key)) { 2723 | // Count the expected number of properties. 2724 | size++; 2725 | // Deep compare each member. 2726 | if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; } 2727 | } 2728 | } 2729 | // Ensure that both objects contain the same number of properties. 2730 | if (result) { 2731 | for (key in b) { 2732 | if (has(b, key) && !(size--)) { break; } 2733 | } 2734 | result = !size; 2735 | } 2736 | } 2737 | // Remove the first object from the stack of traversed objects. 2738 | aStack.pop(); 2739 | bStack.pop(); 2740 | 2741 | return result; 2742 | 2743 | function has(obj, key) { 2744 | return Object.prototype.hasOwnProperty.call(obj, key); 2745 | } 2746 | 2747 | function isFunction(obj) { 2748 | return typeof obj === 'function'; 2749 | } 2750 | } 2751 | }; 2752 | 2753 | getJasmineRequireObj().toBe = function() { 2754 | function toBe() { 2755 | return { 2756 | compare: function(actual, expected) { 2757 | return { 2758 | pass: actual === expected 2759 | }; 2760 | } 2761 | }; 2762 | } 2763 | 2764 | return toBe; 2765 | }; 2766 | 2767 | getJasmineRequireObj().toBeCloseTo = function() { 2768 | 2769 | function toBeCloseTo() { 2770 | return { 2771 | compare: function(actual, expected, precision) { 2772 | if (precision !== 0) { 2773 | precision = precision || 2; 2774 | } 2775 | 2776 | return { 2777 | pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2) 2778 | }; 2779 | } 2780 | }; 2781 | } 2782 | 2783 | return toBeCloseTo; 2784 | }; 2785 | 2786 | getJasmineRequireObj().toBeDefined = function() { 2787 | function toBeDefined() { 2788 | return { 2789 | compare: function(actual) { 2790 | return { 2791 | pass: (void 0 !== actual) 2792 | }; 2793 | } 2794 | }; 2795 | } 2796 | 2797 | return toBeDefined; 2798 | }; 2799 | 2800 | getJasmineRequireObj().toBeFalsy = function() { 2801 | function toBeFalsy() { 2802 | return { 2803 | compare: function(actual) { 2804 | return { 2805 | pass: !!!actual 2806 | }; 2807 | } 2808 | }; 2809 | } 2810 | 2811 | return toBeFalsy; 2812 | }; 2813 | 2814 | getJasmineRequireObj().toBeGreaterThan = function() { 2815 | 2816 | function toBeGreaterThan() { 2817 | return { 2818 | compare: function(actual, expected) { 2819 | return { 2820 | pass: actual > expected 2821 | }; 2822 | } 2823 | }; 2824 | } 2825 | 2826 | return toBeGreaterThan; 2827 | }; 2828 | 2829 | 2830 | getJasmineRequireObj().toBeLessThan = function() { 2831 | function toBeLessThan() { 2832 | return { 2833 | 2834 | compare: function(actual, expected) { 2835 | return { 2836 | pass: actual < expected 2837 | }; 2838 | } 2839 | }; 2840 | } 2841 | 2842 | return toBeLessThan; 2843 | }; 2844 | getJasmineRequireObj().toBeNaN = function(j$) { 2845 | 2846 | function toBeNaN() { 2847 | return { 2848 | compare: function(actual) { 2849 | var result = { 2850 | pass: (actual !== actual) 2851 | }; 2852 | 2853 | if (result.pass) { 2854 | result.message = 'Expected actual not to be NaN.'; 2855 | } else { 2856 | result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; }; 2857 | } 2858 | 2859 | return result; 2860 | } 2861 | }; 2862 | } 2863 | 2864 | return toBeNaN; 2865 | }; 2866 | 2867 | getJasmineRequireObj().toBeNull = function() { 2868 | 2869 | function toBeNull() { 2870 | return { 2871 | compare: function(actual) { 2872 | return { 2873 | pass: actual === null 2874 | }; 2875 | } 2876 | }; 2877 | } 2878 | 2879 | return toBeNull; 2880 | }; 2881 | 2882 | getJasmineRequireObj().toBeTruthy = function() { 2883 | 2884 | function toBeTruthy() { 2885 | return { 2886 | compare: function(actual) { 2887 | return { 2888 | pass: !!actual 2889 | }; 2890 | } 2891 | }; 2892 | } 2893 | 2894 | return toBeTruthy; 2895 | }; 2896 | 2897 | getJasmineRequireObj().toBeUndefined = function() { 2898 | 2899 | function toBeUndefined() { 2900 | return { 2901 | compare: function(actual) { 2902 | return { 2903 | pass: void 0 === actual 2904 | }; 2905 | } 2906 | }; 2907 | } 2908 | 2909 | return toBeUndefined; 2910 | }; 2911 | 2912 | getJasmineRequireObj().toContain = function() { 2913 | function toContain(util, customEqualityTesters) { 2914 | customEqualityTesters = customEqualityTesters || []; 2915 | 2916 | return { 2917 | compare: function(actual, expected) { 2918 | 2919 | return { 2920 | pass: util.contains(actual, expected, customEqualityTesters) 2921 | }; 2922 | } 2923 | }; 2924 | } 2925 | 2926 | return toContain; 2927 | }; 2928 | 2929 | getJasmineRequireObj().toEqual = function() { 2930 | 2931 | function toEqual(util, customEqualityTesters) { 2932 | customEqualityTesters = customEqualityTesters || []; 2933 | 2934 | return { 2935 | compare: function(actual, expected) { 2936 | var result = { 2937 | pass: false 2938 | }; 2939 | 2940 | result.pass = util.equals(actual, expected, customEqualityTesters); 2941 | 2942 | return result; 2943 | } 2944 | }; 2945 | } 2946 | 2947 | return toEqual; 2948 | }; 2949 | 2950 | getJasmineRequireObj().toHaveBeenCalled = function(j$) { 2951 | 2952 | function toHaveBeenCalled() { 2953 | return { 2954 | compare: function(actual) { 2955 | var result = {}; 2956 | 2957 | if (!j$.isSpy(actual)) { 2958 | throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); 2959 | } 2960 | 2961 | if (arguments.length > 1) { 2962 | throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); 2963 | } 2964 | 2965 | result.pass = actual.calls.any(); 2966 | 2967 | result.message = result.pass ? 2968 | 'Expected spy ' + actual.and.identity() + ' not to have been called.' : 2969 | 'Expected spy ' + actual.and.identity() + ' to have been called.'; 2970 | 2971 | return result; 2972 | } 2973 | }; 2974 | } 2975 | 2976 | return toHaveBeenCalled; 2977 | }; 2978 | 2979 | getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { 2980 | 2981 | function toHaveBeenCalledWith(util, customEqualityTesters) { 2982 | return { 2983 | compare: function() { 2984 | var args = Array.prototype.slice.call(arguments, 0), 2985 | actual = args[0], 2986 | expectedArgs = args.slice(1), 2987 | result = { pass: false }; 2988 | 2989 | if (!j$.isSpy(actual)) { 2990 | throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); 2991 | } 2992 | 2993 | if (!actual.calls.any()) { 2994 | result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but it was never called.'; }; 2995 | return result; 2996 | } 2997 | 2998 | if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) { 2999 | result.pass = true; 3000 | result.message = function() { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + j$.pp(expectedArgs) + ' but it was.'; }; 3001 | } else { 3002 | result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but actual calls were ' + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + '.'; }; 3003 | } 3004 | 3005 | return result; 3006 | } 3007 | }; 3008 | } 3009 | 3010 | return toHaveBeenCalledWith; 3011 | }; 3012 | 3013 | getJasmineRequireObj().toMatch = function(j$) { 3014 | 3015 | function toMatch() { 3016 | return { 3017 | compare: function(actual, expected) { 3018 | if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { 3019 | throw new Error('Expected is not a String or a RegExp'); 3020 | } 3021 | 3022 | var regexp = new RegExp(expected); 3023 | 3024 | return { 3025 | pass: regexp.test(actual) 3026 | }; 3027 | } 3028 | }; 3029 | } 3030 | 3031 | return toMatch; 3032 | }; 3033 | 3034 | getJasmineRequireObj().toThrow = function(j$) { 3035 | 3036 | function toThrow(util) { 3037 | return { 3038 | compare: function(actual, expected) { 3039 | var result = { pass: false }, 3040 | threw = false, 3041 | thrown; 3042 | 3043 | if (typeof actual != 'function') { 3044 | throw new Error('Actual is not a Function'); 3045 | } 3046 | 3047 | try { 3048 | actual(); 3049 | } catch (e) { 3050 | threw = true; 3051 | thrown = e; 3052 | } 3053 | 3054 | if (!threw) { 3055 | result.message = 'Expected function to throw an exception.'; 3056 | return result; 3057 | } 3058 | 3059 | if (arguments.length == 1) { 3060 | result.pass = true; 3061 | result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; }; 3062 | 3063 | return result; 3064 | } 3065 | 3066 | if (util.equals(thrown, expected)) { 3067 | result.pass = true; 3068 | result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; }; 3069 | } else { 3070 | result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; }; 3071 | } 3072 | 3073 | return result; 3074 | } 3075 | }; 3076 | } 3077 | 3078 | return toThrow; 3079 | }; 3080 | 3081 | getJasmineRequireObj().toThrowError = function(j$) { 3082 | function toThrowError (util) { 3083 | return { 3084 | compare: function(actual) { 3085 | var threw = false, 3086 | pass = {pass: true}, 3087 | fail = {pass: false}, 3088 | thrown; 3089 | 3090 | if (typeof actual != 'function') { 3091 | throw new Error('Actual is not a Function'); 3092 | } 3093 | 3094 | var errorMatcher = getMatcher.apply(null, arguments); 3095 | 3096 | try { 3097 | actual(); 3098 | } catch (e) { 3099 | threw = true; 3100 | thrown = e; 3101 | } 3102 | 3103 | if (!threw) { 3104 | fail.message = 'Expected function to throw an Error.'; 3105 | return fail; 3106 | } 3107 | 3108 | if (!(thrown instanceof Error)) { 3109 | fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; }; 3110 | return fail; 3111 | } 3112 | 3113 | if (errorMatcher.hasNoSpecifics()) { 3114 | pass.message = 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(thrown) + '.'; 3115 | return pass; 3116 | } 3117 | 3118 | if (errorMatcher.matches(thrown)) { 3119 | pass.message = function() { 3120 | return 'Expected function not to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + '.'; 3121 | }; 3122 | return pass; 3123 | } else { 3124 | fail.message = function() { 3125 | return 'Expected function to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + 3126 | ', but it threw ' + errorMatcher.thrownDescription(thrown) + '.'; 3127 | }; 3128 | return fail; 3129 | } 3130 | } 3131 | }; 3132 | 3133 | function getMatcher() { 3134 | var expected = null, 3135 | errorType = null; 3136 | 3137 | if (arguments.length == 2) { 3138 | expected = arguments[1]; 3139 | if (isAnErrorType(expected)) { 3140 | errorType = expected; 3141 | expected = null; 3142 | } 3143 | } else if (arguments.length > 2) { 3144 | errorType = arguments[1]; 3145 | expected = arguments[2]; 3146 | if (!isAnErrorType(errorType)) { 3147 | throw new Error('Expected error type is not an Error.'); 3148 | } 3149 | } 3150 | 3151 | if (expected && !isStringOrRegExp(expected)) { 3152 | if (errorType) { 3153 | throw new Error('Expected error message is not a string or RegExp.'); 3154 | } else { 3155 | throw new Error('Expected is not an Error, string, or RegExp.'); 3156 | } 3157 | } 3158 | 3159 | function messageMatch(message) { 3160 | if (typeof expected == 'string') { 3161 | return expected == message; 3162 | } else { 3163 | return expected.test(message); 3164 | } 3165 | } 3166 | 3167 | return { 3168 | errorTypeDescription: errorType ? j$.fnNameFor(errorType) : 'an exception', 3169 | thrownDescription: function(thrown) { 3170 | var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception', 3171 | thrownMessage = ''; 3172 | 3173 | if (expected) { 3174 | thrownMessage = ' with message ' + j$.pp(thrown.message); 3175 | } 3176 | 3177 | return thrownName + thrownMessage; 3178 | }, 3179 | messageDescription: function() { 3180 | if (expected === null) { 3181 | return ''; 3182 | } else if (expected instanceof RegExp) { 3183 | return ' with a message matching ' + j$.pp(expected); 3184 | } else { 3185 | return ' with message ' + j$.pp(expected); 3186 | } 3187 | }, 3188 | hasNoSpecifics: function() { 3189 | return expected === null && errorType === null; 3190 | }, 3191 | matches: function(error) { 3192 | return (errorType === null || error instanceof errorType) && 3193 | (expected === null || messageMatch(error.message)); 3194 | } 3195 | }; 3196 | } 3197 | 3198 | function isStringOrRegExp(potential) { 3199 | return potential instanceof RegExp || (typeof potential == 'string'); 3200 | } 3201 | 3202 | function isAnErrorType(type) { 3203 | if (typeof type !== 'function') { 3204 | return false; 3205 | } 3206 | 3207 | var Surrogate = function() {}; 3208 | Surrogate.prototype = type.prototype; 3209 | return (new Surrogate()) instanceof Error; 3210 | } 3211 | } 3212 | 3213 | return toThrowError; 3214 | }; 3215 | 3216 | getJasmineRequireObj().interface = function(jasmine, env) { 3217 | var jasmineInterface = { 3218 | describe: function(description, specDefinitions) { 3219 | return env.describe(description, specDefinitions); 3220 | }, 3221 | 3222 | xdescribe: function(description, specDefinitions) { 3223 | return env.xdescribe(description, specDefinitions); 3224 | }, 3225 | 3226 | fdescribe: function(description, specDefinitions) { 3227 | return env.fdescribe(description, specDefinitions); 3228 | }, 3229 | 3230 | it: function() { 3231 | return env.it.apply(env, arguments); 3232 | }, 3233 | 3234 | xit: function() { 3235 | return env.xit.apply(env, arguments); 3236 | }, 3237 | 3238 | fit: function() { 3239 | return env.fit.apply(env, arguments); 3240 | }, 3241 | 3242 | beforeEach: function() { 3243 | return env.beforeEach.apply(env, arguments); 3244 | }, 3245 | 3246 | afterEach: function() { 3247 | return env.afterEach.apply(env, arguments); 3248 | }, 3249 | 3250 | beforeAll: function() { 3251 | return env.beforeAll.apply(env, arguments); 3252 | }, 3253 | 3254 | afterAll: function() { 3255 | return env.afterAll.apply(env, arguments); 3256 | }, 3257 | 3258 | expect: function(actual) { 3259 | return env.expect(actual); 3260 | }, 3261 | 3262 | pending: function() { 3263 | return env.pending.apply(env, arguments); 3264 | }, 3265 | 3266 | fail: function() { 3267 | return env.fail.apply(env, arguments); 3268 | }, 3269 | 3270 | spyOn: function(obj, methodName) { 3271 | return env.spyOn(obj, methodName); 3272 | }, 3273 | 3274 | jsApiReporter: new jasmine.JsApiReporter({ 3275 | timer: new jasmine.Timer() 3276 | }), 3277 | 3278 | jasmine: jasmine 3279 | }; 3280 | 3281 | jasmine.addCustomEqualityTester = function(tester) { 3282 | env.addCustomEqualityTester(tester); 3283 | }; 3284 | 3285 | jasmine.addMatchers = function(matchers) { 3286 | return env.addMatchers(matchers); 3287 | }; 3288 | 3289 | jasmine.clock = function() { 3290 | return env.clock; 3291 | }; 3292 | 3293 | return jasmineInterface; 3294 | }; 3295 | 3296 | getJasmineRequireObj().version = function() { 3297 | return '2.3.4'; 3298 | }; 3299 | --------------------------------------------------------------------------------