├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bin └── jasmine-node ├── bower.json ├── lib └── jasmine-node │ ├── async-callback.js │ ├── autotest.js │ ├── cli.js │ ├── cs.js │ ├── index.js │ ├── jasmine-1.3.1.js │ ├── reporter.js │ ├── requirejs-runner.js │ ├── requirejs-spec-loader.js │ ├── requirejs-wrapper-template.js │ └── spec-collection.js ├── package.json ├── scripts └── specs ├── spec-requirejs-coffee ├── RequireCsSpec.coffee ├── RequireJsSpec.coffee ├── requirecs.sut.coffee └── requirejs-setup.js ├── spec-requirejs ├── requirejs-setup.js ├── requirejs-wrapper-template.js ├── requirejs.spec.js └── requirejs.sut.js ├── spec ├── AsyncSpec.coffee ├── CoffeeSpec.coffee ├── GrammarHelper.coffee ├── HelperSpec.coffee ├── SampleSpecs.js ├── TestSpec.js ├── TimerSpec.js ├── async-callback_spec.js ├── helper_spec.js ├── litcoffee │ └── Litcoffee.spec.litcoffee ├── nested.js │ └── NestedSpec.js ├── nested │ ├── NestedSpec.js │ └── uber-nested │ │ └── UberNestedSpec.js ├── reporter_spec.js └── sample_helper.js └── specs.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *.iml 4 | *.ipr 5 | *.iws 6 | *.tmproj 7 | .project 8 | .settings 9 | .externalToolBuilders 10 | *.swp 11 | node_modules 12 | *~ 13 | /.c9revisions/ 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018-present Christopher J. Brody and other contributors 4 | Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jasmine-node 2 | ====== 3 | 4 | [![LICENSE](https://img.shields.io/github/license/mhevery/jasmine-node.svg?style=flat-square)](./LICENSE) 5 | [![npm](https://img.shields.io/npm/v/jasmine-node.svg?style=flat-square)](https://www.npmjs.com/package/jasmine-node) 6 | [![contributors](https://img.shields.io/badge/contributors-many-purple.svg?style=flat-square)](https://github.com/mhevery/jasmine-node/graphs/contributors) 7 | 8 | This node.js module makes the wonderful [Pivotal Lab's jasmine](http://github.com/pivotal/jasmine) 9 | spec framework (version 1) available in node.js. 10 | 11 | 12 | Project status 13 | -------------- 14 | 15 | This project is now in maintenance mode. It is recommended to use the `jasmine` or `jasmine-npm` 16 | package whenever possible. 17 | 18 | 19 | jasmine 20 | ------- 21 | 22 | Version `1.3.1` of Jasmine is currently included with node-jasmine. This is a forked version from the 23 | [Karma project](https://github.com/karma-runner/karma-jasmine), which allows you to use the 24 | `ddescribe` and `iit` functions to run individual suites or specs. 25 | 26 | NOTICE: BETA `2.0.0` Support in the `Jasmine2.0` branch (with rewrite in CoffeeScript) is now abandoned and no longer supported. 27 | 28 | Supported Node.js versions 29 | -------------------------- 30 | 31 | * Current: 32 | - 10 33 | - 12 34 | * Deprecated: 35 | - 8 (EOL in December 2019) 36 | - 6 (already past EOL) 37 | - 4 (already past EOL) 38 | 39 | Older versions of Node.js are no longer supported. It is *highly* recommended to upgrade to a supported version of Node.js. 40 | 41 | what's new 42 | ---------- 43 | * Growl notifications with the `--growl` flag (requires Growl to be installed) 44 | * Ability to test specs written in Literate CoffeeScript 45 | * Teamcity Reporter reinstated. 46 | * Ability to specify multiple files to test via list in command line 47 | * Ability to suppress stack trace with `--noStack` 48 | * Async tests now run in the expected context instead of the global one 49 | * `--config` flag that allows you to assign variables to process.env 50 | * Terminal Reporters are now available in the Jasmine Object #184 51 | * Done is now available in all timeout specs #199 52 | * `afterEach` is available in requirejs #179 53 | * Editors that replace instead of changing files should work with autotest #198 54 | * Jasmine Mock Clock now works! 55 | * Autotest now works! 56 | * Using the latest Jasmine! 57 | * Verbose mode tabs `describe` blocks much more accurately! 58 | * `--coffee` now allows specs written in Literate CoffeeScript (`.litcoffee`) 59 | 60 | install 61 | ------ 62 | 63 | To install the latest official version, use NPM: 64 | 65 | ```sh 66 | npm install jasmine-node -g 67 | ``` 68 | 69 | To install the latest _bleeding edge_ version, clone this repository and check 70 | out the `beta` branch. 71 | 72 | usage 73 | ------ 74 | 75 | Write the specifications for your code in `*.js` and `*.coffee` files in the `spec/` directory. 76 | You can use sub-directories to better organise your specs. In the specs use `describe()`, `it()` etc. exactly 77 | as you would in client-side jasmine specs. 78 | 79 | **Note**: your specification files must be named as `*spec.js`, `*spec.coffee` or `*spec.litcoffee`, 80 | which matches the regular expression `/spec\.(js|coffee|litcoffee)$/i`; 81 | otherwise jasmine-node won't find them! 82 | For example, `sampleSpecs.js` is wrong, `sampleSpec.js` is right. 83 | 84 | If you have installed the npm package, you can run it with: 85 | 86 | ```sh 87 | jasmine-node spec/ 88 | ``` 89 | 90 | If you aren't using npm, you should add `pwd`/lib to the `$NODE_PATH` 91 | environment variable, then run: 92 | 93 | ```sh 94 | node lib/jasmine-node/cli.js 95 | ``` 96 | 97 | 98 | You can supply the following arguments: 99 | 100 | * `--autotest`, provides automatic execution of specs after each change 101 | * `--watch`, when used with `--autotest`, paths after `--watch` will be 102 | watched for changes, allowing to watch for changes outside of specs directory 103 | * `--coffee`, allow execution of `.coffee` and `.litcoffee` specs 104 | * `--color`, indicates spec output should uses color to 105 | indicates passing (green) or failing (red) specs 106 | * `--noColor`, do not use color in the output 107 | * `-m, --match REGEXP`, match only specs containing "REGEXPspec" 108 | * `--matchall`, relax requirement of "spec" in spec file names 109 | * `--verbose`, verbose output as the specs are run 110 | * `--junitreport`, export tests results as junitreport xml format 111 | * `--output FOLDER`, defines the output folder for junitreport files 112 | * `--teamcity`, converts all console output to teamcity custom test runner commands. (Normally auto detected.) 113 | * `--growl`, display test run summary in a growl notification (in addition to other outputs) 114 | * `--runWithRequireJs`, loads all specs using requirejs instead of node's native require method 115 | * `--requireJsSetup`, file run before specs to include and configure RequireJS 116 | * `--test-dir`, the absolute root directory path where tests are located 117 | * `--nohelpers`, does not load helpers 118 | * `--forceexit`, force exit once tests complete 119 | * `--captureExceptions`, listen to global exceptions, report them and exit (interferes with Domains in NodeJs, so do not use if using Domains as well 120 | * `--config NAME VALUE`, set a global variable in `process.env` 121 | * `--noStack`, suppress the stack trace generated from a test failure 122 | 123 | Individual files to test can be added as bare arguments to the end of the args. 124 | 125 | Example: 126 | 127 | ```bash 128 | jasmine-node --coffee spec/AsyncSpec.coffee spec/CoffeeSpec.coffee spec/SampleSpec.js 129 | ``` 130 | 131 | async tests 132 | ----------- 133 | 134 | jasmine-node includes an alternate syntax for writing asynchronous tests. Accepting 135 | a done callback in the specification will trigger jasmine-node to run the test 136 | asynchronously waiting until the `done()` callback is called. 137 | 138 | ```javascript 139 | var request = require('request'); 140 | 141 | it("should respond with hello world", function(done) { 142 | request("http://localhost:3000/hello", function(error, response, body){ 143 | expect(body).toEqual("hello world"); 144 | done(); 145 | }); 146 | }); 147 | ``` 148 | 149 | An asynchronous test will fail after `5000` ms if `done()` is not called. This timeout 150 | can be changed by setting `jasmine.getEnv().defaultTimeoutInterval` or by passing a timeout 151 | interval in the specification. 152 | 153 | ```javascript 154 | var request = require('request'); 155 | 156 | it("should respond with hello world", function(done) { 157 | request("http://localhost:3000/hello", function(error, response, body){ 158 | done(); 159 | }); 160 | }, 250); // timeout after 250 ms 161 | ``` 162 | 163 | or 164 | 165 | ```javascript 166 | var request = require('request'); 167 | 168 | jasmine.getEnv().defaultTimeoutInterval = 500; 169 | 170 | it("should respond with hello world", function(done) { 171 | request("http://localhost:3000/hello", function(error, response, body){ 172 | done(); 173 | }); // timeout after 500 ms 174 | }); 175 | ``` 176 | 177 | Checkout [`spec/SampleSpecs.js`](https://github.com/mhevery/jasmine-node/blob/master/spec/SampleSpecs.js) to see how to use it. 178 | 179 | 180 | requirejs 181 | --------- 182 | 183 | There is a sample project in `/spec-requirejs`. It is comprised of: 184 | 185 | 1. `requirejs-setup.js`, this pulls in our wrapper template (next) 186 | 1. `requirejs-wrapper-template`, this builds up requirejs settings 187 | 1. `requirejs.sut.js`, this is a __SU__bject To __T__est, something required by requirejs 188 | 1. `requirejs.spec.js`, the actual jasmine spec for testing 189 | 190 | To run it: 191 | 192 | ```sh 193 | node lib/jasmine-node/cli.js --runWithRequireJs --requireJsSetup ./spec-requirejs/requirejs-setup.js ./spec-requirejs/ 194 | ``` 195 | 196 | exceptions 197 | ---------- 198 | 199 | Often you'll want to capture an uncaught exception and log it to the console, 200 | this is accomplished by using the `--captureExceptions` flag. Exceptions will 201 | be reported to the console, but jasmine-node will attempt to recover and 202 | continue. It was decided to not change the current functionality until `2.0`. So, 203 | until then, jasmine-node will still return `0` and continue on without this flag. 204 | 205 | ### Scenario ### 206 | 207 | You require a module, but it doesn't exist, ie `require('Q')` instead of 208 | `require('q')`. Jasmine-Node reports the error to the console, but carries on 209 | and returns `0`. This messes up Travis-CI because you need it to return a 210 | non-zero status while doing CI tests. 211 | 212 | ### Mitigation ### 213 | 214 | Before `--captureExceptions` 215 | 216 | ```sh 217 | > jasmine-node --coffee spec 218 | > echo $status 219 | 0 220 | ``` 221 | 222 | Run jasmine node with the `--captureExceptions` flag. 223 | 224 | ```sh 225 | > jasmine-node --coffee --captureExceptions spec 226 | > echo $status 227 | 1 228 | ``` 229 | 230 | 231 | growl notifications 232 | ------------------- 233 | 234 | Jasmine node can display [Growl](http://growl.info) notifications of test 235 | run summaries in addition to other reports. 236 | Growl must be installed separately, see [node-growl](https://github.com/visionmedia/node-growl) 237 | for platform-specific instructions. Pass the `--growl` flag to enable the notifications. 238 | 239 | 240 | development 241 | ----------- 242 | 243 | Install the dependent packages by running: 244 | 245 | ```sh 246 | npm install 247 | ``` 248 | 249 | Run the specs before you send your pull request: 250 | 251 | ```sh 252 | specs.sh 253 | ``` 254 | 255 | __Note:__ Some tests are designed to fail in the specs.sh. After each of the 256 | individual runs completes, there is a line that lists what the expected 257 | Pass/Assert/Fail count should be. If you add/remove/edit tests, please be sure 258 | to update this with your PR. 259 | 260 | 261 | changelog 262 | --------- 263 | 264 | * `1.16.2` Back to `jasmine-growl-reporter@~0.2.0` (needed by Node.js pre-4.0) 265 | * `1.16.1` Use `~` instead of `^` in `dependencies` (may be needed by some really old npm versions) 266 | * `1.16.0` Fix `dependencies` to prevent major package updates 267 | * `1.15.0` Switch to coffeescript package 268 | * `1.14.6` Update dependencies to resolve `npm audit` issues 269 | * `1.14.5` Using `~` instead of `^` for reporter version (thanks to [Maxim-Filimonov](https://github.com/Maxim-Filimonov)) 270 | * `1.14.4` Rolled back jasmine reporter version (thanks to [tjmcduffie](https://github.com/tjmcduffie)) 271 | * `1.14.3` Added 'onComplete' callback to TeamCityReporter (thanks to [JoergFiedler](https://github.com/JoergFiedler)) 272 | * `1.14.2` Uhhh...not sure what happened here. 273 | * `1.14.1` Default to noColors if not in a TTY 274 | * `1.14.0` Add support for `iit`, `ddescribe` (thanks to [mgcrea](https://github.com/mgcrea)) 275 | * `1.13.1` Add coffee-script support for 1.7.x (thanks to [nathancarter](https://github.com/nathancarter)) 276 | * `1.13.0` Added timing to the verbose reporter (thanks to [rick-kilgore](https://github.com/rick-kilgore)) 277 | * `1.12.1` Fixed an issue where an undefined variable caused an unhelpful 278 | exception in --watch Resolves #278 279 | * `1.12.0` 280 | * Changed `util.print` to `stdout.write` (thanks to [nrstott](https://github.com/nrstott)) 281 | * Don’t affect line numbers with --requireJsSetup (thanks to [daviddaurelio](https://github.com/davidaurelio)) 282 | * Catch errors when loading helpers (thanks to [pimterry](https://github.com/pimterry)) 283 | * Keep autotesting until all tests have passed (thanks to [notclive](https://github.com/notclive)) 284 | * `1.11.0` Added Growl notification option `--growl` (thanks to 285 | [AlphaHydrae](https://github.com/AlphaHydrae)) 286 | * `1.10.2` Restored stack filter which was accidentally removed (thanks to 287 | [kevinsawicki](https://github.com/kevinsawicki)) 288 | * `1.10.1` `beforeEach` and `afterEach` now properly handle the async-timeout function 289 | * `1.10.0` Skipped tests now show in the terminal reporter's output (thanks 290 | to [kevinsawicki](https://github.com/kevinsawicki)) 291 | * `1.9.1` Timeout now consistent between Async and Non-Async Calls (thanks to 292 | [codemnky](https://github.com/codemnky)) 293 | * `1.9.0` Now re-throwing the file-not-found error, added info to README.md, 294 | printing version with `--version` 295 | * `1.8.1` Fixed silent failure due to invalid REGEX (thanks to 296 | [pimterry](https://github.com/pimterry)) 297 | * `1.8.0` Fixed bug in autotest with multiple paths and added `--watch` feature 298 | (thanks to [davegb3](https://github.com/davegb3)) 299 | * `1.7.1` Removed unneeded fs dependency (thanks to 300 | [kevinsawicki](https://github.com/kevinsawicki)) Fixed broken fs call in 301 | node `0.6` (thanks to [abe33](https://github.com/abe33)) 302 | * `1.7.0` Literate CoffeeScript now testable (thanks to [magicmoose](https://github.com/magicmoose)) 303 | * `1.6.0` Teamcity Reporter Reinstated (thanks to [bhcleek](https://github.com/bhcleek)) 304 | * `1.5.1` Missing files and require exceptions will now report instead of failing silently 305 | * `1.5.0` Now takes multiple files for execution. (thanks to [abe33](https://github.com/abe33)) 306 | * `1.4.0` Optional flag to suppress stack trace on test failure (thanks to [Lastalas](https://github.com/Lastalas)) 307 | * `1.3.1` Fixed context for async tests (thanks to [omryn](https://github.com/omryn)) 308 | * `1.3.0` Added `--config` flag for changeable testing environments 309 | * `1.2.3` Fixed #179, #184, #198, #199. Fixes autotest, afterEach in requirejs, terminal reporter is in jasmine object, done function missing in async tests 310 | * `1.2.2` Revert Exception Capturing to avoid Breaking Domain Tests 311 | * `1.2.1` Emergency fix for path reference missing 312 | * `1.2.0` Fixed #149, #152, #171, #181, #195. `--autotest` now works as expected, jasmine clock now responds to the fake ticking as requested, and removed the path.exists warning 313 | * `1.1.1` Fixed #173, #169 (Blocks were not indented in verbose properly, added more documentation to address #180 314 | * `1.1.0` Updated Jasmine to `1.3.1`, fixed fs missing, catching uncaught exceptions, other fixes 315 | -------------------------------------------------------------------------------- /bin/jasmine-node: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | if( !process.env.NODE_ENV ) process.env.NODE_ENV = 'test'; 4 | 5 | var path = require('path'); 6 | require(path.join(__dirname,'../lib/jasmine-node/cli.js')); 7 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jasmine-node", 3 | "version": "1.12.0", 4 | "homepage": "https://github.com/mhevery/jasmine-node", 5 | "authors": [ 6 | "Misko Hevery ", 7 | "Chris M " 8 | ], 9 | "description": "Integration of Jasmine Spec framework with Node.js", 10 | "main": "lib/jasmine-node", 11 | "keywords": [ 12 | "jasmine", 13 | "node", 14 | "commonjs", 15 | "node" 16 | ], 17 | "license": "MIT", 18 | "private": true 19 | } 20 | -------------------------------------------------------------------------------- /lib/jasmine-node/async-callback.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var withoutAsync = {}; 3 | 4 | ["it", "beforeEach", "afterEach"].forEach(function(jasmineFunction) { 5 | withoutAsync[jasmineFunction] = jasmine.Env.prototype[jasmineFunction]; 6 | return jasmine.Env.prototype[jasmineFunction] = function() { 7 | var args = Array.prototype.slice.call(arguments, 0); 8 | var timeout = null; 9 | if (isLastArgumentATimeout(args)) { 10 | timeout = args.pop(); 11 | // The changes to the jasmine test runner causes undef to be passed when 12 | // calling all it()'s now. If the last argument isn't a timeout and the 13 | // last argument IS undefined, let's just pop it off. Since out of bounds 14 | // items are undefined anyways, *hopefully* removing an undef item won't 15 | // hurt. 16 | } else if (args[args.length-1] == undefined) { 17 | args.pop(); 18 | } 19 | if (isLastArgumentAnAsyncSpecFunction(args)) 20 | { 21 | var specFunction = args.pop(); 22 | args.push(function() { 23 | return asyncSpec(specFunction, this, timeout); 24 | }); 25 | } 26 | return withoutAsync[jasmineFunction].apply(this, args); 27 | }; 28 | }); 29 | 30 | function isLastArgumentATimeout(args) 31 | { 32 | return args.length > 0 && (typeof args[args.length-1]) === "number"; 33 | } 34 | 35 | function isLastArgumentAnAsyncSpecFunction(args) 36 | { 37 | return args.length > 0 && (typeof args[args.length-1]) === "function" && args[args.length-1].length > 0; 38 | } 39 | 40 | function asyncSpec(specFunction, spec, timeout) { 41 | if (timeout == null) timeout = jasmine.getEnv().defaultTimeoutInterval || 1000; 42 | var done = false; 43 | spec.runs(function() { 44 | try { 45 | return specFunction.call(spec, function(error) { 46 | done = true; 47 | if (error != null) return spec.fail(error); 48 | }); 49 | } catch (e) { 50 | done = true; 51 | throw e; 52 | } 53 | }); 54 | return spec.waitsFor(function() { 55 | if (done === true) { 56 | return true; 57 | } 58 | }, "spec to complete", timeout); 59 | }; 60 | 61 | }).call(this); 62 | -------------------------------------------------------------------------------- /lib/jasmine-node/autotest.js: -------------------------------------------------------------------------------- 1 | var walkdir = require('walkdir'); 2 | var collection = require('./spec-collection'); 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var child_process = require('child_process'); 6 | var gaze = require('gaze'); 7 | var _ = require('underscore'); 8 | 9 | var baseArgv = []; 10 | 11 | for(var i = 0; i < process.argv.length; i++) { 12 | if(process.argv[i] !== '--autotest') { 13 | baseArgv.push(process.argv[i]); 14 | } 15 | } 16 | 17 | var run_external = function(command, args, callback) { 18 | var child = child_process.spawn(command, args); 19 | child.stdout.on('data', function(data) { 20 | process.stdout.write(data); 21 | }); 22 | child.stderr.on('data', function(data) { 23 | process.stderr.write(data); 24 | }); 25 | if(typeof callback == 'function') { 26 | child.on('exit', callback); 27 | } 28 | } 29 | 30 | var last_run_successful = false; 31 | 32 | var run_everything = function() { 33 | // run the suite when it starts 34 | var argv = [].concat(baseArgv); 35 | run_external(argv.shift(), argv, function (code) { 36 | last_run_successful = code === 0 37 | }); 38 | } 39 | 40 | exports.start = function(loadpaths, watchFolders, patterns) { 41 | var watchPatterns; 42 | 43 | loadpaths.forEach(function(loadpath){ 44 | 45 | // If loadpath is just a single file, we should just watch that file 46 | stats = fs.statSync(loadpath); 47 | if (stats.isFile()) { 48 | watchPatterns = loadpath; 49 | } else { 50 | watchPatterns = patterns.map(function(p) { 51 | return path.join(loadpath, p); 52 | }); 53 | } 54 | 55 | changedFunc = function(event, file) { 56 | console.log(file + ' was changed'); 57 | 58 | var match = path.basename(file, path.extname(file)) + ".*"; 59 | match = match.replace(new RegExp("spec", "i"), ""); 60 | 61 | var argv = [].concat(baseArgv, ["--match", match]); 62 | run_external(argv.shift(), argv, function(code) { 63 | // run everything if we fixed some bugs 64 | if(code == 0) { 65 | if(!last_run_successful) { 66 | run_everything(); 67 | } 68 | } else { 69 | last_run_successful = false; 70 | } 71 | }); 72 | } 73 | 74 | // Vim seems to change a file multiple times, with non-scientific testing 75 | // the only time we didn't duplicate the call to onChanged was at 2.5s 76 | // Passing true to have onChanged run on the leading edge of the timeout 77 | var onChanged = _.debounce(changedFunc, 2500, true); 78 | 79 | gaze(watchPatterns, function(err, watcher) { 80 | // Get all watched files 81 | console.log("Watching for changes in " + loadpath); 82 | 83 | // On file changed 84 | this.on('all', onChanged); 85 | }); 86 | 87 | 88 | 89 | }); 90 | 91 | 92 | watchFolders.forEach(function(watchPath) { 93 | // If watchPath is just a single file, we should just watch that file 94 | stats = fs.statSync(watchPath); 95 | if (stats.isFile()) { 96 | watchPatterns = watchPath; 97 | } else { 98 | watchPatterns = patterns.map(function(p) { 99 | return path.join(watchPath, p); 100 | }); 101 | } 102 | 103 | // We debounce run_everything here due to the Vim issue described above. 104 | var onChanged = _.debounce(run_everything, 2500, true); 105 | 106 | 107 | gaze(watchPatterns, function(err, watcher) { 108 | console.log("Watching for changes in " + watchPath); 109 | 110 | this.on('all', onChanged); 111 | }); 112 | 113 | 114 | 115 | }); 116 | 117 | run_everything(); 118 | }; 119 | -------------------------------------------------------------------------------- /lib/jasmine-node/cli.js: -------------------------------------------------------------------------------- 1 | var util, 2 | Path= require('path'), 3 | fs = require('fs'); 4 | 5 | var jasmine = require('./index'); 6 | 7 | 8 | try { 9 | util = require('util') 10 | } catch(e) { 11 | util = require('sys') 12 | } 13 | 14 | var helperCollection = require('./spec-collection'); 15 | 16 | var specFolders = []; 17 | var watchFolders = []; 18 | 19 | // The following line keeps the jasmine setTimeout in the proper scope 20 | jasmine.setTimeout = jasmine.getGlobal().setTimeout; 21 | jasmine.setInterval = jasmine.getGlobal().setInterval; 22 | for (var key in jasmine) 23 | global[key] = jasmine[key]; 24 | 25 | var isVerbose = false; 26 | var showColors = true; 27 | // Disable if we're not on a TTY 28 | if (!process.stdout.isTTY) { 29 | showColors = false; 30 | } 31 | var teamcity = process.env.TEAMCITY_PROJECT_NAME || false; 32 | var useRequireJs = false; 33 | var extensions = "js"; 34 | var match = '.'; 35 | var matchall = false; 36 | var autotest = false; 37 | var useHelpers = true; 38 | var forceExit = false; 39 | var captureExceptions = false; 40 | var includeStackTrace = true; 41 | var growl = false; 42 | 43 | var junitreport = { 44 | report: false, 45 | savePath : "./reports/", 46 | useDotNotation: true, 47 | consolidate: true 48 | } 49 | 50 | var args = process.argv.slice(2); 51 | var existsSync = fs.existsSync || Path.existsSync; 52 | 53 | while(args.length) { 54 | var arg = args.shift(); 55 | 56 | switch(arg) 57 | { 58 | case '--version': 59 | printVersion(); 60 | case '--color': 61 | showColors = true; 62 | break; 63 | case '--noColor': 64 | case '--nocolor': 65 | showColors = false; 66 | break; 67 | case '--verbose': 68 | isVerbose = true; 69 | break; 70 | case '--coffee': 71 | try { 72 | require('coffeescript/register'); // support CoffeeScript >=1.7.0 73 | } catch ( e ) { 74 | require('coffeescript'); // support CoffeeScript <=1.6.3 75 | } 76 | extensions = "js|coffee|litcoffee"; 77 | break; 78 | case '-m': 79 | case '--match': 80 | match = args.shift(); 81 | break; 82 | case '--matchall': 83 | matchall = true; 84 | break; 85 | case '--junitreport': 86 | junitreport.report = true; 87 | break; 88 | case '--output': 89 | junitreport.savePath = args.shift(); 90 | break; 91 | case '--teamcity': 92 | teamcity = true; 93 | break; 94 | case '--requireJsSetup': 95 | var setup = args.shift(); 96 | 97 | if(!existsSync(setup)) 98 | throw new Error("RequireJS setup '" + setup + "' doesn't exist!"); 99 | 100 | useRequireJs = setup; 101 | break; 102 | case '--runWithRequireJs': 103 | useRequireJs = useRequireJs || true; 104 | break; 105 | case '--nohelpers': 106 | useHelpers = false; 107 | break; 108 | case '--test-dir': 109 | var dir = args.shift(); 110 | 111 | if(!existsSync(dir)) 112 | throw new Error("Test root path '" + dir + "' doesn't exist!"); 113 | 114 | specFolders.push(dir); // NOTE: Does not look from current working directory. 115 | break; 116 | case '--autotest': 117 | autotest = true; 118 | break; 119 | case '--watch': 120 | var nextWatchDir; 121 | 122 | // Add the following arguments, until we see another argument starting with '-' 123 | while (args[0] && args[0][0] !== '-') { 124 | nextWatchDir = args.shift(); 125 | watchFolders.push(nextWatchDir); 126 | 127 | if (!existsSync(nextWatchDir)) 128 | throw new Error("Watch path '" + nextWatchDir + "' doesn't exist!"); 129 | } 130 | break; 131 | 132 | case '--forceexit': 133 | forceExit = true; 134 | break; 135 | case '--captureExceptions': 136 | captureExceptions = true; 137 | break; 138 | case '--noStack': 139 | includeStackTrace = false; 140 | break; 141 | case '--growl': 142 | growl = true; 143 | break; 144 | case '--config': 145 | var configKey = args.shift(); 146 | var configValue = args.shift(); 147 | process.env[configKey]=configValue; 148 | break; 149 | case '-h': 150 | help(); 151 | default: 152 | if (arg.match(/^--params=.*/)) { 153 | break; 154 | } 155 | if (arg.match(/^--/)) help(); 156 | if (arg.match(/^\/.*/)) { 157 | specFolders.push(arg); 158 | } else { 159 | specFolders.push(Path.join(process.cwd(), arg)); 160 | } 161 | break; 162 | } 163 | } 164 | 165 | if (specFolders.length === 0) { 166 | help(); 167 | } else { 168 | // Check to see if all our files exist 169 | for (var idx = 0; idx < specFolders.length; idx++) { 170 | if (!existsSync(specFolders[idx])) { 171 | console.log("File: " + specFolders[idx] + " is missing."); 172 | return; 173 | } 174 | } 175 | } 176 | 177 | if (autotest) { 178 | var patterns = ['**/*.js']; 179 | 180 | if (extensions.indexOf("coffee") !== -1) { 181 | patterns.push('**/*.coffee'); 182 | } 183 | 184 | require('./autotest').start(specFolders, watchFolders, patterns); 185 | 186 | return; 187 | } 188 | 189 | var exitCode = 0; 190 | 191 | if (captureExceptions) { 192 | process.on('uncaughtException', function(e) { 193 | console.error(e.stack || e); 194 | exitCode = 1; 195 | process.exit(exitCode); 196 | }); 197 | } 198 | 199 | process.on("exit", onExit); 200 | 201 | function onExit() { 202 | process.removeListener("exit", onExit); 203 | process.exit(exitCode); 204 | } 205 | 206 | var onComplete = function(runner, log) { 207 | process.stdout.write('\n'); 208 | if (runner.results().failedCount == 0) { 209 | exitCode = 0; 210 | } else { 211 | exitCode = 1; 212 | } 213 | if (forceExit) { 214 | process.exit(exitCode); 215 | } 216 | }; 217 | 218 | if(useHelpers){ 219 | specFolders.forEach(function(path){ 220 | jasmine.loadHelpersInFolder(path, 221 | new RegExp("helpers?\\.(" + extensions + ")$", 'i')); 222 | 223 | }) 224 | } 225 | 226 | try { 227 | var regExpSpec = new RegExp(match + (matchall ? "" : "spec\\.") + "(" + extensions + ")$", 'i') 228 | } catch (error) { 229 | console.error("Failed to build spec-matching regex: " + error); 230 | process.exit(2); 231 | } 232 | 233 | 234 | var options = { 235 | specFolders: specFolders, 236 | onComplete: onComplete, 237 | isVerbose: isVerbose, 238 | showColors: showColors, 239 | teamcity: teamcity, 240 | useRequireJs: useRequireJs, 241 | regExpSpec: regExpSpec, 242 | junitreport: junitreport, 243 | includeStackTrace: includeStackTrace, 244 | growl: growl 245 | } 246 | 247 | jasmine.executeSpecsInFolder(options); 248 | 249 | 250 | function help(){ 251 | process.stdout.write([ 252 | 'USAGE: jasmine-node [--color|--noColor] [--verbose] [--coffee] directory' 253 | , '' 254 | , 'Options:' 255 | , ' --autotest - rerun automatically the specs when a file changes' 256 | , ' --watch PATH - when used with --autotest, watches the given path(s) and runs all tests if a change is detected' 257 | , ' --color - use color coding for output' 258 | , ' --noColor - do not use color coding for output' 259 | , ' -m, --match REGEXP - load only specs containing "REGEXPspec"' 260 | , ' --matchall - relax requirement of "spec" in spec file names' 261 | , ' --verbose - print extra information per each test run' 262 | , ' --coffee - load coffeescript which allows execution .coffee files' 263 | , ' --junitreport - export tests results as junitreport xml format' 264 | , ' --output - defines the output folder for junitreport files' 265 | , ' --teamcity - converts all console output to teamcity custom test runner commands. (Normally auto detected.)' 266 | , ' --growl - display test run summary in a growl notification (in addition to other outputs)' 267 | , ' --runWithRequireJs - loads all specs using requirejs instead of node\'s native require method' 268 | , ' --requireJsSetup - file run before specs to include and configure RequireJS' 269 | , ' --test-dir - the absolute root directory path where tests are located' 270 | , ' --nohelpers - does not load helpers.' 271 | , ' --forceexit - force exit once tests complete.' 272 | , ' --captureExceptions- listen to global exceptions, report them and exit (interferes with Domains)' 273 | , ' --config NAME VALUE- set a global variable in process.env' 274 | , ' --noStack - suppress the stack trace generated from a test failure' 275 | , ' --version - show the current version' 276 | , ' -h, --help - display this help and exit' 277 | , '' 278 | ].join("\n")); 279 | 280 | process.exit(-1); 281 | } 282 | 283 | function printVersion(){ 284 | const package = require(__dirname + '/../../package.json') 285 | console.log(package.version); 286 | process.exit(0); 287 | } 288 | -------------------------------------------------------------------------------- /lib/jasmine-node/cs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license cs 0.4.2 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/jrburke/require-cs for details 5 | */ 6 | 7 | /*jslint */ 8 | /*global define, window, XMLHttpRequest, importScripts, Packages, java, 9 | ActiveXObject, process, require */ 10 | 11 | define(['coffeescript'], function (CoffeeScript) { 12 | 'use strict'; 13 | var fs, getXhr, 14 | progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], 15 | fetchText = function () { 16 | throw new Error('Environment unsupported.'); 17 | }, 18 | buildMap = {}; 19 | 20 | if (typeof process !== "undefined" && 21 | process.versions && 22 | !!process.versions.node) { 23 | //Using special require.nodeRequire, something added by r.js. 24 | fs = require.nodeRequire('fs'); 25 | fetchText = function (path, callback) { 26 | callback(fs.readFileSync(path, 'utf8')); 27 | }; 28 | } else if ((typeof window !== "undefined" && window.navigator && window.document) || typeof importScripts !== "undefined") { 29 | // Browser action 30 | getXhr = function () { 31 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first. 32 | var xhr, i, progId; 33 | if (typeof XMLHttpRequest !== "undefined") { 34 | return new XMLHttpRequest(); 35 | } else { 36 | for (i = 0; i < 3; i++) { 37 | progId = progIds[i]; 38 | try { 39 | xhr = new ActiveXObject(progId); 40 | } catch (e) {} 41 | 42 | if (xhr) { 43 | progIds = [progId]; // so faster next time 44 | break; 45 | } 46 | } 47 | } 48 | 49 | if (!xhr) { 50 | throw new Error("getXhr(): XMLHttpRequest not available"); 51 | } 52 | 53 | return xhr; 54 | }; 55 | 56 | fetchText = function (url, callback) { 57 | var xhr = getXhr(); 58 | xhr.open('GET', url, true); 59 | xhr.onreadystatechange = function (evt) { 60 | //Do not explicitly handle errors, those should be 61 | //visible via console output in the browser. 62 | if (xhr.readyState === 4) { 63 | callback(xhr.responseText); 64 | } 65 | }; 66 | xhr.send(null); 67 | }; 68 | // end browser.js adapters 69 | } else if (typeof Packages !== 'undefined') { 70 | //Why Java, why is this so awkward? 71 | fetchText = function (path, callback) { 72 | var encoding = "utf-8", 73 | file = new java.io.File(path), 74 | lineSeparator = java.lang.System.getProperty("line.separator"), 75 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), 76 | stringBuffer, line, 77 | content = ''; 78 | try { 79 | stringBuffer = new java.lang.StringBuffer(); 80 | line = input.readLine(); 81 | 82 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 83 | // http://www.unicode.org/faq/utf_bom.html 84 | 85 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: 86 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 87 | if (line && line.length() && line.charAt(0) === 0xfeff) { 88 | // Eat the BOM, since we've already found the encoding on this file, 89 | // and we plan to concatenating this buffer with others; the BOM should 90 | // only appear at the top of a file. 91 | line = line.substring(1); 92 | } 93 | 94 | stringBuffer.append(line); 95 | 96 | while ((line = input.readLine()) !== null) { 97 | stringBuffer.append(lineSeparator); 98 | stringBuffer.append(line); 99 | } 100 | //Make sure we return a JavaScript string and not a Java string. 101 | content = String(stringBuffer.toString()); //String 102 | } finally { 103 | input.close(); 104 | } 105 | callback(content); 106 | }; 107 | } 108 | 109 | return { 110 | get: function () { 111 | return CoffeeScript; 112 | }, 113 | 114 | write: function (pluginName, name, write) { 115 | if (buildMap.hasOwnProperty(name)) { 116 | var text = buildMap[name]; 117 | write.asModule(pluginName + "!" + name, text); 118 | } 119 | }, 120 | 121 | version: '0.4.2', 122 | 123 | load: function (name, parentRequire, load, config) { 124 | var path = parentRequire.toUrl(name + '.coffee'); 125 | fetchText(path, function (text) { 126 | 127 | //Do CoffeeScript transform. 128 | try { 129 | text = CoffeeScript.compile(text, config.CoffeeScript); 130 | } 131 | catch (err) { 132 | err.message = "In " + path + ", " + err.message; 133 | throw(err); 134 | } 135 | 136 | //Hold on to the transformed text if a build. 137 | if (config.isBuild) { 138 | buildMap[name] = text; 139 | } 140 | 141 | //IE with conditional comments on cannot handle the 142 | //sourceURL trick, so skip it if enabled. 143 | /*@if (@_jscript) @else @*/ 144 | if (!config.isBuild) { 145 | text += "\r\n//@ sourceURL=" + path; 146 | } 147 | /*@end@*/ 148 | 149 | load.fromText(name, text); 150 | 151 | //Give result to load. Need to wait until the module 152 | //is fully parse, which will happen after this 153 | //execution. 154 | parentRequire([name], function (value) { 155 | load(value); 156 | }); 157 | }); 158 | } 159 | }; 160 | }); 161 | -------------------------------------------------------------------------------- /lib/jasmine-node/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var mkdirp = require('mkdirp'); 3 | var util; 4 | try { 5 | util = require('util') 6 | } catch(e) { 7 | util = require('sys') 8 | } 9 | 10 | var path = require('path'); 11 | 12 | var filename = __dirname + '/jasmine-1.3.1.js'; 13 | var isWindowUndefined = typeof global.window === 'undefined'; 14 | if (isWindowUndefined) { 15 | global.window = { 16 | setTimeout: setTimeout, 17 | clearTimeout: clearTimeout, 18 | setInterval: setInterval, 19 | clearInterval: clearInterval 20 | }; 21 | } 22 | 23 | var src = fs.readFileSync(filename); 24 | // Put jasmine in the global context, this is somewhat like running in a 25 | // browser where every file will have access to `jasmine` 26 | var jasmine = require('vm').runInThisContext(src + "\njasmine;", filename); 27 | 28 | 29 | if (isWindowUndefined) { 30 | delete global.window; 31 | } 32 | require("./async-callback"); 33 | require("jasmine-reporters"); 34 | var nodeReporters = require('./reporter').jasmineNode; 35 | jasmine.TerminalVerboseReporter = nodeReporters.TerminalVerboseReporter; 36 | jasmine.TerminalReporter = nodeReporters.TerminalReporter; 37 | jasmine.TeamcityReporter = nodeReporters.TeamcityReporter; 38 | jasmine.GrowlReporter = require('jasmine-growl-reporter'); 39 | 40 | 41 | jasmine.loadHelpersInFolder = function(folder, matcher) { 42 | // Check to see if the folder is actually a file, if so, back up to the 43 | // parent directory and find some helpers 44 | folderStats = fs.statSync(folder); 45 | if (folderStats.isFile()) { 46 | folder = path.dirname(folder); 47 | } 48 | 49 | var helpers = [], 50 | helperCollection = require('./spec-collection'); 51 | 52 | helperCollection.load([folder], matcher); 53 | helpers = helperCollection.getSpecs(); 54 | 55 | for (var i = 0, len = helpers.length; i < len; ++i) { 56 | var file = helpers[i].path(); 57 | 58 | try { 59 | var helper = require(file.replace(/\.*$/, "")); 60 | } catch (e) { 61 | console.log("Exception loading helper: " + file) 62 | console.log(e); 63 | throw e; // If any of the helpers fail to load, fail everything 64 | } 65 | 66 | for (var key in helper) { 67 | global[key]= helper[key]; 68 | } 69 | } 70 | }; 71 | 72 | function removeJasmineFrames(text) { 73 | if (!text) { 74 | return text; 75 | } 76 | 77 | var lines = []; 78 | text.split(/\n/).forEach(function(line){ 79 | if (line.indexOf(filename) == -1) { 80 | lines.push(line); 81 | } 82 | }); 83 | return lines.join('\n'); 84 | } 85 | 86 | jasmine.executeSpecsInFolder = function(options){ 87 | var folders = options['specFolders']; 88 | var done = options['onComplete']; 89 | var isVerbose = options['isVerbose']; 90 | var showColors = options['showColors']; 91 | var teamcity = options['teamcity']; 92 | var useRequireJs = options['useRequireJs']; 93 | var matcher = options['regExpSpec']; 94 | var junitreport = options['junitreport']; 95 | var includeStackTrace = options['includeStackTrace']; 96 | var growl = options['growl']; 97 | 98 | // Overwriting it allows us to handle custom async specs 99 | it = function(desc, func, timeout) { 100 | return jasmine.getEnv().it(desc, func, timeout); 101 | } 102 | beforeEach = function(func, timeout) { 103 | return jasmine.getEnv().beforeEach(func, timeout); 104 | } 105 | afterEach = function(func, timeout) { 106 | return jasmine.getEnv().afterEach(func, timeout); 107 | } 108 | var fileMatcher = matcher || new RegExp(".(js)$", "i"), 109 | colors = showColors || false, 110 | specs = require('./spec-collection'), 111 | jasmineEnv = jasmine.getEnv(); 112 | 113 | specs.load(folders, fileMatcher); 114 | 115 | if(junitreport && junitreport.report) { 116 | var existsSync = fs.existsSync || path.existsSync; 117 | if(!existsSync(junitreport.savePath)) { 118 | console.log('creating junit xml report save path: ' + junitreport.savePath); 119 | mkdirp.sync(junitreport.savePath, "0755"); 120 | } 121 | jasmineEnv.addReporter(new jasmine.JUnitXmlReporter(junitreport.savePath, 122 | junitreport.consolidate, 123 | junitreport.useDotNotation)); 124 | } 125 | 126 | if(teamcity){ 127 | jasmineEnv.addReporter(new jasmine.TeamcityReporter({onComplete: done})); 128 | } else if(isVerbose) { 129 | jasmineEnv.addReporter(new jasmine.TerminalVerboseReporter({ print: print, 130 | color: showColors, 131 | onComplete: done, 132 | stackFilter: removeJasmineFrames})); 133 | } else { 134 | jasmineEnv.addReporter(new jasmine.TerminalReporter({print: print, 135 | color: showColors, 136 | includeStackTrace: includeStackTrace, 137 | onComplete: done, 138 | stackFilter: removeJasmineFrames})); 139 | } 140 | 141 | if (growl) { 142 | jasmineEnv.addReporter(new jasmine.GrowlReporter()); 143 | } 144 | 145 | if (useRequireJs) { 146 | require('./requirejs-runner').executeJsRunner( 147 | specs, 148 | done, 149 | jasmineEnv, 150 | typeof useRequireJs === 'string' ? useRequireJs : null 151 | ); 152 | } else { 153 | var specsList = specs.getSpecs(); 154 | 155 | for (var i = 0, len = specsList.length; i < len; ++i) { 156 | var filename = specsList[i]; 157 | delete require.cache[filename.path()]; 158 | // Catch exceptions in loading the spec 159 | try { 160 | require(filename.path().replace(/\.\w+$/, "")); 161 | } catch (e) { 162 | console.log("Exception loading: " + filename.path()); 163 | console.log(e); 164 | throw e; 165 | } 166 | } 167 | 168 | jasmineEnv.execute(); 169 | } 170 | }; 171 | 172 | function now(){ 173 | return new Date().getTime(); 174 | } 175 | 176 | jasmine.asyncSpecWait = function(){ 177 | var wait = jasmine.asyncSpecWait; 178 | wait.start = now(); 179 | wait.done = false; 180 | (function innerWait(){ 181 | waits(10); 182 | runs(function() { 183 | if (wait.start + wait.timeout < now()) { 184 | expect('timeout waiting for spec').toBeNull(); 185 | } else if (wait.done) { 186 | wait.done = false; 187 | } else { 188 | innerWait(); 189 | } 190 | }); 191 | })(); 192 | }; 193 | jasmine.asyncSpecWait.timeout = 4 * 1000; 194 | jasmine.asyncSpecDone = function(){ 195 | jasmine.asyncSpecWait.done = true; 196 | }; 197 | 198 | function print(str) { 199 | process.stdout.write(util.format(str)); 200 | } 201 | 202 | for ( var key in jasmine) { 203 | exports[key] = jasmine[key]; 204 | } 205 | -------------------------------------------------------------------------------- /lib/jasmine-node/jasmine-1.3.1.js: -------------------------------------------------------------------------------- 1 | var isCommonJS = typeof window == "undefined" && typeof exports == "object"; 2 | 3 | /** 4 | * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. 5 | * 6 | * @namespace 7 | */ 8 | var jasmine = {}; 9 | if (isCommonJS) exports.jasmine = jasmine; 10 | /** 11 | * @private 12 | */ 13 | jasmine.unimplementedMethod_ = function() { 14 | throw new Error("unimplemented method"); 15 | }; 16 | 17 | /** 18 | * Use jasmine.undefined instead of undefined, since undefined is just 19 | * a plain old variable and may be redefined by somebody else. 20 | * 21 | * @private 22 | */ 23 | jasmine.undefined = jasmine.___undefined___; 24 | 25 | /** 26 | * Show diagnostic messages in the console if set to true 27 | * 28 | */ 29 | jasmine.VERBOSE = false; 30 | 31 | /** 32 | * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. 33 | * 34 | */ 35 | jasmine.DEFAULT_UPDATE_INTERVAL = 250; 36 | 37 | /** 38 | * Maximum levels of nesting that will be included when an object is pretty-printed 39 | */ 40 | jasmine.MAX_PRETTY_PRINT_DEPTH = 40; 41 | 42 | /** 43 | * Default timeout interval in milliseconds for waitsFor() blocks. 44 | */ 45 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; 46 | 47 | /** 48 | * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite. 49 | * Set to false to let the exception bubble up in the browser. 50 | * 51 | */ 52 | jasmine.CATCH_EXCEPTIONS = true; 53 | 54 | jasmine.getGlobal = function() { 55 | function getGlobal() { 56 | return this; 57 | } 58 | 59 | return getGlobal(); 60 | }; 61 | 62 | /** 63 | * Allows for bound functions to be compared. Internal use only. 64 | * 65 | * @ignore 66 | * @private 67 | * @param base {Object} bound 'this' for the function 68 | * @param name {Function} function to find 69 | */ 70 | jasmine.bindOriginal_ = function(base, name) { 71 | var original = base[name]; 72 | if (original.apply) { 73 | return function() { 74 | return original.apply(base, arguments); 75 | }; 76 | } else { 77 | // IE support 78 | return jasmine.getGlobal()[name]; 79 | } 80 | }; 81 | 82 | jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); 83 | jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); 84 | jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); 85 | jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); 86 | 87 | jasmine.MessageResult = function(values) { 88 | this.type = 'log'; 89 | this.values = values; 90 | this.trace = new Error(); // todo: test better 91 | }; 92 | 93 | jasmine.MessageResult.prototype.toString = function() { 94 | var text = ""; 95 | for (var i = 0; i < this.values.length; i++) { 96 | if (i > 0) text += " "; 97 | if (jasmine.isString_(this.values[i])) { 98 | text += this.values[i]; 99 | } else { 100 | text += jasmine.pp(this.values[i]); 101 | } 102 | } 103 | return text; 104 | }; 105 | 106 | jasmine.ExpectationResult = function(params) { 107 | this.type = 'expect'; 108 | this.matcherName = params.matcherName; 109 | this.passed_ = params.passed; 110 | this.expected = params.expected; 111 | this.actual = params.actual; 112 | this.message = this.passed_ ? 'Passed.' : params.message; 113 | 114 | var trace = (params.trace || new Error(this.message)); 115 | this.trace = this.passed_ ? '' : trace; 116 | }; 117 | 118 | jasmine.ExpectationResult.prototype.toString = function () { 119 | return this.message; 120 | }; 121 | 122 | jasmine.ExpectationResult.prototype.passed = function () { 123 | return this.passed_; 124 | }; 125 | 126 | /** 127 | * Getter for the Jasmine environment. Ensures one gets created 128 | */ 129 | jasmine.getEnv = function() { 130 | var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); 131 | return env; 132 | }; 133 | 134 | /** 135 | * @ignore 136 | * @private 137 | * @param value 138 | * @returns {Boolean} 139 | */ 140 | jasmine.isArray_ = function(value) { 141 | return jasmine.isA_("Array", value); 142 | }; 143 | 144 | /** 145 | * @ignore 146 | * @private 147 | * @param value 148 | * @returns {Boolean} 149 | */ 150 | jasmine.isString_ = function(value) { 151 | return jasmine.isA_("String", value); 152 | }; 153 | 154 | /** 155 | * @ignore 156 | * @private 157 | * @param value 158 | * @returns {Boolean} 159 | */ 160 | jasmine.isNumber_ = function(value) { 161 | return jasmine.isA_("Number", value); 162 | }; 163 | 164 | /** 165 | * @ignore 166 | * @private 167 | * @param {String} typeName 168 | * @param value 169 | * @returns {Boolean} 170 | */ 171 | jasmine.isA_ = function(typeName, value) { 172 | return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; 173 | }; 174 | 175 | /** 176 | * Pretty printer for expecations. Takes any object and turns it into a human-readable string. 177 | * 178 | * @param value {Object} an object to be outputted 179 | * @returns {String} 180 | */ 181 | jasmine.pp = function(value) { 182 | var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); 183 | stringPrettyPrinter.format(value); 184 | return stringPrettyPrinter.string; 185 | }; 186 | 187 | /** 188 | * Returns true if the object is a DOM Node. 189 | * 190 | * @param {Object} obj object to check 191 | * @returns {Boolean} 192 | */ 193 | jasmine.isDomNode = function(obj) { 194 | return obj.nodeType > 0; 195 | }; 196 | 197 | /** 198 | * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. 199 | * 200 | * @example 201 | * // don't care about which function is passed in, as long as it's a function 202 | * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); 203 | * 204 | * @param {Class} clazz 205 | * @returns matchable object of the type clazz 206 | */ 207 | jasmine.any = function(clazz) { 208 | return new jasmine.Matchers.Any(clazz); 209 | }; 210 | 211 | /** 212 | * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the 213 | * attributes on the object. 214 | * 215 | * @example 216 | * // don't care about any other attributes than foo. 217 | * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); 218 | * 219 | * @param sample {Object} sample 220 | * @returns matchable object for the sample 221 | */ 222 | jasmine.objectContaining = function (sample) { 223 | return new jasmine.Matchers.ObjectContaining(sample); 224 | }; 225 | 226 | /** 227 | * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. 228 | * 229 | * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine 230 | * expectation syntax. Spies can be checked if they were called or not and what the calling params were. 231 | * 232 | * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). 233 | * 234 | * Spies are torn down at the end of every spec. 235 | * 236 | * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. 237 | * 238 | * @example 239 | * // a stub 240 | * var myStub = jasmine.createSpy('myStub'); // can be used anywhere 241 | * 242 | * // spy example 243 | * var foo = { 244 | * not: function(bool) { return !bool; } 245 | * } 246 | * 247 | * // actual foo.not will not be called, execution stops 248 | * spyOn(foo, 'not'); 249 | 250 | // foo.not spied upon, execution will continue to implementation 251 | * spyOn(foo, 'not').andCallThrough(); 252 | * 253 | * // fake example 254 | * var foo = { 255 | * not: function(bool) { return !bool; } 256 | * } 257 | * 258 | * // foo.not(val) will return val 259 | * spyOn(foo, 'not').andCallFake(function(value) {return value;}); 260 | * 261 | * // mock example 262 | * foo.not(7 == 7); 263 | * expect(foo.not).toHaveBeenCalled(); 264 | * expect(foo.not).toHaveBeenCalledWith(true); 265 | * 266 | * @constructor 267 | * @see spyOn, jasmine.createSpy, jasmine.createSpyObj 268 | * @param {String} name 269 | */ 270 | jasmine.Spy = function(name) { 271 | /** 272 | * The name of the spy, if provided. 273 | */ 274 | this.identity = name || 'unknown'; 275 | /** 276 | * Is this Object a spy? 277 | */ 278 | this.isSpy = true; 279 | /** 280 | * The actual function this spy stubs. 281 | */ 282 | this.plan = function() { 283 | }; 284 | /** 285 | * Tracking of the most recent call to the spy. 286 | * @example 287 | * var mySpy = jasmine.createSpy('foo'); 288 | * mySpy(1, 2); 289 | * mySpy.mostRecentCall.args = [1, 2]; 290 | */ 291 | this.mostRecentCall = {}; 292 | 293 | /** 294 | * Holds arguments for each call to the spy, indexed by call count 295 | * @example 296 | * var mySpy = jasmine.createSpy('foo'); 297 | * mySpy(1, 2); 298 | * mySpy(7, 8); 299 | * mySpy.mostRecentCall.args = [7, 8]; 300 | * mySpy.argsForCall[0] = [1, 2]; 301 | * mySpy.argsForCall[1] = [7, 8]; 302 | */ 303 | this.argsForCall = []; 304 | this.calls = []; 305 | }; 306 | 307 | /** 308 | * Tells a spy to call through to the actual implementation. 309 | * 310 | * @example 311 | * var foo = { 312 | * bar: function() { // do some stuff } 313 | * } 314 | * 315 | * // defining a spy on an existing property: foo.bar 316 | * spyOn(foo, 'bar').andCallThrough(); 317 | */ 318 | jasmine.Spy.prototype.andCallThrough = function() { 319 | this.plan = this.originalValue; 320 | return this; 321 | }; 322 | 323 | /** 324 | * For setting the return value of a spy. 325 | * 326 | * @example 327 | * // defining a spy from scratch: foo() returns 'baz' 328 | * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); 329 | * 330 | * // defining a spy on an existing property: foo.bar() returns 'baz' 331 | * spyOn(foo, 'bar').andReturn('baz'); 332 | * 333 | * @param {Object} value 334 | */ 335 | jasmine.Spy.prototype.andReturn = function(value) { 336 | this.plan = function() { 337 | return value; 338 | }; 339 | return this; 340 | }; 341 | 342 | /** 343 | * For throwing an exception when a spy is called. 344 | * 345 | * @example 346 | * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' 347 | * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); 348 | * 349 | * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' 350 | * spyOn(foo, 'bar').andThrow('baz'); 351 | * 352 | * @param {String} exceptionMsg 353 | */ 354 | jasmine.Spy.prototype.andThrow = function(exceptionMsg) { 355 | this.plan = function() { 356 | throw exceptionMsg; 357 | }; 358 | return this; 359 | }; 360 | 361 | /** 362 | * Calls an alternate implementation when a spy is called. 363 | * 364 | * @example 365 | * var baz = function() { 366 | * // do some stuff, return something 367 | * } 368 | * // defining a spy from scratch: foo() calls the function baz 369 | * var foo = jasmine.createSpy('spy on foo').andCall(baz); 370 | * 371 | * // defining a spy on an existing property: foo.bar() calls an anonymnous function 372 | * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); 373 | * 374 | * @param {Function} fakeFunc 375 | */ 376 | jasmine.Spy.prototype.andCallFake = function(fakeFunc) { 377 | this.plan = fakeFunc; 378 | return this; 379 | }; 380 | 381 | /** 382 | * Resets all of a spy's the tracking variables so that it can be used again. 383 | * 384 | * @example 385 | * spyOn(foo, 'bar'); 386 | * 387 | * foo.bar(); 388 | * 389 | * expect(foo.bar.callCount).toEqual(1); 390 | * 391 | * foo.bar.reset(); 392 | * 393 | * expect(foo.bar.callCount).toEqual(0); 394 | */ 395 | jasmine.Spy.prototype.reset = function() { 396 | this.wasCalled = false; 397 | this.callCount = 0; 398 | this.argsForCall = []; 399 | this.calls = []; 400 | this.mostRecentCall = {}; 401 | }; 402 | 403 | jasmine.createSpy = function(name) { 404 | 405 | var spyObj = function() { 406 | spyObj.wasCalled = true; 407 | spyObj.callCount++; 408 | var args = jasmine.util.argsToArray(arguments); 409 | spyObj.mostRecentCall.object = this; 410 | spyObj.mostRecentCall.args = args; 411 | spyObj.argsForCall.push(args); 412 | spyObj.calls.push({object: this, args: args}); 413 | return spyObj.plan.apply(this, arguments); 414 | }; 415 | 416 | var spy = new jasmine.Spy(name); 417 | 418 | for (var prop in spy) { 419 | spyObj[prop] = spy[prop]; 420 | } 421 | 422 | spyObj.reset(); 423 | 424 | return spyObj; 425 | }; 426 | 427 | /** 428 | * Determines whether an object is a spy. 429 | * 430 | * @param {jasmine.Spy|Object} putativeSpy 431 | * @returns {Boolean} 432 | */ 433 | jasmine.isSpy = function(putativeSpy) { 434 | return putativeSpy && putativeSpy.isSpy; 435 | }; 436 | 437 | /** 438 | * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something 439 | * large in one call. 440 | * 441 | * @param {String} baseName name of spy class 442 | * @param {Array} methodNames array of names of methods to make spies 443 | */ 444 | jasmine.createSpyObj = function(baseName, methodNames) { 445 | if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { 446 | throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); 447 | } 448 | var obj = {}; 449 | for (var i = 0; i < methodNames.length; i++) { 450 | obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); 451 | } 452 | return obj; 453 | }; 454 | 455 | /** 456 | * All parameters are pretty-printed and concatenated together, then written to the current spec's output. 457 | * 458 | * Be careful not to leave calls to jasmine.log in production code. 459 | */ 460 | jasmine.log = function() { 461 | var spec = jasmine.getEnv().currentSpec; 462 | spec.log.apply(spec, arguments); 463 | }; 464 | 465 | /** 466 | * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. 467 | * 468 | * @example 469 | * // spy example 470 | * var foo = { 471 | * not: function(bool) { return !bool; } 472 | * } 473 | * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops 474 | * 475 | * @see jasmine.createSpy 476 | * @param obj 477 | * @param methodName 478 | * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods 479 | */ 480 | var spyOn = function(obj, methodName) { 481 | return jasmine.getEnv().currentSpec.spyOn(obj, methodName); 482 | }; 483 | if (isCommonJS) exports.spyOn = spyOn; 484 | 485 | /** 486 | * Creates a Jasmine spec that will be added to the current suite. 487 | * 488 | * // TODO: pending tests 489 | * 490 | * @example 491 | * it('should be true', function() { 492 | * expect(true).toEqual(true); 493 | * }); 494 | * 495 | * @param {String} desc description of this specification 496 | * @param {Function} func defines the preconditions and expectations of the spec 497 | */ 498 | var it = function(desc, func) { 499 | return jasmine.getEnv().it(desc, func); 500 | }; 501 | if (isCommonJS) exports.it = it; 502 | 503 | /** 504 | * Creates an exclusive Jasmine spec that will be added to the current suite. 505 | * 506 | * If at least one exclusive (iit) spec is registered, only these exclusive specs are run. 507 | * Note, that this behavior works only with the default specFilter. 508 | * Note, that iit has higher priority over ddescribe 509 | * 510 | * @example 511 | * describe('suite', function() { 512 | * iit('should be true', function() { 513 | * // only this spec will be run 514 | * }); 515 | * 516 | * it('should be false', function() { 517 | * // this won't be run 518 | * }); 519 | * }); 520 | * 521 | * @param {String} desc description of this specification 522 | * @param {Function} func defines the preconditions and expectations of the spec 523 | */ 524 | var iit = function(desc, func) { 525 | return jasmine.getEnv().iit(desc, func); 526 | }; 527 | if (isCommonJS) exports.iit = iit; 528 | 529 | /** 530 | * Creates a disabled Jasmine spec. 531 | * 532 | * A convenience method that allows existing specs to be disabled temporarily during development. 533 | * 534 | * @param {String} desc description of this specification 535 | * @param {Function} func defines the preconditions and expectations of the spec 536 | */ 537 | var xit = function(desc, func) { 538 | return jasmine.getEnv().xit(desc, func); 539 | }; 540 | if (isCommonJS) exports.xit = xit; 541 | 542 | /** 543 | * Starts a chain for a Jasmine expectation. 544 | * 545 | * It is passed an Object that is the actual value and should chain to one of the many 546 | * jasmine.Matchers functions. 547 | * 548 | * @param {Object} actual Actual value to test against and expected value 549 | * @return {jasmine.Matchers} 550 | */ 551 | var expect = function(actual) { 552 | return jasmine.getEnv().currentSpec.expect(actual); 553 | }; 554 | if (isCommonJS) exports.expect = expect; 555 | 556 | /** 557 | * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. 558 | * 559 | * @param {Function} func Function that defines part of a jasmine spec. 560 | */ 561 | var runs = function(func) { 562 | jasmine.getEnv().currentSpec.runs(func); 563 | }; 564 | if (isCommonJS) exports.runs = runs; 565 | 566 | /** 567 | * Waits a fixed time period before moving to the next block. 568 | * 569 | * @deprecated Use waitsFor() instead 570 | * @param {Number} timeout milliseconds to wait 571 | */ 572 | var waits = function(timeout) { 573 | jasmine.getEnv().currentSpec.waits(timeout); 574 | }; 575 | if (isCommonJS) exports.waits = waits; 576 | 577 | /** 578 | * Waits for the latchFunction to return true before proceeding to the next block. 579 | * 580 | * @param {Function} latchFunction 581 | * @param {String} optional_timeoutMessage 582 | * @param {Number} optional_timeout 583 | */ 584 | var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 585 | jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); 586 | }; 587 | if (isCommonJS) exports.waitsFor = waitsFor; 588 | 589 | /** 590 | * A function that is called before each spec in a suite. 591 | * 592 | * Used for spec setup, including validating assumptions. 593 | * 594 | * @param {Function} beforeEachFunction 595 | */ 596 | var beforeEach = function(beforeEachFunction) { 597 | jasmine.getEnv().beforeEach(beforeEachFunction); 598 | }; 599 | if (isCommonJS) exports.beforeEach = beforeEach; 600 | 601 | /** 602 | * A function that is called after each spec in a suite. 603 | * 604 | * Used for restoring any state that is hijacked during spec execution. 605 | * 606 | * @param {Function} afterEachFunction 607 | */ 608 | var afterEach = function(afterEachFunction) { 609 | jasmine.getEnv().afterEach(afterEachFunction); 610 | }; 611 | if (isCommonJS) exports.afterEach = afterEach; 612 | 613 | /** 614 | * Defines a suite of specifications. 615 | * 616 | * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared 617 | * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization 618 | * of setup in some tests. 619 | * 620 | * @example 621 | * // TODO: a simple suite 622 | * 623 | * // TODO: a simple suite with a nested describe block 624 | * 625 | * @param {String} description A string, usually the class under test. 626 | * @param {Function} specDefinitions function that defines several specs. 627 | */ 628 | var describe = function(description, specDefinitions) { 629 | return jasmine.getEnv().describe(description, specDefinitions); 630 | }; 631 | if (isCommonJS) exports.describe = describe; 632 | 633 | 634 | /** 635 | * Defines an exclusive suite of specifications. 636 | * 637 | * If at least one exclusive (ddescribe) suite is registered, only these exclusive suites are run. 638 | * Note, that this behavior works only with the default specFilter. 639 | * 640 | * @example 641 | * ddescribe('exclusive suite', function() { 642 | * it('should be true', function() { 643 | * // this spec will be run 644 | * }); 645 | * 646 | * it('should be false', function() { 647 | * // this spec will be run as well 648 | * }); 649 | * }); 650 | * 651 | * describe('normal suite', function() { 652 | * // no spec from this suite will be run 653 | * }); 654 | * 655 | * 656 | * @param {String} description A string, usually the class under test. 657 | * @param {Function} specDefinitions function that defines several specs. 658 | */ 659 | var ddescribe = function(description, specDefinitions) { 660 | return jasmine.getEnv().ddescribe(description, specDefinitions); 661 | }; 662 | if (isCommonJS) exports.ddescribe = ddescribe; 663 | 664 | /** 665 | * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. 666 | * 667 | * @param {String} description A string, usually the class under test. 668 | * @param {Function} specDefinitions function that defines several specs. 669 | */ 670 | var xdescribe = function(description, specDefinitions) { 671 | return jasmine.getEnv().xdescribe(description, specDefinitions); 672 | }; 673 | if (isCommonJS) exports.xdescribe = xdescribe; 674 | 675 | 676 | // Provide the XMLHttpRequest class for IE 5.x-6.x: 677 | jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { 678 | function tryIt(f) { 679 | try { 680 | return f(); 681 | } catch(e) { 682 | } 683 | return null; 684 | } 685 | 686 | var xhr = tryIt(function() { 687 | return new ActiveXObject("Msxml2.XMLHTTP.6.0"); 688 | }) || 689 | tryIt(function() { 690 | return new ActiveXObject("Msxml2.XMLHTTP.3.0"); 691 | }) || 692 | tryIt(function() { 693 | return new ActiveXObject("Msxml2.XMLHTTP"); 694 | }) || 695 | tryIt(function() { 696 | return new ActiveXObject("Microsoft.XMLHTTP"); 697 | }); 698 | 699 | if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); 700 | 701 | return xhr; 702 | } : XMLHttpRequest; 703 | /** 704 | * @namespace 705 | */ 706 | jasmine.util = {}; 707 | 708 | /** 709 | * Declare that a child class inherit it's prototype from the parent class. 710 | * 711 | * @private 712 | * @param {Function} childClass 713 | * @param {Function} parentClass 714 | */ 715 | jasmine.util.inherit = function(childClass, parentClass) { 716 | /** 717 | * @private 718 | */ 719 | var subclass = function() { 720 | }; 721 | subclass.prototype = parentClass.prototype; 722 | childClass.prototype = new subclass(); 723 | }; 724 | 725 | jasmine.util.formatException = function(e) { 726 | var lineNumber; 727 | if (e.line) { 728 | lineNumber = e.line; 729 | } 730 | else if (e.lineNumber) { 731 | lineNumber = e.lineNumber; 732 | } 733 | 734 | var file; 735 | 736 | if (e.sourceURL) { 737 | file = e.sourceURL; 738 | } 739 | else if (e.fileName) { 740 | file = e.fileName; 741 | } 742 | 743 | var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); 744 | 745 | if (file && lineNumber) { 746 | message += ' in ' + file + ' (line ' + lineNumber + ')'; 747 | } 748 | 749 | return message; 750 | }; 751 | 752 | jasmine.util.htmlEscape = function(str) { 753 | if (!str) return str; 754 | return str.replace(/&/g, '&') 755 | .replace(//g, '>'); 757 | }; 758 | 759 | jasmine.util.argsToArray = function(args) { 760 | var arrayOfArgs = []; 761 | for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); 762 | return arrayOfArgs; 763 | }; 764 | 765 | jasmine.util.extend = function(destination, source) { 766 | for (var property in source) destination[property] = source[property]; 767 | return destination; 768 | }; 769 | 770 | /** 771 | * Environment for Jasmine 772 | * 773 | * @constructor 774 | */ 775 | jasmine.Env = function() { 776 | this.currentSpec = null; 777 | this.currentSuite = null; 778 | this.currentRunner_ = new jasmine.Runner(this); 779 | 780 | this.reporter = new jasmine.MultiReporter(); 781 | 782 | this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; 783 | this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; 784 | this.lastUpdate = 0; 785 | this.specFilter = function(spec) { 786 | return this.exclusive_ <= spec.exclusive_; 787 | }; 788 | 789 | this.nextSpecId_ = 0; 790 | this.nextSuiteId_ = 0; 791 | this.equalityTesters_ = []; 792 | 793 | // 0 - normal 794 | // 1 - contains some ddescribe 795 | // 2 - contains some iit 796 | this.exclusive_ = 0; 797 | 798 | // wrap matchers 799 | this.matchersClass = function() { 800 | jasmine.Matchers.apply(this, arguments); 801 | }; 802 | jasmine.util.inherit(this.matchersClass, jasmine.Matchers); 803 | 804 | jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); 805 | }; 806 | 807 | 808 | jasmine.Env.prototype.setTimeout = jasmine.setTimeout; 809 | jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; 810 | jasmine.Env.prototype.setInterval = jasmine.setInterval; 811 | jasmine.Env.prototype.clearInterval = jasmine.clearInterval; 812 | 813 | /** 814 | * @returns an object containing jasmine version build info, if set. 815 | */ 816 | jasmine.Env.prototype.version = function () { 817 | if (jasmine.version_) { 818 | return jasmine.version_; 819 | } else { 820 | throw new Error('Version not set'); 821 | } 822 | }; 823 | 824 | /** 825 | * @returns string containing jasmine version build info, if set. 826 | */ 827 | jasmine.Env.prototype.versionString = function() { 828 | if (!jasmine.version_) { 829 | return "version unknown"; 830 | } 831 | 832 | var version = this.version(); 833 | var versionString = version.major + "." + version.minor + "." + version.build; 834 | if (version.release_candidate) { 835 | versionString += ".rc" + version.release_candidate; 836 | } 837 | versionString += " revision " + version.revision; 838 | return versionString; 839 | }; 840 | 841 | /** 842 | * @returns a sequential integer starting at 0 843 | */ 844 | jasmine.Env.prototype.nextSpecId = function () { 845 | return this.nextSpecId_++; 846 | }; 847 | 848 | /** 849 | * @returns a sequential integer starting at 0 850 | */ 851 | jasmine.Env.prototype.nextSuiteId = function () { 852 | return this.nextSuiteId_++; 853 | }; 854 | 855 | /** 856 | * Register a reporter to receive status updates from Jasmine. 857 | * @param {jasmine.Reporter} reporter An object which will receive status updates. 858 | */ 859 | jasmine.Env.prototype.addReporter = function(reporter) { 860 | this.reporter.addReporter(reporter); 861 | }; 862 | 863 | jasmine.Env.prototype.execute = function() { 864 | this.currentRunner_.execute(); 865 | }; 866 | 867 | jasmine.Env.prototype.describe = function(description, specDefinitions) { 868 | var suite = new jasmine.Suite(this, description, null, this.currentSuite); 869 | return this.describe_(suite, specDefinitions); 870 | }; 871 | 872 | jasmine.Env.prototype.describe_ = function(suite, specDefinitions) { 873 | var parentSuite = this.currentSuite; 874 | if (parentSuite) { 875 | parentSuite.add(suite); 876 | } else { 877 | this.currentRunner_.add(suite); 878 | } 879 | 880 | this.currentSuite = suite; 881 | 882 | var declarationError = null; 883 | try { 884 | specDefinitions.call(suite); 885 | } catch(e) { 886 | declarationError = e; 887 | } 888 | 889 | if (declarationError) { 890 | this.it("encountered a declaration exception", function() { 891 | throw declarationError; 892 | }); 893 | } 894 | 895 | this.currentSuite = parentSuite; 896 | 897 | return suite; 898 | }; 899 | 900 | jasmine.Env.prototype.ddescribe = function(description, specDefinitions) { 901 | var suite = new jasmine.Suite(this, description, null, this.currentSuite); 902 | suite.exclusive_ = 1; 903 | this.exclusive_ = Math.max(this.exclusive_, 1); 904 | 905 | return this.describe_(suite, specDefinitions); 906 | }; 907 | 908 | jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { 909 | if (this.currentSuite) { 910 | this.currentSuite.beforeEach(beforeEachFunction); 911 | } else { 912 | this.currentRunner_.beforeEach(beforeEachFunction); 913 | } 914 | }; 915 | 916 | jasmine.Env.prototype.currentRunner = function () { 917 | return this.currentRunner_; 918 | }; 919 | 920 | jasmine.Env.prototype.afterEach = function(afterEachFunction) { 921 | if (this.currentSuite) { 922 | this.currentSuite.afterEach(afterEachFunction); 923 | } else { 924 | this.currentRunner_.afterEach(afterEachFunction); 925 | } 926 | 927 | }; 928 | 929 | jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { 930 | return { 931 | execute: function() { 932 | } 933 | }; 934 | }; 935 | 936 | jasmine.Env.prototype.it = function(description, func) { 937 | var spec = new jasmine.Spec(this, this.currentSuite, description); 938 | this.currentSuite.add(spec); 939 | this.currentSpec = spec; 940 | 941 | if (func) { 942 | spec.runs(func); 943 | } 944 | 945 | return spec; 946 | }; 947 | 948 | jasmine.Env.prototype.iit = function(description, func) { 949 | var spec = this.it(description, func); 950 | spec.exclusive_ = 2; 951 | this.exclusive_ = 2; 952 | 953 | return spec; 954 | }; 955 | 956 | jasmine.Env.prototype.xit = function(desc, func) { 957 | return { 958 | id: this.nextSpecId(), 959 | runs: function() { 960 | } 961 | }; 962 | }; 963 | 964 | jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { 965 | if (a.source != b.source) 966 | mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); 967 | 968 | if (a.ignoreCase != b.ignoreCase) 969 | mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); 970 | 971 | if (a.global != b.global) 972 | mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); 973 | 974 | if (a.multiline != b.multiline) 975 | mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); 976 | 977 | if (a.sticky != b.sticky) 978 | mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); 979 | 980 | return (mismatchValues.length === 0); 981 | }; 982 | 983 | jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { 984 | if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { 985 | return true; 986 | } 987 | 988 | a.__Jasmine_been_here_before__ = b; 989 | b.__Jasmine_been_here_before__ = a; 990 | 991 | var hasKey = function(obj, keyName) { 992 | return obj !== null && obj[keyName] !== jasmine.undefined; 993 | }; 994 | 995 | for (var property in b) { 996 | if (!hasKey(a, property) && hasKey(b, property)) { 997 | mismatchKeys.push("expected has key '" + property + "', but missing from actual."); 998 | } 999 | } 1000 | for (property in a) { 1001 | if (!hasKey(b, property) && hasKey(a, property)) { 1002 | mismatchKeys.push("expected missing key '" + property + "', but present in actual."); 1003 | } 1004 | } 1005 | for (property in b) { 1006 | if (property == '__Jasmine_been_here_before__') continue; 1007 | if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { 1008 | mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); 1009 | } 1010 | } 1011 | 1012 | if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { 1013 | mismatchValues.push("arrays were not the same length"); 1014 | } 1015 | 1016 | delete a.__Jasmine_been_here_before__; 1017 | delete b.__Jasmine_been_here_before__; 1018 | return (mismatchKeys.length === 0 && mismatchValues.length === 0); 1019 | }; 1020 | 1021 | jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { 1022 | mismatchKeys = mismatchKeys || []; 1023 | mismatchValues = mismatchValues || []; 1024 | 1025 | for (var i = 0; i < this.equalityTesters_.length; i++) { 1026 | var equalityTester = this.equalityTesters_[i]; 1027 | var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); 1028 | if (result !== jasmine.undefined) return result; 1029 | } 1030 | 1031 | if (a === b) return true; 1032 | 1033 | if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { 1034 | return (a == jasmine.undefined && b == jasmine.undefined); 1035 | } 1036 | 1037 | if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { 1038 | return a === b; 1039 | } 1040 | 1041 | if (a instanceof Date && b instanceof Date) { 1042 | return a.getTime() == b.getTime(); 1043 | } 1044 | 1045 | if (a.jasmineMatches) { 1046 | return a.jasmineMatches(b); 1047 | } 1048 | 1049 | if (b.jasmineMatches) { 1050 | return b.jasmineMatches(a); 1051 | } 1052 | 1053 | if (a instanceof jasmine.Matchers.ObjectContaining) { 1054 | return a.matches(b); 1055 | } 1056 | 1057 | if (b instanceof jasmine.Matchers.ObjectContaining) { 1058 | return b.matches(a); 1059 | } 1060 | 1061 | if (jasmine.isString_(a) && jasmine.isString_(b)) { 1062 | return (a == b); 1063 | } 1064 | 1065 | if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { 1066 | return (a == b); 1067 | } 1068 | 1069 | if (a instanceof RegExp && b instanceof RegExp) { 1070 | return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); 1071 | } 1072 | 1073 | if (typeof a === "object" && typeof b === "object") { 1074 | return this.compareObjects_(a, b, mismatchKeys, mismatchValues); 1075 | } 1076 | 1077 | //Straight check 1078 | return (a === b); 1079 | }; 1080 | 1081 | jasmine.Env.prototype.contains_ = function(haystack, needle) { 1082 | if (jasmine.isArray_(haystack)) { 1083 | for (var i = 0; i < haystack.length; i++) { 1084 | if (this.equals_(haystack[i], needle)) return true; 1085 | } 1086 | return false; 1087 | } 1088 | return haystack.indexOf(needle) >= 0; 1089 | }; 1090 | 1091 | jasmine.Env.prototype.addEqualityTester = function(equalityTester) { 1092 | this.equalityTesters_.push(equalityTester); 1093 | }; 1094 | /** No-op base class for Jasmine reporters. 1095 | * 1096 | * @constructor 1097 | */ 1098 | jasmine.Reporter = function() { 1099 | }; 1100 | 1101 | //noinspection JSUnusedLocalSymbols 1102 | jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { 1103 | }; 1104 | 1105 | //noinspection JSUnusedLocalSymbols 1106 | jasmine.Reporter.prototype.reportRunnerResults = function(runner) { 1107 | }; 1108 | 1109 | //noinspection JSUnusedLocalSymbols 1110 | jasmine.Reporter.prototype.reportSuiteResults = function(suite) { 1111 | }; 1112 | 1113 | //noinspection JSUnusedLocalSymbols 1114 | jasmine.Reporter.prototype.reportSpecStarting = function(spec) { 1115 | }; 1116 | 1117 | //noinspection JSUnusedLocalSymbols 1118 | jasmine.Reporter.prototype.reportSpecResults = function(spec) { 1119 | }; 1120 | 1121 | //noinspection JSUnusedLocalSymbols 1122 | jasmine.Reporter.prototype.log = function(str) { 1123 | }; 1124 | 1125 | /** 1126 | * Blocks are functions with executable code that make up a spec. 1127 | * 1128 | * @constructor 1129 | * @param {jasmine.Env} env 1130 | * @param {Function} func 1131 | * @param {jasmine.Spec} spec 1132 | */ 1133 | jasmine.Block = function(env, func, spec) { 1134 | this.env = env; 1135 | this.func = func; 1136 | this.spec = spec; 1137 | }; 1138 | 1139 | jasmine.Block.prototype.execute = function(onComplete) { 1140 | if (!jasmine.CATCH_EXCEPTIONS) { 1141 | this.func.apply(this.spec); 1142 | } 1143 | else { 1144 | try { 1145 | this.func.apply(this.spec); 1146 | } catch (e) { 1147 | this.spec.fail(e); 1148 | } 1149 | } 1150 | onComplete(); 1151 | }; 1152 | /** JavaScript API reporter. 1153 | * 1154 | * @constructor 1155 | */ 1156 | jasmine.JsApiReporter = function() { 1157 | this.started = false; 1158 | this.finished = false; 1159 | this.suites_ = []; 1160 | this.results_ = {}; 1161 | }; 1162 | 1163 | jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { 1164 | this.started = true; 1165 | var suites = runner.topLevelSuites(); 1166 | for (var i = 0; i < suites.length; i++) { 1167 | var suite = suites[i]; 1168 | this.suites_.push(this.summarize_(suite)); 1169 | } 1170 | }; 1171 | 1172 | jasmine.JsApiReporter.prototype.suites = function() { 1173 | return this.suites_; 1174 | }; 1175 | 1176 | jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { 1177 | var isSuite = suiteOrSpec instanceof jasmine.Suite; 1178 | var summary = { 1179 | id: suiteOrSpec.id, 1180 | name: suiteOrSpec.description, 1181 | type: isSuite ? 'suite' : 'spec', 1182 | children: [] 1183 | }; 1184 | 1185 | if (isSuite) { 1186 | var children = suiteOrSpec.children(); 1187 | for (var i = 0; i < children.length; i++) { 1188 | summary.children.push(this.summarize_(children[i])); 1189 | } 1190 | } 1191 | return summary; 1192 | }; 1193 | 1194 | jasmine.JsApiReporter.prototype.results = function() { 1195 | return this.results_; 1196 | }; 1197 | 1198 | jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { 1199 | return this.results_[specId]; 1200 | }; 1201 | 1202 | //noinspection JSUnusedLocalSymbols 1203 | jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { 1204 | this.finished = true; 1205 | }; 1206 | 1207 | //noinspection JSUnusedLocalSymbols 1208 | jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { 1209 | }; 1210 | 1211 | //noinspection JSUnusedLocalSymbols 1212 | jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { 1213 | this.results_[spec.id] = { 1214 | messages: spec.results().getItems(), 1215 | result: spec.results().failedCount > 0 ? "failed" : "passed" 1216 | }; 1217 | }; 1218 | 1219 | //noinspection JSUnusedLocalSymbols 1220 | jasmine.JsApiReporter.prototype.log = function(str) { 1221 | }; 1222 | 1223 | jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ 1224 | var results = {}; 1225 | for (var i = 0; i < specIds.length; i++) { 1226 | var specId = specIds[i]; 1227 | results[specId] = this.summarizeResult_(this.results_[specId]); 1228 | } 1229 | return results; 1230 | }; 1231 | 1232 | jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ 1233 | var summaryMessages = []; 1234 | var messagesLength = result.messages.length; 1235 | for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { 1236 | var resultMessage = result.messages[messageIndex]; 1237 | summaryMessages.push({ 1238 | text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, 1239 | passed: resultMessage.passed ? resultMessage.passed() : true, 1240 | type: resultMessage.type, 1241 | message: resultMessage.message, 1242 | trace: { 1243 | stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined 1244 | } 1245 | }); 1246 | } 1247 | 1248 | return { 1249 | result : result.result, 1250 | messages : summaryMessages 1251 | }; 1252 | }; 1253 | 1254 | /** 1255 | * @constructor 1256 | * @param {jasmine.Env} env 1257 | * @param actual 1258 | * @param {jasmine.Spec} spec 1259 | */ 1260 | jasmine.Matchers = function(env, actual, spec, opt_isNot) { 1261 | this.env = env; 1262 | this.actual = actual; 1263 | this.spec = spec; 1264 | this.isNot = opt_isNot || false; 1265 | this.reportWasCalled_ = false; 1266 | }; 1267 | 1268 | // todo: @deprecated as of Jasmine 0.11, remove soon [xw] 1269 | jasmine.Matchers.pp = function(str) { 1270 | throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); 1271 | }; 1272 | 1273 | // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] 1274 | jasmine.Matchers.prototype.report = function(result, failing_message, details) { 1275 | throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); 1276 | }; 1277 | 1278 | jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { 1279 | for (var methodName in prototype) { 1280 | if (methodName == 'report') continue; 1281 | var orig = prototype[methodName]; 1282 | matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); 1283 | } 1284 | }; 1285 | 1286 | jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { 1287 | return function() { 1288 | var matcherArgs = jasmine.util.argsToArray(arguments); 1289 | var result = matcherFunction.apply(this, arguments); 1290 | 1291 | if (this.isNot) { 1292 | result = !result; 1293 | } 1294 | 1295 | if (this.reportWasCalled_) return result; 1296 | 1297 | var message; 1298 | if (!result) { 1299 | if (this.message) { 1300 | message = this.message.apply(this, arguments); 1301 | if (jasmine.isArray_(message)) { 1302 | message = message[this.isNot ? 1 : 0]; 1303 | } 1304 | } else { 1305 | var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); 1306 | message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; 1307 | if (matcherArgs.length > 0) { 1308 | for (var i = 0; i < matcherArgs.length; i++) { 1309 | if (i > 0) message += ","; 1310 | message += " " + jasmine.pp(matcherArgs[i]); 1311 | } 1312 | } 1313 | message += "."; 1314 | } 1315 | } 1316 | var expectationResult = new jasmine.ExpectationResult({ 1317 | matcherName: matcherName, 1318 | passed: result, 1319 | expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], 1320 | actual: this.actual, 1321 | message: message 1322 | }); 1323 | this.spec.addMatcherResult(expectationResult); 1324 | return jasmine.undefined; 1325 | }; 1326 | }; 1327 | 1328 | 1329 | 1330 | 1331 | /** 1332 | * toBe: compares the actual to the expected using === 1333 | * @param expected 1334 | */ 1335 | jasmine.Matchers.prototype.toBe = function(expected) { 1336 | return this.actual === expected; 1337 | }; 1338 | 1339 | /** 1340 | * toNotBe: compares the actual to the expected using !== 1341 | * @param expected 1342 | * @deprecated as of 1.0. Use not.toBe() instead. 1343 | */ 1344 | jasmine.Matchers.prototype.toNotBe = function(expected) { 1345 | return this.actual !== expected; 1346 | }; 1347 | 1348 | /** 1349 | * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. 1350 | * 1351 | * @param expected 1352 | */ 1353 | jasmine.Matchers.prototype.toEqual = function(expected) { 1354 | return this.env.equals_(this.actual, expected); 1355 | }; 1356 | 1357 | /** 1358 | * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual 1359 | * @param expected 1360 | * @deprecated as of 1.0. Use not.toEqual() instead. 1361 | */ 1362 | jasmine.Matchers.prototype.toNotEqual = function(expected) { 1363 | return !this.env.equals_(this.actual, expected); 1364 | }; 1365 | 1366 | /** 1367 | * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes 1368 | * a pattern or a String. 1369 | * 1370 | * @param expected 1371 | */ 1372 | jasmine.Matchers.prototype.toMatch = function(expected) { 1373 | return new RegExp(expected).test(this.actual); 1374 | }; 1375 | 1376 | /** 1377 | * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch 1378 | * @param expected 1379 | * @deprecated as of 1.0. Use not.toMatch() instead. 1380 | */ 1381 | jasmine.Matchers.prototype.toNotMatch = function(expected) { 1382 | return !(new RegExp(expected).test(this.actual)); 1383 | }; 1384 | 1385 | /** 1386 | * Matcher that compares the actual to jasmine.undefined. 1387 | */ 1388 | jasmine.Matchers.prototype.toBeDefined = function() { 1389 | return (this.actual !== jasmine.undefined); 1390 | }; 1391 | 1392 | /** 1393 | * Matcher that compares the actual to jasmine.undefined. 1394 | */ 1395 | jasmine.Matchers.prototype.toBeUndefined = function() { 1396 | return (this.actual === jasmine.undefined); 1397 | }; 1398 | 1399 | /** 1400 | * Matcher that compares the actual to null. 1401 | */ 1402 | jasmine.Matchers.prototype.toBeNull = function() { 1403 | return (this.actual === null); 1404 | }; 1405 | 1406 | /** 1407 | * Matcher that compares the actual to NaN. 1408 | */ 1409 | jasmine.Matchers.prototype.toBeNaN = function() { 1410 | this.message = function() { 1411 | return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; 1412 | }; 1413 | 1414 | return (this.actual !== this.actual); 1415 | }; 1416 | 1417 | /** 1418 | * Matcher that boolean not-nots the actual. 1419 | */ 1420 | jasmine.Matchers.prototype.toBeTruthy = function() { 1421 | return !!this.actual; 1422 | }; 1423 | 1424 | 1425 | /** 1426 | * Matcher that boolean nots the actual. 1427 | */ 1428 | jasmine.Matchers.prototype.toBeFalsy = function() { 1429 | return !this.actual; 1430 | }; 1431 | 1432 | 1433 | /** 1434 | * Matcher that checks to see if the actual, a Jasmine spy, was called. 1435 | */ 1436 | jasmine.Matchers.prototype.toHaveBeenCalled = function() { 1437 | if (arguments.length > 0) { 1438 | throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); 1439 | } 1440 | 1441 | if (!jasmine.isSpy(this.actual)) { 1442 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1443 | } 1444 | 1445 | this.message = function() { 1446 | return [ 1447 | "Expected spy " + this.actual.identity + " to have been called.", 1448 | "Expected spy " + this.actual.identity + " not to have been called." 1449 | ]; 1450 | }; 1451 | 1452 | return this.actual.wasCalled; 1453 | }; 1454 | 1455 | /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ 1456 | jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; 1457 | 1458 | /** 1459 | * Matcher that checks to see if the actual, a Jasmine spy, was not called. 1460 | * 1461 | * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead 1462 | */ 1463 | jasmine.Matchers.prototype.wasNotCalled = function() { 1464 | if (arguments.length > 0) { 1465 | throw new Error('wasNotCalled does not take arguments'); 1466 | } 1467 | 1468 | if (!jasmine.isSpy(this.actual)) { 1469 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1470 | } 1471 | 1472 | this.message = function() { 1473 | return [ 1474 | "Expected spy " + this.actual.identity + " to not have been called.", 1475 | "Expected spy " + this.actual.identity + " to have been called." 1476 | ]; 1477 | }; 1478 | 1479 | return !this.actual.wasCalled; 1480 | }; 1481 | 1482 | /** 1483 | * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. 1484 | * 1485 | * @example 1486 | * 1487 | */ 1488 | jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { 1489 | var expectedArgs = jasmine.util.argsToArray(arguments); 1490 | if (!jasmine.isSpy(this.actual)) { 1491 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1492 | } 1493 | this.message = function() { 1494 | var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."; 1495 | var positiveMessage = ""; 1496 | if (this.actual.callCount === 0) { 1497 | positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; 1498 | } else { 1499 | positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '') 1500 | } 1501 | return [positiveMessage, invertedMessage]; 1502 | }; 1503 | 1504 | return this.env.contains_(this.actual.argsForCall, expectedArgs); 1505 | }; 1506 | 1507 | /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ 1508 | jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; 1509 | 1510 | /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ 1511 | jasmine.Matchers.prototype.wasNotCalledWith = function() { 1512 | var expectedArgs = jasmine.util.argsToArray(arguments); 1513 | if (!jasmine.isSpy(this.actual)) { 1514 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1515 | } 1516 | 1517 | this.message = function() { 1518 | return [ 1519 | "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", 1520 | "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" 1521 | ]; 1522 | }; 1523 | 1524 | return !this.env.contains_(this.actual.argsForCall, expectedArgs); 1525 | }; 1526 | 1527 | /** 1528 | * Matcher that checks that the expected item is an element in the actual Array. 1529 | * 1530 | * @param {Object} expected 1531 | */ 1532 | jasmine.Matchers.prototype.toContain = function(expected) { 1533 | return this.env.contains_(this.actual, expected); 1534 | }; 1535 | 1536 | /** 1537 | * Matcher that checks that the expected item is NOT an element in the actual Array. 1538 | * 1539 | * @param {Object} expected 1540 | * @deprecated as of 1.0. Use not.toContain() instead. 1541 | */ 1542 | jasmine.Matchers.prototype.toNotContain = function(expected) { 1543 | return !this.env.contains_(this.actual, expected); 1544 | }; 1545 | 1546 | jasmine.Matchers.prototype.toBeLessThan = function(expected) { 1547 | return this.actual < expected; 1548 | }; 1549 | 1550 | jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { 1551 | return this.actual > expected; 1552 | }; 1553 | 1554 | /** 1555 | * Matcher that checks that the expected item is equal to the actual item 1556 | * up to a given level of decimal precision (default 2). 1557 | * 1558 | * @param {Number} expected 1559 | * @param {Number} precision, as number of decimal places 1560 | */ 1561 | jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { 1562 | if (!(precision === 0)) { 1563 | precision = precision || 2; 1564 | } 1565 | return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2); 1566 | }; 1567 | 1568 | /** 1569 | * Matcher that checks that the expected exception was thrown by the actual. 1570 | * 1571 | * @param {String} [expected] 1572 | */ 1573 | jasmine.Matchers.prototype.toThrow = function(expected) { 1574 | var result = false; 1575 | var exception; 1576 | if (typeof this.actual != 'function') { 1577 | throw new Error('Actual is not a function'); 1578 | } 1579 | try { 1580 | this.actual(); 1581 | } catch (e) { 1582 | exception = e; 1583 | } 1584 | if (exception) { 1585 | result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); 1586 | } 1587 | 1588 | var not = this.isNot ? "not " : ""; 1589 | 1590 | this.message = function() { 1591 | if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { 1592 | return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); 1593 | } else { 1594 | return "Expected function to throw an exception."; 1595 | } 1596 | }; 1597 | 1598 | return result; 1599 | }; 1600 | 1601 | jasmine.Matchers.Any = function(expectedClass) { 1602 | this.expectedClass = expectedClass; 1603 | }; 1604 | 1605 | jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { 1606 | if (this.expectedClass == String) { 1607 | return typeof other == 'string' || other instanceof String; 1608 | } 1609 | 1610 | if (this.expectedClass == Number) { 1611 | return typeof other == 'number' || other instanceof Number; 1612 | } 1613 | 1614 | if (this.expectedClass == Function) { 1615 | return typeof other == 'function' || other instanceof Function; 1616 | } 1617 | 1618 | if (this.expectedClass == Object) { 1619 | return typeof other == 'object'; 1620 | } 1621 | 1622 | return other instanceof this.expectedClass; 1623 | }; 1624 | 1625 | jasmine.Matchers.Any.prototype.jasmineToString = function() { 1626 | return ''; 1627 | }; 1628 | 1629 | jasmine.Matchers.ObjectContaining = function (sample) { 1630 | this.sample = sample; 1631 | }; 1632 | 1633 | jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { 1634 | mismatchKeys = mismatchKeys || []; 1635 | mismatchValues = mismatchValues || []; 1636 | 1637 | var env = jasmine.getEnv(); 1638 | 1639 | var hasKey = function(obj, keyName) { 1640 | return obj != null && obj[keyName] !== jasmine.undefined; 1641 | }; 1642 | 1643 | for (var property in this.sample) { 1644 | if (!hasKey(other, property) && hasKey(this.sample, property)) { 1645 | mismatchKeys.push("expected has key '" + property + "', but missing from actual."); 1646 | } 1647 | else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { 1648 | mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); 1649 | } 1650 | } 1651 | 1652 | return (mismatchKeys.length === 0 && mismatchValues.length === 0); 1653 | }; 1654 | 1655 | jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { 1656 | return ""; 1657 | }; 1658 | // Mock setTimeout, clearTimeout 1659 | // Contributed by Pivotal Computer Systems, www.pivotalsf.com 1660 | 1661 | jasmine.FakeTimer = function() { 1662 | this.reset(); 1663 | 1664 | var self = this; 1665 | self.setTimeout = function(funcToCall, millis) { 1666 | self.timeoutsMade++; 1667 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); 1668 | return self.timeoutsMade; 1669 | }; 1670 | 1671 | self.setInterval = function(funcToCall, millis) { 1672 | self.timeoutsMade++; 1673 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); 1674 | return self.timeoutsMade; 1675 | }; 1676 | 1677 | self.clearTimeout = function(timeoutKey) { 1678 | self.scheduledFunctions[timeoutKey] = jasmine.undefined; 1679 | }; 1680 | 1681 | self.clearInterval = function(timeoutKey) { 1682 | self.scheduledFunctions[timeoutKey] = jasmine.undefined; 1683 | }; 1684 | 1685 | }; 1686 | 1687 | jasmine.FakeTimer.prototype.reset = function() { 1688 | this.timeoutsMade = 0; 1689 | this.scheduledFunctions = {}; 1690 | this.nowMillis = 0; 1691 | }; 1692 | 1693 | jasmine.FakeTimer.prototype.tick = function(millis) { 1694 | var oldMillis = this.nowMillis; 1695 | var newMillis = oldMillis + millis; 1696 | this.runFunctionsWithinRange(oldMillis, newMillis); 1697 | this.nowMillis = newMillis; 1698 | }; 1699 | 1700 | jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { 1701 | var scheduledFunc; 1702 | var funcsToRun = []; 1703 | for (var timeoutKey in this.scheduledFunctions) { 1704 | scheduledFunc = this.scheduledFunctions[timeoutKey]; 1705 | if (scheduledFunc != jasmine.undefined && 1706 | scheduledFunc.runAtMillis >= oldMillis && 1707 | scheduledFunc.runAtMillis <= nowMillis) { 1708 | funcsToRun.push(scheduledFunc); 1709 | this.scheduledFunctions[timeoutKey] = jasmine.undefined; 1710 | } 1711 | } 1712 | 1713 | if (funcsToRun.length > 0) { 1714 | funcsToRun.sort(function(a, b) { 1715 | return a.runAtMillis - b.runAtMillis; 1716 | }); 1717 | for (var i = 0; i < funcsToRun.length; ++i) { 1718 | try { 1719 | var funcToRun = funcsToRun[i]; 1720 | this.nowMillis = funcToRun.runAtMillis; 1721 | funcToRun.funcToCall(); 1722 | if (funcToRun.recurring) { 1723 | this.scheduleFunction(funcToRun.timeoutKey, 1724 | funcToRun.funcToCall, 1725 | funcToRun.millis, 1726 | true); 1727 | } 1728 | } catch(e) { 1729 | } 1730 | } 1731 | this.runFunctionsWithinRange(oldMillis, nowMillis); 1732 | } 1733 | }; 1734 | 1735 | jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { 1736 | this.scheduledFunctions[timeoutKey] = { 1737 | runAtMillis: this.nowMillis + millis, 1738 | funcToCall: funcToCall, 1739 | recurring: recurring, 1740 | timeoutKey: timeoutKey, 1741 | millis: millis 1742 | }; 1743 | }; 1744 | 1745 | /** 1746 | * @namespace 1747 | */ 1748 | jasmine.Clock = { 1749 | defaultFakeTimer: new jasmine.FakeTimer(), 1750 | 1751 | reset: function() { 1752 | jasmine.Clock.assertInstalled(); 1753 | jasmine.Clock.defaultFakeTimer.reset(); 1754 | }, 1755 | 1756 | tick: function(millis) { 1757 | jasmine.Clock.assertInstalled(); 1758 | jasmine.Clock.defaultFakeTimer.tick(millis); 1759 | }, 1760 | 1761 | runFunctionsWithinRange: function(oldMillis, nowMillis) { 1762 | jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); 1763 | }, 1764 | 1765 | scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { 1766 | jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); 1767 | }, 1768 | 1769 | useMock: function() { 1770 | if (!jasmine.Clock.isInstalled()) { 1771 | var spec = jasmine.getEnv().currentSpec; 1772 | spec.after(jasmine.Clock.uninstallMock); 1773 | 1774 | jasmine.Clock.installMock(); 1775 | } 1776 | }, 1777 | 1778 | installMock: function() { 1779 | jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; 1780 | }, 1781 | 1782 | uninstallMock: function() { 1783 | jasmine.Clock.assertInstalled(); 1784 | jasmine.Clock.installed = jasmine.Clock.real; 1785 | }, 1786 | 1787 | real: { 1788 | setTimeout: jasmine.getGlobal().setTimeout, 1789 | clearTimeout: jasmine.getGlobal().clearTimeout, 1790 | setInterval: jasmine.getGlobal().setInterval, 1791 | clearInterval: jasmine.getGlobal().clearInterval 1792 | }, 1793 | 1794 | assertInstalled: function() { 1795 | if (!jasmine.Clock.isInstalled()) { 1796 | throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); 1797 | } 1798 | }, 1799 | 1800 | isInstalled: function() { 1801 | return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; 1802 | }, 1803 | 1804 | installed: null 1805 | }; 1806 | jasmine.Clock.installed = jasmine.Clock.real; 1807 | 1808 | //else for IE support 1809 | jasmine.getGlobal().setTimeout = function(funcToCall, millis) { 1810 | if (jasmine.Clock.installed.setTimeout.apply) { 1811 | return jasmine.Clock.installed.setTimeout.apply(this, arguments); 1812 | } else { 1813 | return jasmine.Clock.installed.setTimeout(funcToCall, millis); 1814 | } 1815 | }; 1816 | 1817 | jasmine.getGlobal().setInterval = function(funcToCall, millis) { 1818 | if (jasmine.Clock.installed.setInterval.apply) { 1819 | return jasmine.Clock.installed.setInterval.apply(this, arguments); 1820 | } else { 1821 | return jasmine.Clock.installed.setInterval(funcToCall, millis); 1822 | } 1823 | }; 1824 | 1825 | jasmine.getGlobal().clearTimeout = function(timeoutKey) { 1826 | if (jasmine.Clock.installed.clearTimeout.apply) { 1827 | return jasmine.Clock.installed.clearTimeout.apply(this, arguments); 1828 | } else { 1829 | return jasmine.Clock.installed.clearTimeout(timeoutKey); 1830 | } 1831 | }; 1832 | 1833 | jasmine.getGlobal().clearInterval = function(timeoutKey) { 1834 | if (jasmine.Clock.installed.clearTimeout.apply) { 1835 | return jasmine.Clock.installed.clearInterval.apply(this, arguments); 1836 | } else { 1837 | return jasmine.Clock.installed.clearInterval(timeoutKey); 1838 | } 1839 | }; 1840 | 1841 | /** 1842 | * @constructor 1843 | */ 1844 | jasmine.MultiReporter = function() { 1845 | this.subReporters_ = []; 1846 | }; 1847 | jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); 1848 | 1849 | jasmine.MultiReporter.prototype.addReporter = function(reporter) { 1850 | this.subReporters_.push(reporter); 1851 | }; 1852 | 1853 | (function() { 1854 | var functionNames = [ 1855 | "reportRunnerStarting", 1856 | "reportRunnerResults", 1857 | "reportSuiteResults", 1858 | "reportSpecStarting", 1859 | "reportSpecResults", 1860 | "log" 1861 | ]; 1862 | for (var i = 0; i < functionNames.length; i++) { 1863 | var functionName = functionNames[i]; 1864 | jasmine.MultiReporter.prototype[functionName] = (function(functionName) { 1865 | return function() { 1866 | for (var j = 0; j < this.subReporters_.length; j++) { 1867 | var subReporter = this.subReporters_[j]; 1868 | if (subReporter[functionName]) { 1869 | subReporter[functionName].apply(subReporter, arguments); 1870 | } 1871 | } 1872 | }; 1873 | })(functionName); 1874 | } 1875 | })(); 1876 | /** 1877 | * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults 1878 | * 1879 | * @constructor 1880 | */ 1881 | jasmine.NestedResults = function() { 1882 | /** 1883 | * The total count of results 1884 | */ 1885 | this.totalCount = 0; 1886 | /** 1887 | * Number of passed results 1888 | */ 1889 | this.passedCount = 0; 1890 | /** 1891 | * Number of failed results 1892 | */ 1893 | this.failedCount = 0; 1894 | /** 1895 | * Was this suite/spec skipped? 1896 | */ 1897 | this.skipped = false; 1898 | /** 1899 | * @ignore 1900 | */ 1901 | this.items_ = []; 1902 | }; 1903 | 1904 | /** 1905 | * Roll up the result counts. 1906 | * 1907 | * @param result 1908 | */ 1909 | jasmine.NestedResults.prototype.rollupCounts = function(result) { 1910 | this.totalCount += result.totalCount; 1911 | this.passedCount += result.passedCount; 1912 | this.failedCount += result.failedCount; 1913 | }; 1914 | 1915 | /** 1916 | * Adds a log message. 1917 | * @param values Array of message parts which will be concatenated later. 1918 | */ 1919 | jasmine.NestedResults.prototype.log = function(values) { 1920 | this.items_.push(new jasmine.MessageResult(values)); 1921 | }; 1922 | 1923 | /** 1924 | * Getter for the results: message & results. 1925 | */ 1926 | jasmine.NestedResults.prototype.getItems = function() { 1927 | return this.items_; 1928 | }; 1929 | 1930 | /** 1931 | * Adds a result, tracking counts (total, passed, & failed) 1932 | * @param {jasmine.ExpectationResult|jasmine.NestedResults} result 1933 | */ 1934 | jasmine.NestedResults.prototype.addResult = function(result) { 1935 | if (result.type != 'log') { 1936 | if (result.items_) { 1937 | this.rollupCounts(result); 1938 | } else { 1939 | this.totalCount++; 1940 | if (result.passed()) { 1941 | this.passedCount++; 1942 | } else { 1943 | this.failedCount++; 1944 | } 1945 | } 1946 | } 1947 | this.items_.push(result); 1948 | }; 1949 | 1950 | /** 1951 | * @returns {Boolean} True if everything below passed 1952 | */ 1953 | jasmine.NestedResults.prototype.passed = function() { 1954 | return this.passedCount === this.totalCount; 1955 | }; 1956 | /** 1957 | * Base class for pretty printing for expectation results. 1958 | */ 1959 | jasmine.PrettyPrinter = function() { 1960 | this.ppNestLevel_ = 0; 1961 | }; 1962 | 1963 | /** 1964 | * Formats a value in a nice, human-readable string. 1965 | * 1966 | * @param value 1967 | */ 1968 | jasmine.PrettyPrinter.prototype.format = function(value) { 1969 | this.ppNestLevel_++; 1970 | try { 1971 | if (value === jasmine.undefined) { 1972 | this.emitScalar('undefined'); 1973 | } else if (value === null) { 1974 | this.emitScalar('null'); 1975 | } else if (value === jasmine.getGlobal()) { 1976 | this.emitScalar(''); 1977 | } else if (value.jasmineToString) { 1978 | this.emitScalar(value.jasmineToString()); 1979 | } else if (typeof value === 'string') { 1980 | this.emitString(value); 1981 | } else if (jasmine.isSpy(value)) { 1982 | this.emitScalar("spy on " + value.identity); 1983 | } else if (value instanceof RegExp) { 1984 | this.emitScalar(value.toString()); 1985 | } else if (typeof value === 'function') { 1986 | this.emitScalar('Function'); 1987 | } else if (typeof value.nodeType === 'number') { 1988 | this.emitScalar('HTMLNode'); 1989 | } else if (value instanceof Date) { 1990 | this.emitScalar('Date(' + value + ')'); 1991 | } else if (value.__Jasmine_been_here_before__) { 1992 | this.emitScalar(''); 1993 | } else if (jasmine.isArray_(value) || typeof value == 'object') { 1994 | value.__Jasmine_been_here_before__ = true; 1995 | if (jasmine.isArray_(value)) { 1996 | this.emitArray(value); 1997 | } else { 1998 | this.emitObject(value); 1999 | } 2000 | delete value.__Jasmine_been_here_before__; 2001 | } else { 2002 | this.emitScalar(value.toString()); 2003 | } 2004 | } finally { 2005 | this.ppNestLevel_--; 2006 | } 2007 | }; 2008 | 2009 | jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { 2010 | for (var property in obj) { 2011 | if (!obj.hasOwnProperty(property)) continue; 2012 | if (property == '__Jasmine_been_here_before__') continue; 2013 | fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 2014 | obj.__lookupGetter__(property) !== null) : false); 2015 | } 2016 | }; 2017 | 2018 | jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; 2019 | jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; 2020 | jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; 2021 | jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; 2022 | 2023 | jasmine.StringPrettyPrinter = function() { 2024 | jasmine.PrettyPrinter.call(this); 2025 | 2026 | this.string = ''; 2027 | }; 2028 | jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); 2029 | 2030 | jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { 2031 | this.append(value); 2032 | }; 2033 | 2034 | jasmine.StringPrettyPrinter.prototype.emitString = function(value) { 2035 | this.append("'" + value + "'"); 2036 | }; 2037 | 2038 | jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { 2039 | if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { 2040 | this.append("Array"); 2041 | return; 2042 | } 2043 | 2044 | this.append('[ '); 2045 | for (var i = 0; i < array.length; i++) { 2046 | if (i > 0) { 2047 | this.append(', '); 2048 | } 2049 | this.format(array[i]); 2050 | } 2051 | this.append(' ]'); 2052 | }; 2053 | 2054 | jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { 2055 | if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { 2056 | this.append("Object"); 2057 | return; 2058 | } 2059 | 2060 | var self = this; 2061 | this.append('{ '); 2062 | var first = true; 2063 | 2064 | this.iterateObject(obj, function(property, isGetter) { 2065 | if (first) { 2066 | first = false; 2067 | } else { 2068 | self.append(', '); 2069 | } 2070 | 2071 | self.append(property); 2072 | self.append(' : '); 2073 | if (isGetter) { 2074 | self.append(''); 2075 | } else { 2076 | self.format(obj[property]); 2077 | } 2078 | }); 2079 | 2080 | this.append(' }'); 2081 | }; 2082 | 2083 | jasmine.StringPrettyPrinter.prototype.append = function(value) { 2084 | this.string += value; 2085 | }; 2086 | jasmine.Queue = function(env) { 2087 | this.env = env; 2088 | 2089 | // parallel to blocks. each true value in this array means the block will 2090 | // get executed even if we abort 2091 | this.ensured = []; 2092 | this.blocks = []; 2093 | this.running = false; 2094 | this.index = 0; 2095 | this.offset = 0; 2096 | this.abort = false; 2097 | }; 2098 | 2099 | jasmine.Queue.prototype.addBefore = function(block, ensure) { 2100 | if (ensure === jasmine.undefined) { 2101 | ensure = false; 2102 | } 2103 | 2104 | this.blocks.unshift(block); 2105 | this.ensured.unshift(ensure); 2106 | }; 2107 | 2108 | jasmine.Queue.prototype.add = function(block, ensure) { 2109 | if (ensure === jasmine.undefined) { 2110 | ensure = false; 2111 | } 2112 | 2113 | this.blocks.push(block); 2114 | this.ensured.push(ensure); 2115 | }; 2116 | 2117 | jasmine.Queue.prototype.insertNext = function(block, ensure) { 2118 | if (ensure === jasmine.undefined) { 2119 | ensure = false; 2120 | } 2121 | 2122 | this.ensured.splice((this.index + this.offset + 1), 0, ensure); 2123 | this.blocks.splice((this.index + this.offset + 1), 0, block); 2124 | this.offset++; 2125 | }; 2126 | 2127 | jasmine.Queue.prototype.start = function(onComplete) { 2128 | this.running = true; 2129 | this.onComplete = onComplete; 2130 | this.next_(); 2131 | }; 2132 | 2133 | jasmine.Queue.prototype.isRunning = function() { 2134 | return this.running; 2135 | }; 2136 | 2137 | jasmine.Queue.LOOP_DONT_RECURSE = true; 2138 | 2139 | jasmine.Queue.prototype.next_ = function() { 2140 | var self = this; 2141 | var goAgain = true; 2142 | 2143 | while (goAgain) { 2144 | goAgain = false; 2145 | 2146 | if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { 2147 | var calledSynchronously = true; 2148 | var completedSynchronously = false; 2149 | 2150 | var onComplete = function () { 2151 | if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { 2152 | completedSynchronously = true; 2153 | return; 2154 | } 2155 | 2156 | if (self.blocks[self.index].abort) { 2157 | self.abort = true; 2158 | } 2159 | 2160 | self.offset = 0; 2161 | self.index++; 2162 | 2163 | var now = new Date().getTime(); 2164 | if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { 2165 | self.env.lastUpdate = now; 2166 | self.env.setTimeout(function() { 2167 | self.next_(); 2168 | }, 0); 2169 | } else { 2170 | if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { 2171 | goAgain = true; 2172 | } else { 2173 | self.next_(); 2174 | } 2175 | } 2176 | }; 2177 | self.blocks[self.index].execute(onComplete); 2178 | 2179 | calledSynchronously = false; 2180 | if (completedSynchronously) { 2181 | onComplete(); 2182 | } 2183 | 2184 | } else { 2185 | self.running = false; 2186 | if (self.onComplete) { 2187 | self.onComplete(); 2188 | } 2189 | } 2190 | } 2191 | }; 2192 | 2193 | jasmine.Queue.prototype.results = function() { 2194 | var results = new jasmine.NestedResults(); 2195 | for (var i = 0; i < this.blocks.length; i++) { 2196 | if (this.blocks[i].results) { 2197 | results.addResult(this.blocks[i].results()); 2198 | } 2199 | } 2200 | return results; 2201 | }; 2202 | 2203 | 2204 | /** 2205 | * Runner 2206 | * 2207 | * @constructor 2208 | * @param {jasmine.Env} env 2209 | */ 2210 | jasmine.Runner = function(env) { 2211 | var self = this; 2212 | self.env = env; 2213 | self.queue = new jasmine.Queue(env); 2214 | self.before_ = []; 2215 | self.after_ = []; 2216 | self.suites_ = []; 2217 | }; 2218 | 2219 | jasmine.Runner.prototype.execute = function() { 2220 | var self = this; 2221 | if (self.env.reporter.reportRunnerStarting) { 2222 | self.env.reporter.reportRunnerStarting(this); 2223 | } 2224 | self.queue.start(function () { 2225 | self.finishCallback(); 2226 | }); 2227 | }; 2228 | 2229 | jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { 2230 | beforeEachFunction.typeName = 'beforeEach'; 2231 | this.before_.splice(0,0,beforeEachFunction); 2232 | }; 2233 | 2234 | jasmine.Runner.prototype.afterEach = function(afterEachFunction) { 2235 | afterEachFunction.typeName = 'afterEach'; 2236 | this.after_.splice(0,0,afterEachFunction); 2237 | }; 2238 | 2239 | 2240 | jasmine.Runner.prototype.finishCallback = function() { 2241 | this.env.reporter.reportRunnerResults(this); 2242 | }; 2243 | 2244 | jasmine.Runner.prototype.addSuite = function(suite) { 2245 | this.suites_.push(suite); 2246 | }; 2247 | 2248 | jasmine.Runner.prototype.add = function(block) { 2249 | if (block instanceof jasmine.Suite) { 2250 | this.addSuite(block); 2251 | } 2252 | this.queue.add(block); 2253 | }; 2254 | 2255 | jasmine.Runner.prototype.specs = function () { 2256 | var suites = this.suites(); 2257 | var specs = []; 2258 | for (var i = 0; i < suites.length; i++) { 2259 | specs = specs.concat(suites[i].specs()); 2260 | } 2261 | return specs; 2262 | }; 2263 | 2264 | jasmine.Runner.prototype.suites = function() { 2265 | return this.suites_; 2266 | }; 2267 | 2268 | jasmine.Runner.prototype.topLevelSuites = function() { 2269 | var topLevelSuites = []; 2270 | for (var i = 0; i < this.suites_.length; i++) { 2271 | if (!this.suites_[i].parentSuite) { 2272 | topLevelSuites.push(this.suites_[i]); 2273 | } 2274 | } 2275 | return topLevelSuites; 2276 | }; 2277 | 2278 | jasmine.Runner.prototype.results = function() { 2279 | return this.queue.results(); 2280 | }; 2281 | /** 2282 | * Internal representation of a Jasmine specification, or test. 2283 | * 2284 | * @constructor 2285 | * @param {jasmine.Env} env 2286 | * @param {jasmine.Suite} suite 2287 | * @param {String} description 2288 | */ 2289 | jasmine.Spec = function(env, suite, description) { 2290 | if (!env) { 2291 | throw new Error('jasmine.Env() required'); 2292 | } 2293 | if (!suite) { 2294 | throw new Error('jasmine.Suite() required'); 2295 | } 2296 | var spec = this; 2297 | spec.id = env.nextSpecId ? env.nextSpecId() : null; 2298 | spec.env = env; 2299 | spec.suite = suite; 2300 | spec.description = description; 2301 | spec.queue = new jasmine.Queue(env); 2302 | 2303 | spec.afterCallbacks = []; 2304 | spec.spies_ = []; 2305 | 2306 | spec.results_ = new jasmine.NestedResults(); 2307 | spec.results_.description = description; 2308 | spec.matchersClass = null; 2309 | spec.exclusive_ = suite.exclusive_; 2310 | }; 2311 | 2312 | jasmine.Spec.prototype.getFullName = function() { 2313 | return this.suite.getFullName() + ' ' + this.description + '.'; 2314 | }; 2315 | 2316 | 2317 | jasmine.Spec.prototype.results = function() { 2318 | return this.results_; 2319 | }; 2320 | 2321 | /** 2322 | * All parameters are pretty-printed and concatenated together, then written to the spec's output. 2323 | * 2324 | * Be careful not to leave calls to jasmine.log in production code. 2325 | */ 2326 | jasmine.Spec.prototype.log = function() { 2327 | return this.results_.log(arguments); 2328 | }; 2329 | 2330 | jasmine.Spec.prototype.runs = function (func) { 2331 | var block = new jasmine.Block(this.env, func, this); 2332 | this.addToQueue(block); 2333 | return this; 2334 | }; 2335 | 2336 | jasmine.Spec.prototype.addToQueue = function (block) { 2337 | if (this.queue.isRunning()) { 2338 | this.queue.insertNext(block); 2339 | } else { 2340 | this.queue.add(block); 2341 | } 2342 | }; 2343 | 2344 | /** 2345 | * @param {jasmine.ExpectationResult} result 2346 | */ 2347 | jasmine.Spec.prototype.addMatcherResult = function(result) { 2348 | this.results_.addResult(result); 2349 | }; 2350 | 2351 | jasmine.Spec.prototype.expect = function(actual) { 2352 | var positive = new (this.getMatchersClass_())(this.env, actual, this); 2353 | positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); 2354 | return positive; 2355 | }; 2356 | 2357 | /** 2358 | * Waits a fixed time period before moving to the next block. 2359 | * 2360 | * @deprecated Use waitsFor() instead 2361 | * @param {Number} timeout milliseconds to wait 2362 | */ 2363 | jasmine.Spec.prototype.waits = function(timeout) { 2364 | var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); 2365 | this.addToQueue(waitsFunc); 2366 | return this; 2367 | }; 2368 | 2369 | /** 2370 | * Waits for the latchFunction to return true before proceeding to the next block. 2371 | * 2372 | * @param {Function} latchFunction 2373 | * @param {String} optional_timeoutMessage 2374 | * @param {Number} optional_timeout 2375 | */ 2376 | jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 2377 | var latchFunction_ = null; 2378 | var optional_timeoutMessage_ = null; 2379 | var optional_timeout_ = null; 2380 | 2381 | for (var i = 0; i < arguments.length; i++) { 2382 | var arg = arguments[i]; 2383 | switch (typeof arg) { 2384 | case 'function': 2385 | latchFunction_ = arg; 2386 | break; 2387 | case 'string': 2388 | optional_timeoutMessage_ = arg; 2389 | break; 2390 | case 'number': 2391 | optional_timeout_ = arg; 2392 | break; 2393 | } 2394 | } 2395 | 2396 | var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); 2397 | this.addToQueue(waitsForFunc); 2398 | return this; 2399 | }; 2400 | 2401 | jasmine.Spec.prototype.fail = function (e) { 2402 | var expectationResult = new jasmine.ExpectationResult({ 2403 | passed: false, 2404 | message: e ? jasmine.util.formatException(e) : 'Exception', 2405 | trace: { stack: e.stack } 2406 | }); 2407 | this.results_.addResult(expectationResult); 2408 | }; 2409 | 2410 | jasmine.Spec.prototype.getMatchersClass_ = function() { 2411 | return this.matchersClass || this.env.matchersClass; 2412 | }; 2413 | 2414 | jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { 2415 | var parent = this.getMatchersClass_(); 2416 | var newMatchersClass = function() { 2417 | parent.apply(this, arguments); 2418 | }; 2419 | jasmine.util.inherit(newMatchersClass, parent); 2420 | jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); 2421 | this.matchersClass = newMatchersClass; 2422 | }; 2423 | 2424 | jasmine.Spec.prototype.finishCallback = function() { 2425 | this.env.reporter.reportSpecResults(this); 2426 | }; 2427 | 2428 | jasmine.Spec.prototype.finish = function(onComplete) { 2429 | this.removeAllSpies(); 2430 | this.finishCallback(); 2431 | if (onComplete) { 2432 | onComplete(); 2433 | } 2434 | }; 2435 | 2436 | jasmine.Spec.prototype.after = function(doAfter) { 2437 | if (this.queue.isRunning()) { 2438 | this.queue.add(new jasmine.Block(this.env, doAfter, this), true); 2439 | } else { 2440 | this.afterCallbacks.unshift(doAfter); 2441 | } 2442 | }; 2443 | 2444 | jasmine.Spec.prototype.execute = function(onComplete) { 2445 | var spec = this; 2446 | if (!spec.env.specFilter(spec)) { 2447 | spec.results_.skipped = true; 2448 | spec.finish(onComplete); 2449 | return; 2450 | } 2451 | 2452 | this.env.reporter.reportSpecStarting(this); 2453 | 2454 | spec.env.currentSpec = spec; 2455 | 2456 | spec.addBeforesAndAftersToQueue(); 2457 | 2458 | spec.queue.start(function () { 2459 | spec.finish(onComplete); 2460 | }); 2461 | }; 2462 | 2463 | jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { 2464 | var runner = this.env.currentRunner(); 2465 | var i; 2466 | 2467 | for (var suite = this.suite; suite; suite = suite.parentSuite) { 2468 | for (i = 0; i < suite.before_.length; i++) { 2469 | this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); 2470 | } 2471 | } 2472 | for (i = 0; i < runner.before_.length; i++) { 2473 | this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); 2474 | } 2475 | for (i = 0; i < this.afterCallbacks.length; i++) { 2476 | this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true); 2477 | } 2478 | for (suite = this.suite; suite; suite = suite.parentSuite) { 2479 | for (i = 0; i < suite.after_.length; i++) { 2480 | this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true); 2481 | } 2482 | } 2483 | for (i = 0; i < runner.after_.length; i++) { 2484 | this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true); 2485 | } 2486 | }; 2487 | 2488 | jasmine.Spec.prototype.explodes = function() { 2489 | throw 'explodes function should not have been called'; 2490 | }; 2491 | 2492 | jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { 2493 | if (obj == jasmine.undefined) { 2494 | throw "spyOn could not find an object to spy upon for " + methodName + "()"; 2495 | } 2496 | 2497 | if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { 2498 | throw methodName + '() method does not exist'; 2499 | } 2500 | 2501 | if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { 2502 | throw new Error(methodName + ' has already been spied upon'); 2503 | } 2504 | 2505 | var spyObj = jasmine.createSpy(methodName); 2506 | 2507 | this.spies_.push(spyObj); 2508 | spyObj.baseObj = obj; 2509 | spyObj.methodName = methodName; 2510 | spyObj.originalValue = obj[methodName]; 2511 | 2512 | obj[methodName] = spyObj; 2513 | 2514 | return spyObj; 2515 | }; 2516 | 2517 | jasmine.Spec.prototype.removeAllSpies = function() { 2518 | for (var i = 0; i < this.spies_.length; i++) { 2519 | var spy = this.spies_[i]; 2520 | spy.baseObj[spy.methodName] = spy.originalValue; 2521 | } 2522 | this.spies_ = []; 2523 | }; 2524 | 2525 | /** 2526 | * Internal representation of a Jasmine suite. 2527 | * 2528 | * @constructor 2529 | * @param {jasmine.Env} env 2530 | * @param {String} description 2531 | * @param {Function} specDefinitions 2532 | * @param {jasmine.Suite} parentSuite 2533 | */ 2534 | jasmine.Suite = function(env, description, specDefinitions, parentSuite) { 2535 | var self = this; 2536 | self.id = env.nextSuiteId ? env.nextSuiteId() : null; 2537 | self.description = description; 2538 | self.queue = new jasmine.Queue(env); 2539 | self.parentSuite = parentSuite; 2540 | self.env = env; 2541 | self.before_ = []; 2542 | self.after_ = []; 2543 | self.children_ = []; 2544 | self.suites_ = []; 2545 | self.specs_ = []; 2546 | self.exclusive_ = parentSuite && parentSuite.exclusive_ || 0; 2547 | }; 2548 | 2549 | jasmine.Suite.prototype.getFullName = function() { 2550 | var fullName = this.description; 2551 | for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { 2552 | fullName = parentSuite.description + ' ' + fullName; 2553 | } 2554 | return fullName; 2555 | }; 2556 | 2557 | jasmine.Suite.prototype.finish = function(onComplete) { 2558 | this.env.reporter.reportSuiteResults(this); 2559 | this.finished = true; 2560 | if (typeof(onComplete) == 'function') { 2561 | onComplete(); 2562 | } 2563 | }; 2564 | 2565 | jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { 2566 | beforeEachFunction.typeName = 'beforeEach'; 2567 | this.before_.unshift(beforeEachFunction); 2568 | }; 2569 | 2570 | jasmine.Suite.prototype.afterEach = function(afterEachFunction) { 2571 | afterEachFunction.typeName = 'afterEach'; 2572 | this.after_.unshift(afterEachFunction); 2573 | }; 2574 | 2575 | jasmine.Suite.prototype.results = function() { 2576 | return this.queue.results(); 2577 | }; 2578 | 2579 | jasmine.Suite.prototype.add = function(suiteOrSpec) { 2580 | this.children_.push(suiteOrSpec); 2581 | if (suiteOrSpec instanceof jasmine.Suite) { 2582 | this.suites_.push(suiteOrSpec); 2583 | this.env.currentRunner().addSuite(suiteOrSpec); 2584 | } else { 2585 | this.specs_.push(suiteOrSpec); 2586 | } 2587 | this.queue.add(suiteOrSpec); 2588 | }; 2589 | 2590 | jasmine.Suite.prototype.specs = function() { 2591 | return this.specs_; 2592 | }; 2593 | 2594 | jasmine.Suite.prototype.suites = function() { 2595 | return this.suites_; 2596 | }; 2597 | 2598 | jasmine.Suite.prototype.children = function() { 2599 | return this.children_; 2600 | }; 2601 | 2602 | jasmine.Suite.prototype.execute = function(onComplete) { 2603 | var self = this; 2604 | this.queue.start(function () { 2605 | self.finish(onComplete); 2606 | }); 2607 | }; 2608 | jasmine.WaitsBlock = function(env, timeout, spec) { 2609 | this.timeout = timeout; 2610 | jasmine.Block.call(this, env, null, spec); 2611 | }; 2612 | 2613 | jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); 2614 | 2615 | jasmine.WaitsBlock.prototype.execute = function (onComplete) { 2616 | if (jasmine.VERBOSE) { 2617 | this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); 2618 | } 2619 | this.env.setTimeout(function () { 2620 | onComplete(); 2621 | }, this.timeout); 2622 | }; 2623 | /** 2624 | * A block which waits for some condition to become true, with timeout. 2625 | * 2626 | * @constructor 2627 | * @extends jasmine.Block 2628 | * @param {jasmine.Env} env The Jasmine environment. 2629 | * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. 2630 | * @param {Function} latchFunction A function which returns true when the desired condition has been met. 2631 | * @param {String} message The message to display if the desired condition hasn't been met within the given time period. 2632 | * @param {jasmine.Spec} spec The Jasmine spec. 2633 | */ 2634 | jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { 2635 | this.timeout = timeout || env.defaultTimeoutInterval; 2636 | this.latchFunction = latchFunction; 2637 | this.message = message; 2638 | this.totalTimeSpentWaitingForLatch = 0; 2639 | jasmine.Block.call(this, env, null, spec); 2640 | }; 2641 | jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); 2642 | 2643 | jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; 2644 | 2645 | jasmine.WaitsForBlock.prototype.execute = function(onComplete) { 2646 | if (jasmine.VERBOSE) { 2647 | this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); 2648 | } 2649 | var latchFunctionResult; 2650 | try { 2651 | latchFunctionResult = this.latchFunction.apply(this.spec); 2652 | } catch (e) { 2653 | this.spec.fail(e); 2654 | onComplete(); 2655 | return; 2656 | } 2657 | 2658 | if (latchFunctionResult) { 2659 | onComplete(); 2660 | } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { 2661 | var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); 2662 | this.spec.fail({ 2663 | name: 'timeout', 2664 | message: message 2665 | }); 2666 | 2667 | this.abort = true; 2668 | onComplete(); 2669 | } else { 2670 | this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; 2671 | var self = this; 2672 | this.env.setTimeout(function() { 2673 | self.execute(onComplete); 2674 | }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); 2675 | } 2676 | }; 2677 | 2678 | jasmine.version_= { 2679 | "major": 1, 2680 | "minor": 3, 2681 | "build": 1, 2682 | "revision": 1354556913 2683 | }; 2684 | -------------------------------------------------------------------------------- /lib/jasmine-node/reporter.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // 3 | // Imports 4 | // 5 | var util; 6 | try { 7 | util = require('util') 8 | } catch(e) { 9 | util = require('sys') 10 | } 11 | 12 | var jasmineNode = {}; 13 | // 14 | // Helpers 15 | // 16 | function noop() {} 17 | 18 | 19 | jasmineNode.TerminalReporter = function(config) { 20 | this.print_ = config.print || function (str) { process.stdout.write(util.format(str)); }; 21 | this.color_ = config.color ? this.ANSIColors : this.NoColors; 22 | 23 | this.started_ = false; 24 | this.finished_ = false; 25 | 26 | this.callback_ = config.onComplete || false 27 | 28 | this.suites_ = []; 29 | this.specResults_ = {}; 30 | this.failures_ = []; 31 | this.includeStackTrace_ = config.includeStackTrace === false ? false : true; 32 | this.stackFilter_ = config.stackFilter || function(t) { return t; }; 33 | } 34 | 35 | 36 | jasmineNode.TerminalReporter.prototype = { 37 | reportRunnerStarting: function(runner) { 38 | this.started_ = true; 39 | this.startedAt = new Date(); 40 | var suites = runner.topLevelSuites(); 41 | for (var i = 0; i < suites.length; i++) { 42 | var suite = suites[i]; 43 | this.suites_.push(this.summarize_(suite)); 44 | } 45 | }, 46 | 47 | ANSIColors: { 48 | pass: function() { return '\033[32m'; }, // Green 49 | fail: function() { return '\033[31m'; }, // Red 50 | specTiming: function() { return '\033[34m'; }, // Blue 51 | suiteTiming: function() { return '\033[33m'; }, // Yelow 52 | ignore: function() { return '\033[37m'; }, // Light Gray 53 | neutral: function() { return '\033[0m'; } // Normal 54 | }, 55 | 56 | NoColors: { 57 | pass: function() { return ''; }, 58 | fail: function() { return ''; }, 59 | specTiming: function() { return ''; }, 60 | suiteTiming: function() { return ''; }, 61 | ignore: function() { return ''; }, 62 | neutral: function() { return ''; } 63 | }, 64 | 65 | summarize_: function(suiteOrSpec) { 66 | var isSuite = suiteOrSpec instanceof jasmine.Suite; 67 | 68 | // We could use a separate object for suite and spec 69 | var summary = { 70 | id: suiteOrSpec.id, 71 | name: suiteOrSpec.description, 72 | type: isSuite? 'suite' : 'spec', 73 | suiteNestingLevel: 0, 74 | children: [] 75 | }; 76 | 77 | if (isSuite) { 78 | var calculateNestingLevel = function(examinedSuite) { 79 | var nestingLevel = 0; 80 | while (examinedSuite.parentSuite !== null) { 81 | nestingLevel += 1; 82 | examinedSuite = examinedSuite.parentSuite; 83 | } 84 | return nestingLevel; 85 | }; 86 | 87 | summary.suiteNestingLevel = calculateNestingLevel(suiteOrSpec); 88 | 89 | var children = suiteOrSpec.children(); 90 | for (var i = 0; i < children.length; i++) { 91 | summary.children.push(this.summarize_(children[i])); 92 | } 93 | } 94 | 95 | return summary; 96 | }, 97 | 98 | // This is heavily influenced by Jasmine's Html/Trivial Reporter 99 | reportRunnerResults: function(runner) { 100 | this.reportFailures_(); 101 | 102 | var results = runner.results(); 103 | var resultColor = (results.failedCount > 0) ? this.color_.fail() : this.color_.pass(); 104 | 105 | var specs = runner.specs(); 106 | var specCount = specs.length; 107 | 108 | var message = "\n\nFinished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + " seconds"; 109 | this.printLine_(message); 110 | 111 | // This is what jasmine-html.js has 112 | //message = "" + specCount + " spec" + ( specCount === 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount === 1) ? "" : "s"); 113 | 114 | this.printLine_(this.stringWithColor_(this.printRunnerResults_(runner), resultColor)); 115 | 116 | this.finished_ = true; 117 | if(this.callback_) { this.callback_(runner); } 118 | }, 119 | 120 | reportFailures_: function() { 121 | if (this.failures_.length === 0) { 122 | return; 123 | } 124 | 125 | var indent = ' ', failure; 126 | this.printLine_('\n'); 127 | 128 | this.print_('Failures:'); 129 | 130 | for (var i = 0; i < this.failures_.length; i++) { 131 | failure = this.failures_[i]; 132 | this.printLine_('\n'); 133 | this.printLine_(' ' + (i + 1) + ') ' + failure.spec); 134 | this.printLine_(' Message:'); 135 | this.printLine_(' ' + this.stringWithColor_(failure.message, this.color_.fail())); 136 | if (this.includeStackTrace_) { 137 | this.printLine_(' Stacktrace:'); 138 | this.print_(' ' + this.stackFilter_(failure.stackTrace)); 139 | } 140 | } 141 | }, 142 | 143 | reportSuiteResults: function(suite) { 144 | // Not used in this context 145 | }, 146 | 147 | reportSpecResults: function(spec) { 148 | var result = spec.results(); 149 | var msg = ''; 150 | if (result.skipped) { 151 | msg = this.stringWithColor_('-', this.color_.ignore()); 152 | } else if (result.passed()) { 153 | msg = this.stringWithColor_('.', this.color_.pass()); 154 | } else { 155 | msg = this.stringWithColor_('F', this.color_.fail()); 156 | this.addFailureToFailures_(spec); 157 | } 158 | this.spec_results += msg; 159 | this.print_(msg); 160 | }, 161 | 162 | addFailureToFailures_: function(spec) { 163 | var result = spec.results(); 164 | var failureItem = null; 165 | 166 | var items_length = result.items_.length; 167 | for (var i = 0; i < items_length; i++) { 168 | if (result.items_[i].passed_ === false) { 169 | failureItem = result.items_[i]; 170 | 171 | var failure = { 172 | spec: spec.suite.getFullName() + " " + spec.description, 173 | message: failureItem.message, 174 | stackTrace: failureItem.trace.stack 175 | } 176 | 177 | this.failures_.push(failure); 178 | } 179 | } 180 | }, 181 | 182 | printRunnerResults_: function(runner){ 183 | var results = runner.results(); 184 | var specs = runner.specs(); 185 | var msg = ''; 186 | var skippedCount = 0; 187 | specs.forEach(function(spec) { 188 | if (spec.results().skipped) { 189 | skippedCount++; 190 | } 191 | }); 192 | var passedCount = specs.length - skippedCount; 193 | msg += passedCount + ' test' + ((passedCount === 1) ? '' : 's') + ', '; 194 | msg += results.totalCount + ' assertion' + ((results.totalCount === 1) ? '' : 's') + ', '; 195 | msg += results.failedCount + ' failure' + ((results.failedCount === 1) ? '' : 's') + ', '; 196 | msg += skippedCount + ' skipped' + '\n'; 197 | return msg; 198 | }, 199 | 200 | // Helper Methods // 201 | stringWithColor_: function(stringValue, color) { 202 | return (color || this.color_.neutral()) + stringValue + this.color_.neutral(); 203 | }, 204 | 205 | printLine_: function(stringValue) { 206 | this.print_(stringValue); 207 | this.print_('\n'); 208 | } 209 | }; 210 | 211 | // *************************************************************** 212 | // TerminalVerboseReporter uses the TerminalReporter's constructor 213 | // *************************************************************** 214 | jasmineNode.TerminalVerboseReporter = function(config) { 215 | jasmineNode.TerminalReporter.call(this, config); 216 | // The extra field in this object 217 | this.indent_ = 0; 218 | this.specTimes_ = {}; 219 | this.suiteTimes_ = {}; 220 | this.suiteResults_ = {}; 221 | } 222 | 223 | 224 | jasmineNode.TerminalVerboseReporter.prototype = { 225 | 226 | reportSpecStarting: function(spec) { 227 | now = new Date().getTime(); 228 | this.specTimes_[spec.id] = now; 229 | var suite = spec.suite; 230 | while (suite) { 231 | if (!this.suiteTimes_[suite.id]) { 232 | this.suiteTimes_[suite.id] = now; 233 | } 234 | suite = suite.parentSuite; 235 | } 236 | }, 237 | 238 | reportSpecResults: function(spec) { 239 | var elapsed = new Date().getTime() - this.specTimes_[spec.id]; 240 | 241 | if (spec.results().failedCount > 0) { 242 | this.addFailureToFailures_(spec); 243 | } 244 | 245 | this.specResults_[spec.id] = { 246 | messages: spec.results().getItems(), 247 | result: spec.results().failedCount > 0 ? 'failed' : 'passed', 248 | runtime: elapsed 249 | }; 250 | }, 251 | 252 | reportSuiteResults: function(suite) { 253 | var startTime = this.suiteTimes_[suite.id]; 254 | if (startTime) { 255 | var elapsed = new Date().getTime() - startTime; 256 | this.suiteResults_[suite.id] = { 257 | runtime: elapsed 258 | }; 259 | } 260 | }, 261 | 262 | reportRunnerResults: function(runner) { 263 | var messages = new Array(); 264 | this.buildMessagesFromResults_(messages, this.suites_); 265 | 266 | var messages_length = messages.length; 267 | for (var i = 0; i < messages_length-1; i++) { 268 | this.printLine_(messages[i]); 269 | } 270 | 271 | this.print_(messages[messages_length-1]); 272 | 273 | // Call the parent object's method 274 | jasmineNode.TerminalReporter.prototype.reportRunnerResults.call(this, runner); 275 | }, 276 | 277 | buildMessagesFromResults_: function(messages, results, depth) { 278 | var element, specResult, specIndentSpaces, msg = ''; 279 | depth = (depth === undefined) ? 0 : depth; 280 | 281 | var results_length = results.length; 282 | for (var i = 0; i < results_length; i++) { 283 | element = results[i]; 284 | 285 | if (element.type === 'spec') { 286 | specResult = this.specResults_[element.id.toString()]; 287 | 288 | if (specResult.result === 'passed') { 289 | msg = this.stringWithColor_(this.indentMessage_(element.name, depth), this.color_.pass()); 290 | } else { 291 | msg = this.stringWithColor_(this.indentMessage_(element.name, depth), this.color_.fail()); 292 | } 293 | msg += this.stringWithColor_(" - " + specResult.runtime + " ms", 294 | this.color_.specTiming()); 295 | 296 | messages.push(msg); 297 | } else { 298 | messages.push(''); 299 | msg = this.indentMessage_(element.name, depth) 300 | if (element.id != null) { 301 | suiteResult = this.suiteResults_[element.id.toString()]; 302 | if (suiteResult) { 303 | msg += this.stringWithColor_(" - " + suiteResult.runtime + " ms", this.color_.suiteTiming()); 304 | } 305 | } 306 | messages.push(msg); 307 | } 308 | 309 | this.buildMessagesFromResults_(messages, element.children, depth + 2); 310 | } 311 | }, 312 | 313 | indentMessage_: function(message, indentCount) { 314 | var _indent = ''; 315 | for (var i = 0; i < indentCount; i++) { 316 | _indent += ' '; 317 | } 318 | return (_indent + message); 319 | } 320 | }; 321 | 322 | // Inherit from TerminalReporter 323 | jasmineNode.TerminalVerboseReporter.prototype.__proto__ = jasmineNode.TerminalReporter.prototype; 324 | 325 | // Extend Teamcity Reporter 326 | jasmineNode.TeamcityReporter = function(config) { 327 | var callback_ = config.onComplete || false; 328 | 329 | (function(superFn) { 330 | jasmineNode.TeamcityReporter.prototype.reportRunnerResults = function(runner) { 331 | superFn.call(this, runner); 332 | if (callback_) {callback_(runner)} 333 | } 334 | }(jasmine.TeamcityReporter.prototype.reportRunnerResults)); 335 | }; 336 | jasmineNode.TeamcityReporter.prototype = new jasmine.TeamcityReporter; 337 | 338 | // 339 | // Exports 340 | // 341 | exports.jasmineNode = jasmineNode; 342 | })(); 343 | -------------------------------------------------------------------------------- /lib/jasmine-node/requirejs-runner.js: -------------------------------------------------------------------------------- 1 | exports.executeJsRunner = function(specCollection, done, jasmineEnv, setupFile) { 2 | var specs, 3 | specLoader = require('./requirejs-spec-loader'), 4 | requirejs = require('requirejs'), 5 | vm = require('vm'), 6 | fs = require('fs'), 7 | coffeescript = require('coffeescript'), 8 | template = fs.readFileSync( 9 | setupFile || (__dirname + '/requirejs-wrapper-template.js'), 10 | 'utf8' 11 | ), 12 | ensureUnixPath = function(path){ 13 | return path.replace(/^(.):/, '/$1').replace(/\\/g, '/'); 14 | }, 15 | buildNewContext = function(spec){ 16 | var context = { 17 | describe: describe, 18 | it: it, 19 | xdescribe: xdescribe, 20 | xit: xit, 21 | beforeEach: beforeEach, 22 | afterEach: afterEach, 23 | spyOn: spyOn, 24 | waitsFor: waitsFor, 25 | waits: waits, 26 | runs: runs, 27 | jasmine: jasmine, 28 | expect: expect, 29 | require: require, 30 | console: console, 31 | process: process, 32 | module: module, 33 | specLoader: specLoader, 34 | __dirname: spec.directory(), 35 | __filename: spec.path(), 36 | baseUrl: buildRelativeDirName(spec.directory()), 37 | csPath: __dirname + '/cs' 38 | }; 39 | 40 | context.global = context; 41 | 42 | return context; 43 | }, 44 | buildRelativeDirName = function(dir){ 45 | var retVal = "", 46 | thisDir = ensureUnixPath(process.cwd()), 47 | toDir = ensureUnixPath(dir).split('/'), 48 | index = 0; 49 | 50 | thisDir = thisDir.split('/'); 51 | 52 | for(; index < thisDir.length || index < toDir.length; index++) { 53 | if(thisDir[index] != toDir[index]){ 54 | for(var i = index; i < thisDir.length-1; i++){ 55 | retVal += '../'; 56 | } 57 | 58 | for(var i = index; i < toDir.length; i++){ 59 | retVal += toDir[i] + '/'; 60 | } 61 | 62 | break; 63 | } 64 | } 65 | 66 | return retVal.trim('/'); 67 | }; 68 | 69 | specCollection.getSpecs().forEach(function(s){ 70 | var script = fs.readFileSync(s.path(), 'utf8'); 71 | 72 | if (s.filename().substr(-6).toLowerCase() == 'coffee') { 73 | script = coffeescript.compile(script); 74 | } 75 | 76 | var newContext = buildNewContext(s); 77 | newContext.setTimeout = jasmine.getGlobal().setTimeout; 78 | newContext.setInterval = jasmine.getGlobal().setInterval; 79 | 80 | var vmContext = vm.createContext(newContext); 81 | vm.runInContext(template, vmContext); 82 | vm.runInContext(script, vmContext, s.path()); 83 | }); 84 | 85 | specLoader.executeWhenAllSpecsAreComplete(jasmineEnv); 86 | }; 87 | -------------------------------------------------------------------------------- /lib/jasmine-node/requirejs-spec-loader.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'), 2 | registry = {}, 3 | timeout = 120000, 4 | now = function() { 5 | return new Date().getTime(); 6 | }, 7 | loader = { 8 | register: function(name) { 9 | registry[name] = false; 10 | }, 11 | completed: function(name){ 12 | registry[name] = true; 13 | } 14 | }, 15 | specLoader = { 16 | defineLoader: function(requirejs) { 17 | requirejs.define('jasmine-spec-loader', function() { 18 | return loader; 19 | }); 20 | }, 21 | executeWhenAllSpecsAreComplete: function(jasmineEnv) { 22 | var allComplete = false, 23 | wait = now(), 24 | timeoutCallback = function() { 25 | allComplete = _.all(registry, function(value) { 26 | return value; 27 | }); 28 | 29 | if(!allComplete && wait + timeout > now()) { 30 | setTimeout(timeoutCallback, 100); 31 | } else if (!allComplete) { 32 | console.log('Failed to load all specs within timeout window.'); 33 | process.exit(-1); 34 | } else { 35 | jasmineEnv.execute(); 36 | } 37 | }; 38 | 39 | setTimeout(timeoutCallback, 100); 40 | }, 41 | setTimeoutInterval: function(value) { 42 | timeout = value; 43 | }, 44 | }; 45 | 46 | for(var key in specLoader) { 47 | exports[key] = specLoader[key]; 48 | } 49 | -------------------------------------------------------------------------------- /lib/jasmine-node/requirejs-wrapper-template.js: -------------------------------------------------------------------------------- 1 | /* Setup file run before spec files to setup the context (and RequireJS 2 | * specifically) to execute the spec file. 3 | * 4 | * Defined by caller: 5 | * - Jasmine predefines 6 | * - require (Node require) 7 | * - __dirname, __filename 8 | * - baseUrl (Relative path to the directory containing this file) 9 | * - csPath (Path to require-cs module) 10 | * 11 | * See requirejs-runner source for full invocation details. 12 | */ 13 | var define, 14 | requirejsOrig = require('requirejs'), 15 | ostring = Object.prototype.toString, 16 | path = require('path'), 17 | isArray = function(it){ 18 | return ostring.call(it) === '[object Array]'; 19 | }, 20 | isFunction = function(it){ 21 | return ostring.call(it) === '[object Function]'; 22 | }, 23 | requirejs = function(deps, callback){ 24 | var retVal; 25 | 26 | if(!isArray(deps) && typeof deps !== 'string'){ 27 | if(isArray(callback)){ 28 | retVal = requirejsOrig(deps, callback, arguments[2]); 29 | } else { 30 | retVal = requirejsOrig(deps, [], callback); 31 | } 32 | } else { 33 | retVal = requirejsOrig(deps, callback); 34 | } 35 | 36 | return retVal; 37 | }; 38 | 39 | requirejsOrig.config({ 40 | baseUrl: baseUrl, 41 | nodeRequire: require, 42 | paths: { 43 | cs: csPath 44 | } 45 | }); 46 | 47 | for(var key in requirejsOrig) { 48 | requirejs[key] = requirejsOrig[key]; 49 | } 50 | 51 | requirejs.config = function(config){ 52 | var alteredConfig = {}; 53 | 54 | for(var key in config) { 55 | alteredConfig[key] = config[key]; 56 | } 57 | 58 | if(alteredConfig.baseUrl){ 59 | var base = baseUrl.replace(/\\/g, '/'), 60 | splitUrl = alteredConfig.baseUrl.replace(/\\/g, '/').split('/'), 61 | index = 0; 62 | 63 | for(; index < splitUrl.length; index++){ 64 | if(splitUrl[index] === '..'){ 65 | base = path.dirname(base); 66 | } else { 67 | base += '/' + splitUrl[index]; 68 | } 69 | } 70 | 71 | alteredConfig.baseUrl = base; 72 | } 73 | 74 | return requirejsOrig.config(alteredConfig); 75 | }; 76 | 77 | require = requirejs; 78 | define = requirejs.define; 79 | -------------------------------------------------------------------------------- /lib/jasmine-node/spec-collection.js: -------------------------------------------------------------------------------- 1 | var walkdir = require('walkdir'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var specs; 5 | 6 | var createSpecObj = function(path, root) { 7 | return { 8 | path: function() { return path; }, 9 | relativePath: function() { return path.replace(root, '').replace(/^[\/\\]/, '').replace(/\\/g, '/'); }, 10 | directory: function() { return path.replace(/[\/\\][\s\w\.-]*$/, "").replace(/\\/g, '/'); }, 11 | relativeDirectory: function() { return relativePath().replace(/[\/\\][\s\w\.-]*$/, "").replace(/\\/g, '/'); }, 12 | filename: function() { return path.replace(/^.*[\\\/]/, ''); } 13 | }; 14 | }; 15 | 16 | exports.load = function(loadpaths, matcher) { 17 | var wannaBeSpecs = [] 18 | specs = []; 19 | loadpaths.forEach(function(loadpath){ 20 | wannaBeSpecs = walkdir.sync(loadpath, { follow_symlinks: true }); 21 | for (var i = 0; i < wannaBeSpecs.length; i++) { 22 | var file = wannaBeSpecs[i]; 23 | try { 24 | if (fs.statSync(file).isFile()) { 25 | if (!/.*node_modules.*/.test(path.relative(loadpath, file)) & 26 | matcher.test(path.basename(file))) { 27 | specs.push(createSpecObj(file)); 28 | } 29 | } 30 | } catch(e) { 31 | // nothing to do here 32 | } 33 | } 34 | }); 35 | }; 36 | 37 | exports.getSpecs = function() { 38 | // Sorts spec paths in ascending alphabetical order to be able to 39 | // run tests in a deterministic order. 40 | specs.sort(function(a, b) { 41 | return a.path().localeCompare(b.path()); 42 | }); 43 | return specs; 44 | }; 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jasmine-node", 3 | "version": "3.0.0", 4 | "description": "DOM-less simple JavaScript BDD testing framework for Node", 5 | "homepage": "https://github.com/mhevery/jasmine-node", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/mhevery/jasmine-node.git" 9 | }, 10 | "keywords": [ 11 | "testing", 12 | "bdd" 13 | ], 14 | "author": "Misko Hevery ", 15 | "license": "MIT", 16 | "dependencies": { 17 | "coffeescript": "~1.12.7", 18 | "gaze": "~1.1.2", 19 | "jasmine-growl-reporter": "~2.0.0", 20 | "jasmine-reporters": "~1.0.0", 21 | "mkdirp": "~0.3.5", 22 | "requirejs": "~2.3.6", 23 | "underscore": "~1.9.1", 24 | "walkdir": "~0.0.12" 25 | }, 26 | "bin": "bin/jasmine-node", 27 | "preferGlobal": true, 28 | "main": "lib/jasmine-node", 29 | "scripts": { 30 | "test": "node lib/jasmine-node/cli.js spec" 31 | }, 32 | "devDependencies": {} 33 | } 34 | -------------------------------------------------------------------------------- /scripts/specs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | entry="node lib/jasmine-node/cli.js " 4 | 5 | if [ $# -ne 0 ]; then 6 | command=$entry"$1 spec" 7 | echo $command 8 | $command 9 | else 10 | echo "Running all tests located in the spec directory" 11 | command=$entry"spec" 12 | echo $command 13 | time $command #/nested/uber-nested 14 | echo -e "\033[1;35m--- Should have 40 tests and 71 assertions and 1 Failure. ---\033[0m" 15 | echo "" 16 | 17 | echo "Running all tests located in the spec directory with coffee option" 18 | command=$entry"--coffee spec" 19 | echo $command 20 | time $command #/nested/uber-nested 21 | echo -e "\033[1;35m--- Should have 40 tests and 71 assertions and 1 Failure. ---\033[0m" 22 | echo "" 23 | 24 | echo "Running all tests located in the spec directory with requirejs option" 25 | #command=$entry"--nohelpers --runWithRequireJs spec-requirejs" 26 | command=$entry"--runWithRequireJs spec" 27 | echo $command 28 | time $command 29 | echo -e "\033[1;35m--- Should have 40 tests and 71 assertions and 1 Failure. ---\033[0m" 30 | 31 | echo "Running all tests located in the spec-requirejs directory with requirejs" 32 | #command=$entry"--nohelpers --runWithRequireJs spec-requirejs" 33 | command=$entry"--runWithRequireJs spec-requirejs" 34 | echo $command 35 | time $command 36 | echo -e "\033[1;35m--- Should have 1 test and 2 assertions and 0 Failures. ---\033[0m" 37 | fi 38 | -------------------------------------------------------------------------------- /spec-requirejs-coffee/RequireCsSpec.coffee: -------------------------------------------------------------------------------- 1 | require [ "cs!requirecs.sut" ], (sut) -> 2 | describe "RequireJs basic tests with spec and sut in CoffeeScript", -> 3 | it "should load coffeescript sut", -> 4 | expect(sut.name).toBe "CoffeeScript To Test" 5 | expect(sut.method(2)).toBe 4 6 | -------------------------------------------------------------------------------- /spec-requirejs-coffee/RequireJsSpec.coffee: -------------------------------------------------------------------------------- 1 | require [ "requirecs.sut" ], (sut) -> 2 | describe "RequireJs basic tests with spec in CoffeeScript", -> 3 | it "should load javascript sut", -> 4 | expect(sut.name).toBe "CoffeeScript To Test" 5 | expect(sut.method(2)).toBe 4 6 | -------------------------------------------------------------------------------- /spec-requirejs-coffee/requirecs.sut.coffee: -------------------------------------------------------------------------------- 1 | define -> 2 | name: 'CoffeeScript To Test' 3 | method: (input) -> 2 * input 4 | -------------------------------------------------------------------------------- /spec-requirejs-coffee/requirejs-setup.js: -------------------------------------------------------------------------------- 1 | /** Custom RequireJS setup file to test user-specified setup files */ 2 | 3 | /* We want to minimize behavior changes between this test setup file and the 4 | * default setup file to avoid breaking tests which rely on any (current or 5 | * future) default behavior. So we: 6 | * - Run the normal setup file 7 | * - Avoid introducing additional global variables 8 | * - Avoid maintaining two copies of the setup file 9 | */ 10 | eval(require('fs').readFileSync(baseUrl + '../lib/jasmine-node/requirejs-wrapper-template.js', 'utf8')); 11 | 12 | // This is our indicator that this custom setup script has run 13 | var setupHasRun = true; 14 | -------------------------------------------------------------------------------- /spec-requirejs/requirejs-setup.js: -------------------------------------------------------------------------------- 1 | /** Custom RequireJS setup file to test user-specified setup files */ 2 | 3 | /* We want to minimize behavior changes between this test setup file and the 4 | * default setup file to avoid breaking tests which rely on any (current or 5 | * future) default behavior. So we: 6 | * - Run the normal setup file 7 | * - Avoid introducing additional global variables 8 | * - Avoid maintaining two copies of the setup file 9 | */ 10 | eval(require('fs').readFileSync(baseUrl + '../lib/jasmine-node/requirejs-wrapper-template.js', 'utf8')); 11 | 12 | // This is our indicator that this custom setup script has run 13 | var setupHasRun = true; 14 | -------------------------------------------------------------------------------- /spec-requirejs/requirejs-wrapper-template.js: -------------------------------------------------------------------------------- 1 | /* Setup file run before spec files to setup the context (and RequireJS 2 | * specifically) to execute the spec file. 3 | * 4 | * Defined by caller: 5 | * - Jasmine predefines 6 | * - require (Node require) 7 | * - __dirname, __filename 8 | * - baseUrl (Relative path to the directory containing this file) 9 | * - csPath (Path to require-cs module) 10 | * 11 | * See requirejs-runner source for full invocation details. 12 | */ 13 | var define, 14 | requirejsOrig = require('requirejs'), 15 | ostring = Object.prototype.toString, 16 | path = require('path'), 17 | isArray = function(it){ 18 | return ostring.call(it) === '[object Array]'; 19 | }, 20 | isFunction = function(it){ 21 | return ostring.call(it) === '[object Function]'; 22 | }, 23 | requirejs = function(deps, callback){ 24 | var retVal; 25 | 26 | if(!isArray(deps) && typeof deps !== 'string'){ 27 | if(isArray(callback)){ 28 | retVal = requirejsOrig(deps, callback, arguments[2]); 29 | } else { 30 | retVal = requirejsOrig(deps, [], callback); 31 | } 32 | } else { 33 | retVal = requirejsOrig(deps, callback); 34 | } 35 | 36 | return retVal; 37 | }; 38 | 39 | requirejsOrig.config({ 40 | baseUrl: baseUrl, 41 | nodeRequire: require, 42 | paths: { 43 | cs: csPath 44 | } 45 | }); 46 | 47 | for(var key in requirejsOrig) { 48 | requirejs[key] = requirejsOrig[key]; 49 | } 50 | 51 | requirejs.config = function(config){ 52 | var alteredConfig = {}; 53 | 54 | for(var key in config) { 55 | alteredConfig[key] = config[key]; 56 | } 57 | 58 | if(alteredConfig.baseUrl){ 59 | var base = baseUrl.replace(/\\/g, '/'), 60 | splitUrl = alteredConfig.baseUrl.replace(/\\/g, '/').split('/'), 61 | index = 0; 62 | 63 | for(; index < splitUrl.length; index++){ 64 | if(splitUrl[index] === '..'){ 65 | base = path.dirname(base); 66 | } else { 67 | base += '/' + splitUrl[index]; 68 | } 69 | } 70 | 71 | alteredConfig.baseUrl = base; 72 | } 73 | 74 | return requirejsOrig.config(alteredConfig); 75 | }; 76 | 77 | require = requirejs; 78 | define = requirejs.define; 79 | -------------------------------------------------------------------------------- /spec-requirejs/requirejs.spec.js: -------------------------------------------------------------------------------- 1 | require(['requirejs.sut'], function(sut){ 2 | describe('RequireJs basic tests', function(){ 3 | beforeEach(function(){ 4 | expect(true).toBeTruthy(); 5 | }); 6 | afterEach(function(){ 7 | expect(true).toBeTruthy(); 8 | }); 9 | 10 | it('should load sut', function(){ 11 | expect(sut.name).toBe('Subject To Test'); 12 | expect(sut.method(2)).toBe(3); 13 | }); 14 | 15 | it('should run setup', function(){ 16 | expect(typeof setupHasRun).toBe('boolean'); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /spec-requirejs/requirejs.sut.js: -------------------------------------------------------------------------------- 1 | define(function(){ 2 | return { 3 | name: 'Subject To Test', 4 | method: function(input){ 5 | return 1+input; 6 | } 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /spec/AsyncSpec.coffee: -------------------------------------------------------------------------------- 1 | #============================================================================= 2 | # Async spec, that will be time outed 3 | #============================================================================= 4 | describe 'async', -> 5 | it 'should be timed out', -> 6 | waitsFor (-> false), 'MIRACLE', 500 7 | 8 | doneFunc = (done) -> 9 | setTimeout(done, 10000) 10 | 11 | it "should timeout after 100 ms", doneFunc, 100 12 | -------------------------------------------------------------------------------- /spec/CoffeeSpec.coffee: -------------------------------------------------------------------------------- 1 | describe 'jasmine-node', -> 2 | 3 | it 'should pass', -> 4 | expect(1+2).toEqual(3) 5 | -------------------------------------------------------------------------------- /spec/GrammarHelper.coffee: -------------------------------------------------------------------------------- 1 | global.testClass = (description, specDefinitions) -> 2 | suite = jasmine.getEnv().describe('Class: ' + description, specDefinitions) 3 | suite.tags = ['class'] 4 | suite.isIntermediate = true; 5 | suite 6 | 7 | global.feature = (description, specDefinitions) -> 8 | suite = jasmine.getEnv().describe('Feature: ' + description, specDefinitions) 9 | suite.tags = ['feature'] 10 | suite.isIntermediate = true; 11 | suite 12 | 13 | global.scenario = (desc, func) -> 14 | suite = jasmine.getEnv().describe('Scenario: ' + desc, func) 15 | suite.tags = ['scenario'] 16 | suite.isIntermediate = true; 17 | suite 18 | 19 | global.should = (description, specDefinitions) -> 20 | suite = jasmine.getEnv().it('It should ' + description, specDefinitions) 21 | suite.tags = ['should'] 22 | suite 23 | -------------------------------------------------------------------------------- /spec/HelperSpec.coffee: -------------------------------------------------------------------------------- 1 | 2 | testClass 'HelperLoader', -> 3 | feature 'Loading order', -> 4 | should 'load the helpers before the specs.', -> 5 | expect(true).toBeTruthy() 6 | # will fail to parse the spec if the helper was not loaded first 7 | -------------------------------------------------------------------------------- /spec/SampleSpecs.js: -------------------------------------------------------------------------------- 1 | describe('jasmine-node', function(){ 2 | 3 | it('should pass', function(){ 4 | expect(1+2).toEqual(3); 5 | }); 6 | 7 | it('shows asynchronous test', function(){ 8 | setTimeout(function(){ 9 | expect('second').toEqual('second'); 10 | asyncSpecDone(); 11 | }, 1); 12 | expect('first').toEqual('first'); 13 | asyncSpecWait(); 14 | }); 15 | 16 | it('shows asynchronous test node-style', function(done){ 17 | setTimeout(function(){ 18 | expect('second').toEqual('second'); 19 | // If you call done() with an argument, it will fail the spec 20 | // so you can use it as a handler for many async node calls 21 | done(); 22 | }, 1); 23 | expect('first').toEqual('first'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /spec/TestSpec.js: -------------------------------------------------------------------------------- 1 | 2 | describe('jasmine-node-flat', function(){ 3 | it('should pass', function(){ 4 | expect(1+2).toEqual(3); 5 | }); 6 | }); 7 | 8 | describe('beforeEach Timeout', function(){ 9 | beforeEach(function(done) { 10 | setTimeout(done, 1000); 11 | }, 100); 12 | it('should fail', function(){ 13 | expect(1+2).toEqual(3); 14 | }); 15 | }); 16 | 17 | describe('afterEach Timeout', function(){ 18 | afterEach(function(done) { 19 | setTimeout(done, 1000); 20 | }, 100); 21 | it('should pass and then afterEach will fail', function(){ 22 | expect(1+2).toEqual(3); 23 | }); 24 | }); 25 | 26 | describe('Testing some characters', function() { 27 | var chars = ['&', '\'', '"', '<', '>']; 28 | for(var i = 0; i < chars.length; i+=1) { 29 | currentChar = chars[i]; 30 | it('should reject ' + currentChar, (function(currentChar) { 31 | expect(false).toEqual(false); 32 | })(currentChar)); 33 | } 34 | }); 35 | 36 | describe('Testing waitsfor functionality', function() { 37 | it("Runs and then waitsFor", function() { 38 | runs(function() { 39 | 1+1; 40 | }); 41 | waitsFor(function() { 42 | return true === false; 43 | }, "the impossible", 1000); 44 | runs(function() { 45 | expect(true).toBeTruthy(); 46 | }); 47 | }); 48 | }); 49 | 50 | describe('root', function () { 51 | 52 | describe('nested', function () { 53 | 54 | xit('nested statement', function () { 55 | expect(1).toBeTruthy(); 56 | }); 57 | 58 | }); 59 | 60 | it('root statement', function () { 61 | expect(1).toBeTruthy(); 62 | }); 63 | 64 | }); 65 | 66 | describe("Top level describe block", function() { 67 | it("first it block in top level describe", function() { 68 | expect(true).toEqual(true); 69 | }); 70 | describe("Second level describe block", function() { 71 | it("first it block in second level describe", function() { 72 | expect(true).toBe(true); 73 | }); 74 | }); 75 | it("second it block in top level describe", function() { 76 | expect(true).toEqual(true); 77 | }); 78 | }); 79 | 80 | describe('async', function () { 81 | 82 | var request = function (str, func) { 83 | func('1', '2', 'hello world'); 84 | }; 85 | 86 | it("should respond with hello world", function(done) { 87 | request("http://localhost:3000/hello", function(error, response, body){ 88 | expect(body).toEqual("hello world"); 89 | done(); 90 | }); 91 | }); 92 | 93 | it("should respond with hello world", function(done) { 94 | request("http://localhost:3000/hello", function(error, response, body){ 95 | expect(body).toEqual("hello world"); 96 | done(); 97 | }); 98 | }, 250); // timeout after 250 ms 99 | 100 | }); 101 | -------------------------------------------------------------------------------- /spec/TimerSpec.js: -------------------------------------------------------------------------------- 1 | describe("Manually ticking the Jasmine Mock Clock", function() { 2 | var timerCallback; 3 | 4 | beforeEach(function() { 5 | timerCallback = jasmine.createSpy('timerCallback'); 6 | jasmine.Clock.useMock(); 7 | }); 8 | 9 | it("causes a timeout to be called synchronously", function() { 10 | setTimeout(timerCallback, 100); 11 | 12 | expect(timerCallback).not.toHaveBeenCalled(); 13 | 14 | jasmine.Clock.tick(101); 15 | 16 | expect(timerCallback).toHaveBeenCalled(); 17 | }); 18 | 19 | it("causes an interval to be called synchronously", function() { 20 | setInterval(timerCallback, 100); 21 | 22 | expect(timerCallback).not.toHaveBeenCalled(); 23 | 24 | jasmine.Clock.tick(102); 25 | expect(timerCallback).toHaveBeenCalled(); 26 | expect(timerCallback.callCount).toEqual(1); 27 | 28 | jasmine.Clock.tick(50); 29 | expect(timerCallback.callCount).toEqual(1); 30 | 31 | jasmine.Clock.tick(50); 32 | expect(timerCallback.callCount).toEqual(2); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /spec/async-callback_spec.js: -------------------------------------------------------------------------------- 1 | describe('async-callback', function() { 2 | var env; 3 | beforeEach(function() { 4 | env = new jasmine.Env(); 5 | }); 6 | 7 | describe('it', function() { 8 | 9 | it("should time out if callback is not called", function() { 10 | env.describe("it", function() { 11 | env.it("doesn't wait", function(done) { 12 | expect(1+2).toEqual(3); 13 | }); 14 | }); 15 | 16 | env.currentRunner().execute(); 17 | 18 | waitsFor(function() { 19 | return env.currentRunner().results().totalCount > 0; 20 | }, 6000); 21 | 22 | runs(function() { 23 | expect(env.currentRunner().results().failedCount).toEqual(1); 24 | expect(firstResult(env.currentRunner()).message).toMatch(/timeout/);; 25 | }); 26 | }); 27 | 28 | it("should accept timeout for individual spec", function() { 29 | env.describe("it", function() { 30 | env.it("doesn't wait", function(done) { 31 | expect(1+2).toEqual(3); 32 | }, 250); 33 | }); 34 | 35 | env.currentRunner().execute(); 36 | 37 | waitsFor(function() { 38 | return env.currentRunner().results().totalCount > 0; 39 | }, 500); 40 | 41 | runs(function() { 42 | expect(env.currentRunner().results().failedCount).toEqual(1); 43 | expect(firstResult(env.currentRunner()).message).toMatch(/timeout/);; 44 | }); 45 | }); 46 | 47 | it("should fail if callback is passed error", function() { 48 | env.describe("it", function() { 49 | env.it("doesn't wait", function(done) { 50 | process.nextTick(function() { 51 | done("Failed asynchronously"); 52 | }); 53 | }); 54 | }); 55 | 56 | env.currentRunner().execute(); 57 | 58 | waitsFor(function() { 59 | return env.currentRunner().results().totalCount > 0; 60 | }); 61 | 62 | runs(function() { 63 | expect(env.currentRunner().results().failedCount).toEqual(1); 64 | expect(firstResult(env.currentRunner()).message).toEqual("Failed asynchronously"); 65 | }); 66 | }); 67 | 68 | 69 | it("should finish after callback is called", function() { 70 | env.describe("it", function() { 71 | env.it("waits", function(done) { 72 | process.nextTick(function() { 73 | env.currentSpec.expect(1+2).toEqual(3); 74 | done(); 75 | }); 76 | }); 77 | }); 78 | 79 | env.currentRunner().execute(); 80 | 81 | waitsFor(function() { 82 | return env.currentRunner().results().totalCount > 0; 83 | }, 2000); 84 | 85 | runs(function() { 86 | expect(env.currentRunner().results().passedCount).toEqual(1); 87 | }); 88 | }); 89 | 90 | it('should run in the context of the current spec', function(){ 91 | var actualContext; 92 | var jasmineSpecContext; 93 | env.describe("it", function() { 94 | env.it("register context", function(done) { 95 | actualContext = this; 96 | jasmineSpecContext = env.currentSpec; 97 | env.expect(this).toBe(jasmineSpecContext); 98 | done(); 99 | }); 100 | }); 101 | 102 | env.currentRunner().execute(); 103 | 104 | waitsFor(function() { 105 | return env.currentRunner().results().totalCount > 0; 106 | }, 'tested jasmine env runner to run the test', 100); 107 | 108 | runs(function() { 109 | expect(actualContext).not.toBe(global); 110 | expect(actualContext).toBe(jasmineSpecContext); 111 | }); 112 | }); 113 | 114 | }); 115 | 116 | describe("beforeEach", function() { 117 | it("should wait for callback", function() { 118 | env.describe("beforeEach", function() { 119 | var waited = false; 120 | env.beforeEach(function(done) { 121 | process.nextTick(function() { 122 | waited = true; 123 | done(); 124 | }); 125 | }); 126 | env.it("waited", function() { 127 | env.currentSpec.expect(waited).toBeTruthy(); 128 | }); 129 | }); 130 | 131 | env.currentRunner().execute(); 132 | 133 | waitsFor(function() { 134 | return env.currentRunner().results().totalCount > 0; 135 | }); 136 | 137 | runs(function() { 138 | expect(env.currentRunner().results().passedCount).toEqual(1); 139 | }); 140 | }); 141 | }); 142 | 143 | describe("afterEach", function() { 144 | it("should be passed async callback", function() { 145 | var completed = false; 146 | env.describe("afterEach", function() { 147 | env.afterEach(function(done) { 148 | process.nextTick(function() { 149 | done('Failed in afterEach'); 150 | completed = true; 151 | }); 152 | }); 153 | env.it("should pass", function() { 154 | this.expect(1+2).toEqual(3); 155 | }); 156 | }); 157 | 158 | env.currentRunner().execute(); 159 | 160 | waitsFor(function() { 161 | return completed === true; 162 | }); 163 | 164 | runs(function() { 165 | expect(env.currentRunner().results().passedCount).toEqual(1); 166 | expect(env.currentRunner().results().failedCount).toEqual(1); 167 | }); 168 | }); 169 | }); 170 | }); 171 | 172 | function firstResult(runner) { 173 | return runner.results().getItems()[0].getItems()[0].getItems()[0]; 174 | } 175 | -------------------------------------------------------------------------------- /spec/helper_spec.js: -------------------------------------------------------------------------------- 1 | describe("helper", function() { 2 | it("should load the helpers", function() { 3 | var expectation= expect(true); 4 | 5 | expect(typeof(expectation.toHaveProperty)).toBe('function'); 6 | }); 7 | }); -------------------------------------------------------------------------------- /spec/litcoffee/Litcoffee.spec.litcoffee: -------------------------------------------------------------------------------- 1 | Literate CoffeeScript 2 | ==================== 3 | 4 | This is a spec using written in Literate CoffeeScript 5 | 6 | describe 'Coffee.litcoffee', -> 7 | 8 | it 'should pass', -> 9 | expect(1+2).toEqual(3) 10 | -------------------------------------------------------------------------------- /spec/nested.js/NestedSpec.js: -------------------------------------------------------------------------------- 1 | describe('jasmine-node-nested.js', function(){ 2 | it('should pass', function(){ 3 | expect(1+2).toEqual(3); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /spec/nested/NestedSpec.js: -------------------------------------------------------------------------------- 1 | describe('jasmine-node-nested', function(){ 2 | it('should pass', function(){ 3 | expect(1+2).toEqual(3); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /spec/nested/uber-nested/UberNestedSpec.js: -------------------------------------------------------------------------------- 1 | describe('jasmine-node-uber-nested', function(){ 2 | it('should pass', function(){ 3 | expect(1+2).toEqual(3); 4 | }); 5 | 6 | describe('failure', function(){ 7 | it('should report failure (THIS IS EXPECTED)', function(){ 8 | expect(true).toBeFalsy(); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /spec/reporter_spec.js: -------------------------------------------------------------------------------- 1 | var jasmineNode = require(__dirname + "/../lib/jasmine-node/reporter").jasmineNode; 2 | 3 | describe('TerminalReporter', function() { 4 | beforeEach(function() { 5 | var config = {} 6 | this.reporter = new jasmineNode.TerminalReporter(config); 7 | }); 8 | 9 | describe("initialize", function() { 10 | it('initializes print_ from config', function() { 11 | var config = { print: true }; 12 | this.reporter = new jasmineNode.TerminalReporter(config); 13 | expect(this.reporter.print_).toBeTruthy(); 14 | }); 15 | 16 | it('initializes color_ from config', function() { 17 | var config = { color: true } 18 | this.reporter = new jasmineNode.TerminalReporter(config); 19 | expect(this.reporter.color_).toEqual(jasmineNode.TerminalReporter.prototype.ANSIColors); 20 | }); 21 | 22 | it('initializes includeStackTrace_ from config', function () { 23 | var config = {} 24 | this.reporter = new jasmineNode.TerminalReporter(config); 25 | expect(this.reporter.includeStackTrace_).toBeTruthy(); 26 | }); 27 | 28 | it('sets the started_ flag to false', function() { 29 | var config = {} 30 | this.reporter = new jasmineNode.TerminalReporter(config); 31 | expect(this.reporter.started_).toBeFalsy(); 32 | }); 33 | 34 | it('sets the finished_ flag to false', function() { 35 | var config = {} 36 | this.reporter = new jasmineNode.TerminalReporter(config); 37 | expect(this.reporter.finished_).toBeFalsy(); 38 | }); 39 | 40 | it('initializes the suites_ array', function() { 41 | var config = {} 42 | this.reporter = new jasmineNode.TerminalReporter(config); 43 | expect(this.reporter.suites_.length).toEqual(0); 44 | }); 45 | 46 | it('initializes the specResults_ to an Object', function() { 47 | var config = {} 48 | this.reporter = new jasmineNode.TerminalReporter(config); 49 | expect(this.reporter.specResults_).toBeDefined(); 50 | }); 51 | 52 | it('initializes the failures_ array', function() { 53 | var config = {} 54 | this.reporter = new jasmineNode.TerminalReporter(config); 55 | expect(this.reporter.failures_.length).toEqual(0); 56 | }); 57 | 58 | it('sets the callback_ property to false by default', function() { 59 | var config = {} 60 | this.reporter = new jasmineNode.TerminalReporter(config); 61 | expect(this.reporter.callback_).toEqual(false) 62 | }); 63 | 64 | it('sets the callback_ property to onComplete if supplied', function() { 65 | var foo = function() { } 66 | var config = { onComplete: foo } 67 | this.reporter = new jasmineNode.TerminalReporter(config); 68 | expect(this.reporter.callback_).toBe(foo) 69 | }); 70 | }); 71 | 72 | describe('when the report runner starts', function() { 73 | beforeEach(function() { 74 | this.spy = spyOn(this.reporter, 'printLine_'); 75 | 76 | var runner = { 77 | topLevelSuites: function() { 78 | var suites = []; 79 | var suite = { id: 25 }; 80 | suites.push(suite); 81 | return suites; 82 | } 83 | }; 84 | this.reporter.reportRunnerStarting(runner); 85 | }); 86 | 87 | it('sets the started_ field to true', function() { 88 | expect(this.reporter.started_).toBeTruthy(); 89 | }); 90 | 91 | it('sets the startedAt field', function() { 92 | // instanceof does not work cross-context (such as when run with requirejs) 93 | var ts = Object.prototype.toString; 94 | expect(ts.call(this.reporter.startedAt)).toBe(ts.call(new Date())); 95 | }); 96 | 97 | it('buildes the suites_ collection', function() { 98 | expect(this.reporter.suites_.length).toEqual(1); 99 | expect(this.reporter.suites_[0].id).toEqual(25); 100 | }); 101 | }); 102 | 103 | describe('the summarize_ creates suite and spec tree', function() { 104 | beforeEach(function() { 105 | this.spec = { 106 | id: 1, 107 | description: 'the spec', 108 | isSuite: false 109 | } 110 | }); 111 | 112 | it('creates a summary object from spec', function() { 113 | var result = this.reporter.summarize_(this.spec); 114 | 115 | expect(result.id).toEqual(1); 116 | expect(result.name).toEqual('the spec'); 117 | expect(result.type).toEqual('spec'); 118 | expect(result.children.length).toEqual(0); 119 | }); 120 | 121 | it('creates a summary object from suite with 1 spec', function() { 122 | var env = { nextSuiteId: false } 123 | var suite = new jasmine.Suite(env, 'suite name', undefined, undefined); 124 | suite.description = 'the suite'; 125 | suite.parentSuite = null; 126 | suite.children_.push(this.spec); 127 | 128 | var result = this.reporter.summarize_(suite); 129 | expect(result.name).toEqual('the suite'); 130 | expect(result.type).toEqual('suite'); 131 | expect(result.children.length).toEqual(1); 132 | 133 | var suiteChildSpec = result.children[0]; 134 | expect(suiteChildSpec.id).toEqual(1); 135 | }); 136 | }); 137 | 138 | describe('reportRunnerResults', function() { 139 | beforeEach(function() { 140 | this.printLineSpy = spyOn(this.reporter, 'printLine_'); 141 | }); 142 | 143 | it('generates the report', function() { 144 | var failuresSpy = spyOn(this.reporter, 'reportFailures_'); 145 | var printRunnerResultsSpy = spyOn(this.reporter, 'printRunnerResults_'). 146 | andReturn('this is the runner result'); 147 | 148 | var callbackSpy = spyOn(this.reporter, 'callback_'); 149 | 150 | var runner = { 151 | results: function() { 152 | var result = { failedCount: 0 }; 153 | return result; 154 | }, 155 | specs: function() { return []; } 156 | }; 157 | this.reporter.startedAt = new Date(); 158 | 159 | this.reporter.reportRunnerResults(runner); 160 | 161 | expect(failuresSpy).toHaveBeenCalled(); 162 | expect(this.printLineSpy).toHaveBeenCalled(); 163 | expect(callbackSpy).toHaveBeenCalled(); 164 | }); 165 | }); 166 | 167 | describe('reportSpecResults', function() { 168 | beforeEach(function() { 169 | this.printSpy = spyOn(this.reporter, 'print_'); 170 | this.spec = { 171 | id: 1, 172 | description: 'the spec', 173 | isSuite: false, 174 | results: function() { 175 | var result = { 176 | passed: function() { return true; } 177 | } 178 | return result; 179 | } 180 | } 181 | }); 182 | 183 | it('prints a \'.\' for pass', function() { 184 | this.reporter.reportSpecResults(this.spec); 185 | expect(this.printSpy).toHaveBeenCalledWith('.'); 186 | }); 187 | 188 | it('prints an \'F\' for failure', function() { 189 | var addFailureToFailuresSpy = spyOn(this.reporter, 'addFailureToFailures_'); 190 | var results = function() { 191 | var result = { 192 | passed: function() { return false; } 193 | } 194 | return result; 195 | } 196 | this.spec.results = results; 197 | 198 | this.reporter.reportSpecResults(this.spec); 199 | 200 | expect(this.printSpy).toHaveBeenCalledWith('F'); 201 | expect(addFailureToFailuresSpy).toHaveBeenCalled(); 202 | }); 203 | }); 204 | 205 | describe('addFailureToFailures', function() { 206 | it('adds message and stackTrace to failures_', function() { 207 | var spec = { 208 | suite: { 209 | getFullName: function() { return 'Suite name' } 210 | }, 211 | description: 'the spec', 212 | results: function() { 213 | var result = { 214 | items_: function() { 215 | var theItems = new Array(); 216 | var item = { 217 | passed_: false, 218 | message: 'the message', 219 | trace: { 220 | stack: 'the stack' 221 | } 222 | } 223 | theItems.push(item); 224 | return theItems; 225 | }.call() 226 | }; 227 | return result; 228 | } 229 | }; 230 | 231 | this.reporter.addFailureToFailures_(spec); 232 | 233 | var failures = this.reporter.failures_; 234 | expect(failures.length).toEqual(1); 235 | var failure = failures[0]; 236 | expect(failure.spec).toEqual('Suite name the spec'); 237 | expect(failure.message).toEqual('the message'); 238 | expect(failure.stackTrace).toEqual('the stack'); 239 | }); 240 | }); 241 | 242 | describe('prints the runner results', function() { 243 | beforeEach(function() { 244 | this.runner = { 245 | results: function() { 246 | var _results = { 247 | totalCount: 23, 248 | failedCount: 52 249 | }; 250 | return _results; 251 | }, 252 | specs: function() { 253 | var _specs = new Array(); 254 | spec = { 255 | results: function() { 256 | var _results = { 257 | skipped: false 258 | } 259 | return _results; 260 | } 261 | }; 262 | _specs.push(spec); 263 | return _specs; 264 | } 265 | }; 266 | }); 267 | 268 | it('uses the specs\'s length, totalCount and failedCount', function() { 269 | var message = this.reporter.printRunnerResults_(this.runner); 270 | expect(message).toEqual('1 test, 23 assertions, 52 failures, 0 skipped\n'); 271 | }); 272 | }); 273 | 274 | describe('reports failures', function() { 275 | beforeEach(function() { 276 | this.printSpy = spyOn(this.reporter, 'print_'); 277 | this.printLineSpy = spyOn(this.reporter, 'printLine_'); 278 | }); 279 | 280 | it('does not report anything when there are no failures', function() { 281 | this.reporter.failures_ = new Array(); 282 | 283 | this.reporter.reportFailures_(); 284 | 285 | expect(this.printLineSpy).not.toHaveBeenCalled(); 286 | }); 287 | 288 | it('prints the failures', function() { 289 | var failure = { 290 | spec: 'the spec', 291 | message: 'the message', 292 | stackTrace: 'the stackTrace' 293 | } 294 | 295 | this.reporter.failures_ = new Array(); 296 | this.reporter.failures_.push(failure); 297 | 298 | this.reporter.reportFailures_(); 299 | 300 | var generatedOutput = 301 | [ [ '\n' ], 302 | [ '\n' ], 303 | [ ' 1) the spec' ], 304 | [ ' Message:' ], 305 | [ ' the message' ], 306 | [ ' Stacktrace:' ] ]; 307 | 308 | expect(this.printLineSpy).toHaveBeenCalled(); 309 | expect(this.printLineSpy.argsForCall).toEqual(generatedOutput); 310 | 311 | expect(this.printSpy).toHaveBeenCalled(); 312 | expect(this.printSpy.argsForCall[0]).toEqual(['Failures:']); 313 | expect(this.printSpy.argsForCall[1]).toEqual([' the stackTrace']); 314 | }); 315 | 316 | it('prints the failures without a Stacktrace', function () { 317 | var config = { includeStackTrace: false }; 318 | this.reporter = new jasmineNode.TerminalReporter(config); 319 | this.printSpy = spyOn(this.reporter, 'print_'); 320 | this.printLineSpy = spyOn(this.reporter, 'printLine_'); 321 | 322 | var failure = { 323 | spec: 'the spec', 324 | message: 'the message', 325 | stackTrace: 'the stackTrace' 326 | } 327 | 328 | this.reporter.failures_ = new Array(); 329 | this.reporter.failures_.push(failure); 330 | 331 | this.reporter.reportFailures_(); 332 | 333 | var generatedOutput = 334 | [ [ '\n' ], 335 | [ '\n' ], 336 | [ ' 1) the spec' ], 337 | [ ' Message:' ], 338 | [ ' the message' ] ]; 339 | 340 | expect(this.printLineSpy).toHaveBeenCalled(); 341 | expect(this.printLineSpy.argsForCall).toEqual(generatedOutput); 342 | 343 | expect(this.printSpy).toHaveBeenCalled(); 344 | expect(this.printSpy.argsForCall[0]).toEqual(['Failures:']); 345 | expect(this.printSpy.argsForCall[1]).toBeUndefined(); 346 | }); 347 | }); 348 | }); 349 | 350 | describe('TerminalVerboseReporter', function() { 351 | beforeEach(function() { 352 | var config = {} 353 | this.verboseReporter = new jasmineNode.TerminalVerboseReporter(config); 354 | this.addFailureToFailuresSpy = spyOn(this.verboseReporter, 'addFailureToFailures_'); 355 | this.spec = { 356 | id: 23, 357 | results: function() { 358 | return { 359 | failedCount: 1, 360 | getItems: function() { 361 | return ["this is the message"]; 362 | } 363 | } 364 | } 365 | }; 366 | }); 367 | 368 | describe('#reportSpecResults', function() { 369 | it('adds the spec to the failures_', function() { 370 | this.verboseReporter.reportSpecResults(this.spec); 371 | 372 | expect(this.addFailureToFailuresSpy).toHaveBeenCalledWith(this.spec); 373 | }); 374 | 375 | it('adds a new object to the specResults_', function() { 376 | this.verboseReporter.reportSpecResults(this.spec); 377 | 378 | expect(this.verboseReporter.specResults_[23].messages).toEqual(['this is the message']); 379 | expect(this.verboseReporter.specResults_[23].result).toEqual('failed'); 380 | }); 381 | }); 382 | 383 | describe('#buildMessagesFromResults_', function() { 384 | beforeEach(function() { 385 | this.suite = { 386 | id: 17, 387 | type: 'suite', 388 | name: 'a describe block', 389 | suiteNestingLevel: 0, 390 | children: [], 391 | getFullName: function() { return "A spec"; }, 392 | }; 393 | 394 | this.spec = { 395 | id: 23, 396 | type: 'spec', 397 | name: 'a spec block', 398 | children: [] 399 | }; 400 | 401 | this.verboseReporter.specResults_['23'] = { 402 | result: 'passed', 403 | runtime: 200 404 | }; 405 | 406 | this.verboseReporter.suiteResults_['17'] = { 407 | runtime: 500 408 | }; 409 | 410 | }); 411 | 412 | it('does not build anything when the results collection is empty', function() { 413 | var results = [], 414 | messages = []; 415 | 416 | this.verboseReporter.buildMessagesFromResults_(messages, results); 417 | 418 | expect(messages.length).toEqual(0); 419 | }); 420 | 421 | it('adds a single suite to the messages', function() { 422 | var results = [], 423 | messages = []; 424 | 425 | results.push(this.suite); 426 | 427 | this.verboseReporter.buildMessagesFromResults_(messages, results); 428 | 429 | expect(messages.length).toEqual(2); 430 | expect(messages[0]).toEqual(''); 431 | expect(messages[1]).toEqual('a describe block - 500 ms'); 432 | }); 433 | 434 | it('adds a single spec with success to the messages', function() { 435 | var results = [], 436 | messages = []; 437 | 438 | this.passSpy = spyOn(this.verboseReporter.color_, 'pass'); 439 | 440 | results.push(this.spec); 441 | 442 | this.verboseReporter.buildMessagesFromResults_(messages, results); 443 | 444 | expect(this.passSpy).toHaveBeenCalled(); 445 | expect(messages.length).toEqual(1); 446 | expect(messages[0]).toEqual('a spec block - 200 ms'); 447 | }); 448 | 449 | it('adds a single spec with failure to the messages', function() { 450 | var results = [], 451 | messages = []; 452 | 453 | this.verboseReporter.specResults_['23'].result = 'failed'; 454 | 455 | this.passSpy = spyOn(this.verboseReporter.color_, 'pass'); 456 | this.failSpy = spyOn(this.verboseReporter.color_, 'fail'); 457 | 458 | results.push(this.spec); 459 | 460 | this.verboseReporter.buildMessagesFromResults_(messages, results); 461 | 462 | expect(this.failSpy).toHaveBeenCalled(); 463 | expect(this.passSpy).not.toHaveBeenCalled(); 464 | }); 465 | 466 | it('adds a suite, a suite and a single spec with success to the messages', function() { 467 | var results = [], 468 | messages = []; 469 | 470 | var subSuite = new Object(); 471 | subSuite.id = '29'; 472 | subSuite.type = 'suite'; 473 | subSuite.name = 'a sub describe block'; 474 | subSuite.suiteNestingLevel = 1; 475 | subSuite.children = []; 476 | subSuite.children.push(this.spec); 477 | 478 | this.suite.children.push(subSuite); 479 | results.push(this.suite); 480 | 481 | this.verboseReporter.suiteResults_['29'] = { 482 | runtime: 350 483 | }; 484 | 485 | this.verboseReporter.buildMessagesFromResults_(messages, results); 486 | 487 | expect(messages.length).toEqual(5); 488 | expect(messages[0]).toEqual(''); 489 | expect(messages[1]).toEqual('a describe block - 500 ms'); 490 | expect(messages[2]).toEqual(''); 491 | expect(messages[3]).toEqual(' a sub describe block - 350 ms'); 492 | expect(messages[4]).toEqual(' a spec block - 200 ms'); 493 | }); 494 | }); 495 | }); 496 | -------------------------------------------------------------------------------- /spec/sample_helper.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | var objectToString = Object.prototype.toString; 4 | var PRIMITIVE_TYPES = [String, Number, RegExp, Boolean, Date]; 5 | 6 | jasmine.Matchers.prototype.toHaveProperty = function(prop) { 7 | try { 8 | return prop in this.actual; 9 | } 10 | catch (e) { 11 | return false; 12 | } 13 | } 14 | 15 | })(); 16 | -------------------------------------------------------------------------------- /specs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | entry="node lib/jasmine-node/cli.js --noStack " 4 | 5 | echo "Running all tests located in the spec directory" 6 | command=$entry"spec" 7 | echo $command 8 | time $command #/nested/uber-nested 9 | echo -e "\033[1;35m--- Should have 59 tests and 104 assertions and 4 Failure. ---\033[0m" 10 | echo "" 11 | 12 | echo "Running all tests located in the spec directory with coffee option" 13 | command=$entry"--coffee spec" 14 | echo $command 15 | time $command #/nested/uber-nested 16 | echo -e "\033[1;35m--- Should have 64 tests and 109 assertions and 6 Failures. ---\033[0m" 17 | echo "" 18 | 19 | echo "Running all tests located in the spec directory with requirejs option" 20 | #command=$entry"--nohelpers --runWithRequireJs spec-requirejs" 21 | command=$entry"--runWithRequireJs spec" 22 | echo $command 23 | time $command 24 | echo -e "\033[1;35m--- Should have 59 tests and 104 assertions and 4 Failure. ---\033[0m" 25 | echo "" 26 | 27 | echo "Running all tests located in the spec-requirejs directory with requirejs, requirejs setup, and coffee option" 28 | command=$entry"--runWithRequireJs --requireJsSetup spec-requirejs-coffee/requirejs-setup.js --coffee spec-requirejs-coffee" 29 | echo $command 30 | time $command 31 | echo -e "\033[1;35m--- Should have 2 tests and 4 assertions and 0 Failure. ---\033[0m" 32 | 33 | echo "Running three specs file in the spec directory with coffee option" 34 | command=$entry"--coffee spec/AsyncSpec.coffee spec/CoffeeSpec.coffee spec/SampleSpecs.js" 35 | echo $command 36 | time $command 37 | echo -e "\033[1;35m--- Should have 3 tests and 3 assertions and 2 Failure. ---\033[0m" 38 | --------------------------------------------------------------------------------