diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 686c71de..3a23f9a5 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -17,7 +17,7 @@ git clone git@github.com:yourUserName/jasmine.git # Clone your fork cd jasmine # Change directory git remote add upstream https://github.com/jasmine/jasmine.git # Assign original repository to a remote named 'upstream' git fetch upstream # Fetch changes not present in your local repository -git merge upstream/master # Sync local master with upstream repository +git merge upstream/main # Sync local main with upstream repository git checkout -b my-new-feature # Create your feature branch git commit -am 'Add some feature' # Commit your changes git push origin my-new-feature # Push to the branch @@ -121,7 +121,7 @@ The easiest way to run the tests in **Internet Explorer** is to run a VM that ha 1. Build `jasmine.js` with `npm run build` and run all specs again - this ensures that your changes self-test well 1. Revert your changes to `jasmine.js` and `jasmine-html.js` * We do this because `jasmine.js` and `jasmine-html.js` are auto-generated (as you've seen in the previous steps) and accepting multiple pull requests when this auto-generated file changes causes lots of headaches - * When we accept your pull request, we will generate these files as a separate commit and merge the entire branch into master + * When we accept your pull request, we will generate these files as a separate commit and merge the entire branch into main Note that we use Travis for Continuous Integration. We only accept green pull requests. diff --git a/.travis.yml b/.travis.yml index 3a2cb0db..dadd8965 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,5 @@ language: node_js -node_js: - - "10" - - "8" - - "12" +node_js: 10 script: $TEST_COMMAND @@ -10,15 +7,17 @@ env: global: - USE_SAUCE=true - TEST_COMMAND="bash travis-core-script.sh" - - JASMINE_LONG_PROPERTY_TESTS="y" - secure: WSPWhlnC4mWSnSPquX+m1/BCu5ch5NygkaHuM2Nea7lD8oS3XLX8QncZZAsQ4lnNfqoDDuBOizG0AESiqNvE4y6x5qvLLTS6q+ce255ZEMZ71TBdZgDEEvGMEjOPPsVXiXyTQOP1lwOPlrbZvaPgWV7e11KIBab6DfFcQpnvDgo= - secure: SW7CJhZnwaNT749Gdnhvqb5rbXlAOsygUAzh9qhtyvbqXKkmJdBIEsO01YF6pbju1X2twE9JvWCOxeZju43NgQChJlPsGbjY2j3k/TdQeTAJesQe2K7ytwghunI30gjEovtRH0T3w1EmcKPH8yj5eBIcB2OYoJHx8KEC7e68q1g= - matrix: - - TEST_COMMAND="npm test" - matrix: include: + - node_js: "10" + env: JASMINE_LONG_PROPERTY_TESTS="y" TEST_COMMAND="npm test" + - node_js: "12" + env: TEST_COMMAND="npm test" + - node_js: "8" + env: TEST_COMMAND="npm test" - env: JASMINE_BROWSER="internet explorer" SAUCE_BROWSER_VERSION=11 SAUCE_OS="Windows 8.1" if: type != pull_request addons: diff --git a/README.md b/README.md index c6a5ff36..c7189c1d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[](http://jasmine.github.io) +[](http://jasmine.github.io) -[![Build Status](https://travis-ci.org/jasmine/jasmine.svg?branch=master)](https://travis-ci.org/jasmine/jasmine) +[![Build Status](https://travis-ci.org/jasmine/jasmine.svg?branch=main)](https://travis-ci.org/jasmine/jasmine) [![Open Source Helpers](https://www.codetriage.com/jasmine/jasmine/badges/users.svg)](https://www.codetriage.com/jasmine/jasmine) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine?ref=badge_shield) @@ -9,59 +9,70 @@ Jasmine is a Behavior Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework. Thus it's suited for websites, [Node.js](http://nodejs.org) projects, or anywhere that JavaScript can run. Documentation & guides live here: [http://jasmine.github.io](http://jasmine.github.io/) -For a quick start guide of Jasmine, see the beginning of [http://jasmine.github.io/edge/introduction.html](http://jasmine.github.io/edge/introduction.html) +For a quick start guide of Jasmine, see the beginning of [http://jasmine.github.io/edge/introduction.html](http://jasmine.github.io/edge/introduction.html). Upgrading from Jasmine 2.x? Check out the [3.0 release notes](https://github.com/jasmine/jasmine/blob/v3.0.0/release_notes/3.0.md) for a list of what's new (including breaking changes). ## Contributing -Please read the [contributors' guide](https://github.com/jasmine/jasmine/blob/master/.github/CONTRIBUTING.md) +Please read the [contributors' guide](https://github.com/jasmine/jasmine/blob/main/.github/CONTRIBUTING.md). ## Installation For the Jasmine NPM module:
-[https://github.com/jasmine/jasmine-npm](https://github.com/jasmine/jasmine-npm) +[https://github.com/jasmine/jasmine-npm](https://github.com/jasmine/jasmine-npm). For the Jasmine Ruby Gem:
-[https://github.com/jasmine/jasmine-gem](https://github.com/jasmine/jasmine-gem) +[https://github.com/jasmine/jasmine-gem](https://github.com/jasmine/jasmine-gem). For the Jasmine Python Egg:
-[https://github.com/jasmine/jasmine-py](https://github.com/jasmine/jasmine-py) +[https://github.com/jasmine/jasmine-py](https://github.com/jasmine/jasmine-py). For the Jasmine headless browser gulp plugin:
-[https://github.com/jasmine/gulp-jasmine-browser](https://github.com/jasmine/gulp-jasmine-browser) +[https://github.com/jasmine/gulp-jasmine-browser](https://github.com/jasmine/gulp-jasmine-browser). To install Jasmine standalone on your local box (where **_{#.#.#}_** below is substituted by the release number downloaded): -* Download the standalone distribution for your desired release from the [releases page](https://github.com/jasmine/jasmine/releases) -* Create a Jasmine directory in your project - `mkdir my-project/jasmine` -* Move the dist to your project directory - `mv jasmine/dist/jasmine-standalone-{#.#.#}.zip my-project/jasmine` -* Change directory - `cd my-project/jasmine` -* Unzip the dist - `unzip jasmine-standalone-{#.#.#}.zip` +* Download the standalone distribution for your desired release from the [releases page](https://github.com/jasmine/jasmine/releases). +* Create a Jasmine directory in your project. - `mkdir my-project/jasmine` +* Move the dist to your project directory. - `mv jasmine/dist/jasmine-standalone-{#.#.#}.zip my-project/jasmine` +* Change directory. - `cd my-project/jasmine` +* Unzip the dist. - `unzip jasmine-standalone-{#.#.#}.zip` Add the following to your HTML file: ```html - - + + - - - + + + ``` ## Supported environments -Jasmine tests itself across many browsers (Safari, Chrome, Firefox, Microsoft Edge, and new Internet Explorer) as well as nodejs. To see the exact version tests are run against look at our [.travis.yml](https://github.com/jasmine/jasmine/blob/master/.travis.yml) +Jasmine tests itself across many browsers (Safari, Chrome, Firefox, Microsoft Edge, and Internet Explorer) as well as nodejs. -[![Sauce Test Status](https://saucelabs.com/browser-matrix/jasmine-js.svg)](https://saucelabs.com/u/jasmine-js) +| Environment | Supported versions | +|-------------------|--------------------| +| Node | 8, 10, 12 | +| Safari | 8-13 | +| Chrome | Evergreen | +| Firefox | Evergreen, 68 | +| Edge | Evergreen | +| Internet Explorer | 10, 11 | + +For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us +at the time of release. Other browsers, as well as older & newer versions of some supported browsers, are likely to work. +However, Jasmine isn't tested against them and they aren't actively supported. ## Support -* Search past discussions: [http://groups.google.com/group/jasmine-js](http://groups.google.com/group/jasmine-js) -* Send an email to the list: [jasmine-js@googlegroups.com](mailto:jasmine-js@googlegroups.com) -* View the project backlog at Pivotal Tracker: [http://www.pivotaltracker.com/projects/10606](http://www.pivotaltracker.com/projects/10606) -* Follow us on Twitter: [@JasmineBDD](http://twitter.com/JasmineBDD) +* Search past discussions: [http://groups.google.com/group/jasmine-js](http://groups.google.com/group/jasmine-js). +* Send an email to the list: [jasmine-js@googlegroups.com](mailto:jasmine-js@googlegroups.com). +* View the project backlog at Pivotal Tracker: [http://www.pivotaltracker.com/projects/10606](http://www.pivotaltracker.com/projects/10606). +* Follow us on Twitter: [@JasmineBDD](http://twitter.com/JasmineBDD). ## Maintainers diff --git a/RELEASE.md b/RELEASE.md index 20790a52..b388cf85 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -7,7 +7,7 @@ Follow the instructions in `CONTRIBUTING.md` during development. ### Git Rules -Please attempt to keep commits to `master` small, but cohesive. If a feature is contained in a bunch of small commits (e.g., it has several wip commits or small work), please squash them when pushing to `master`. +Please attempt to keep commits to `main` small, but cohesive. If a feature is contained in a bunch of small commits (e.g., it has several wip commits or small work), please squash them when pushing to `main`. ### Version @@ -29,29 +29,32 @@ When jasmine-core revs its major or minor version, the binding libraries should When ready to release - specs are all green and the stories are done: 1. Update the release notes in `release_notes` - use the Anchorman gem to generate the markdown file and edit accordingly -1. Update the version in `package.json` to a release candidate -1. Update any links or top-level landing page for the Github Pages +1. Update the version in `package.json` +1. Copy version to the Ruby gem with `grunt build:copyVersionToGem` + +### Commit and push core changes + +1. Commit release notes and version changes (jasmine.js, version.rb, package.json) +1. Push +1. Wait for Travis to go green ### Build standalone distribution 1. Build the standalone distribution with `grunt buildStandaloneDist` -### Release the Python egg +### Release the core Ruby gem + +1. __NOTE__: You will likely need to push a new jasmine gem with a dependent version right after this release. See below. +1. `rake release` - tags the repo with the version, builds the `jasmine-core` gem, pushes the gem to Rubygems.org. In order to release you will have to ensure you have rubygems creds locally. + +### Release the core Python egg Install [twine](https://github.com/pypa/twine) 1. `python setup.py sdist` 1. `twine upload dist/jasmine-core-.tar.gz` You will need pypi credentials to upload the egg. -### Release the Ruby gem - -1. Copy version to the Ruby gem with `grunt build:copyVersionToGem` -1. __NOTE__: You will likely need to point to a local jasmine gem in order to run tests locally. _Do not_ push this version of the Gemfile. -1. __NOTE__: You will likely need to push a new jasmine gem with a dependent version right after this release. -1. Push these changes to GitHub and verify that this SHA is green -1. `rake release` - tags the repo with the version, builds the `jasmine-core` gem, pushes the gem to Rubygems.org. In order to release you will have to ensure you have rubygems creds locally. - -### Release the NPM +### Release the core NPM module 1. `npm adduser` to save your credentials locally 1. `npm publish .` to publish what's in `package.json` @@ -60,15 +63,35 @@ Install [twine](https://github.com/pypa/twine) Probably only need to do this when releasing a minor version, and not a patch version. -1. `cp -R edge ${version}` to copy the current edge docs to the new version -1. Add a link to the new version in `index.html` +1. `rake update_edge_jasmine` +1. `npm run jsdoc` +1. `rake release[${version}]` to copy the current edge docs to the new version +1. Commit and push. + +### Release the binding libraries + +#### NPM + +1. Create release notes using Anchorman as above +1. In `package.json`, update both the package version and the jasmine-core dependency version +1. Commit and push. +1. Wait for Travis to go green again. +1. `grunt release ` +1. `npm publish .` + +#### Gem + +1. Create release notes using Anchorman as above +1. Update the version number in `lib/jasmine/version.rb`. +1. Update the jasmine-core dependency version in `jasmine.gemspec`. +1. Commit and push. +1. Wait for Travis to go green again. +1. `rake release` ### Finally -1. Visit the [Releases page for Jasmine](https://github.com/jasmine/jasmine/releases), find the tag just pushed. - 1. Paste in a link to the correct release notes for this release. The link should reference the blob and tag correctly, and the markdown file for the notes. - 1. If it is a pre-release, mark it as such. - 1. Attach the standalone zipfile - - -There should be a post to Pivotal Labs blog and a tweet to that link. +For each of the above GitHub repos: +1. Visit the releases page and find the tag just published. +1. Paste in a link to the correct release notes for this release. The link should reference the blob and tag correctly, and the markdown file for the notes. +1. If it is a pre-release, mark it as such. +1. For core, attach the standalone zipfile. diff --git a/lib/jasmine-core/boot.js b/lib/jasmine-core/boot.js index f3354fb2..acd4c5a8 100644 --- a/lib/jasmine-core/boot.js +++ b/lib/jasmine-core/boot.js @@ -31,13 +31,16 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ (function() { + var jasmineRequire = window.jasmineRequire || require('./jasmine.js'); /** * ## Require & Instantiate * * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. */ - window.jasmine = jasmineRequire.core(jasmineRequire); + var jasmine = jasmineRequire.core(jasmineRequire), + global = jasmine.getGlobal(); + global.jasmine = jasmine; /** * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. @@ -59,7 +62,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. */ - extend(window, jasmineInterface); + extend(global, jasmineInterface); /** * ## Runner Parameters diff --git a/lib/jasmine-core/boot/boot.js b/lib/jasmine-core/boot/boot.js index 2d684628..e1e20f1c 100644 --- a/lib/jasmine-core/boot/boot.js +++ b/lib/jasmine-core/boot/boot.js @@ -9,13 +9,16 @@ */ (function() { + var jasmineRequire = window.jasmineRequire || require('./jasmine.js'); /** * ## Require & Instantiate * * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. */ - window.jasmine = jasmineRequire.core(jasmineRequire); + var jasmine = jasmineRequire.core(jasmineRequire), + global = jasmine.getGlobal(); + global.jasmine = jasmine; /** * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. @@ -37,7 +40,7 @@ /** * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. */ - extend(window, jasmineInterface); + extend(global, jasmineInterface); /** * ## Runner Parameters diff --git a/lib/jasmine-core/example/src/Player.js b/lib/jasmine-core/example/src/Player.js index fcce8268..11851966 100644 --- a/lib/jasmine-core/example/src/Player.js +++ b/lib/jasmine-core/example/src/Player.js @@ -19,4 +19,4 @@ Player.prototype.resume = function() { Player.prototype.makeFavorite = function() { this.currentlyPlayingSong.persistFavoriteStatus(true); -}; \ No newline at end of file +}; diff --git a/lib/jasmine-core/example/src/Song.js b/lib/jasmine-core/example/src/Song.js index a8a3f2dd..02527cb1 100644 --- a/lib/jasmine-core/example/src/Song.js +++ b/lib/jasmine-core/example/src/Song.js @@ -4,4 +4,4 @@ function Song() { Song.prototype.persistFavoriteStatus = function(value) { // something complicated throw new Error("not yet implemented"); -}; \ No newline at end of file +}; diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index c53a1e36..5e436b3e 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -20,6 +20,8 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +var jasmineRequire = window.jasmineRequire || require('./jasmine.js'); + jasmineRequire.html = function(j$) { j$.ResultsNode = jasmineRequire.ResultsNode(); j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); @@ -130,7 +132,7 @@ jasmineRequire.HtmlReporter = function(j$) { if (result.status === 'failed') { failures.push(failureDom(result)); } - addDeprecationWarnings(result); + addDeprecationWarnings(result, 'suite'); }; this.specStarted = function(result) { @@ -166,7 +168,7 @@ jasmineRequire.HtmlReporter = function(j$) { failures.push(failureDom(result)); } - addDeprecationWarnings(result); + addDeprecationWarnings(result, 'spec'); }; this.displaySpecInCorrectFormat = function(result) { @@ -305,14 +307,27 @@ jasmineRequire.HtmlReporter = function(j$) { addDeprecationWarnings(doneResult); - var warningBarClassName = 'jasmine-bar jasmine-warning'; for (i = 0; i < deprecationWarnings.length; i++) { - var warning = deprecationWarnings[i]; + var context; + + switch (deprecationWarnings[i].runnableType) { + case 'spec': + context = '(in spec: ' + deprecationWarnings[i].runnableName + ')'; + break; + case 'suite': + context = '(in suite: ' + deprecationWarnings[i].runnableName + ')'; + break; + default: + context = ''; + } + alert.appendChild( createDom( 'span', - { className: warningBarClassName }, - 'DEPRECATION: ' + warning + { className: 'jasmine-bar jasmine-warning' }, + 'DEPRECATION: ' + deprecationWarnings[i].message, + createDom('br'), + context ) ); } @@ -350,9 +365,11 @@ jasmineRequire.HtmlReporter = function(j$) { find('.jasmine-failures-menu').onclick = function() { setMenuModeTo('jasmine-failure-list'); + return false; }; find('.jasmine-spec-list-menu').onclick = function() { setMenuModeTo('jasmine-spec-list'); + return false; }; setMenuModeTo('jasmine-failure-list'); @@ -621,12 +638,16 @@ jasmineRequire.HtmlReporter = function(j$) { return addToExistingQueryString('spec', els.join(' ')); } - function addDeprecationWarnings(result) { + function addDeprecationWarnings(result, runnableType) { if (result && result.deprecationWarnings) { for (var i = 0; i < result.deprecationWarnings.length; i++) { var warning = result.deprecationWarnings[i].message; if (!j$.util.arrayContains(warning)) { - deprecationWarnings.push(warning); + deprecationWarnings.push({ + message: warning, + runnableName: result.fullName, + runnableType: runnableType + }); } } } diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 3135ef51..5ab5fec7 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -71,7 +71,6 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.Expector = jRequire.Expector(j$); j$.Expectation = jRequire.Expectation(j$); j$.buildExpectationResult = jRequire.buildExpectationResult(j$); - j$.noopTimer = jRequire.noopTimer(); j$.JsApiReporter = jRequire.JsApiReporter(j$); j$.asymmetricEqualityTesterArgCompatShim = jRequire.asymmetricEqualityTesterArgCompatShim( j$ @@ -168,8 +167,10 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) { 'toBeUndefined', 'toContain', 'toEqual', + 'toHaveSize', 'toHaveBeenCalled', 'toHaveBeenCalledBefore', + 'toHaveBeenCalledOnceWith', 'toHaveBeenCalledTimes', 'toHaveBeenCalledWith', 'toHaveClass', @@ -266,6 +267,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { return j$.isA_('AsyncFunction', value); }; + j$.isGeneratorFunction_ = function(value) { + return j$.isA_('GeneratorFunction', value); + }; + j$.isTypedArray_ = function(value) { return ( j$.isA_('Float32Array', value) || @@ -338,6 +343,24 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { ); }; + j$.isWeakMap = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + typeof jasmineGlobal.WeakMap !== 'undefined' && + obj.constructor === jasmineGlobal.WeakMap + ); + }; + + j$.isDataView = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + typeof jasmineGlobal.DataView !== 'undefined' && + obj.constructor === jasmineGlobal.DataView + ); + }; + j$.isPromise = function(obj) { return ( typeof jasmineGlobal.Promise !== 'undefined' && @@ -704,7 +727,7 @@ getJasmineRequireObj().Spec = function(j$) { return true; }; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; - this.timer = attrs.timer || j$.noopTimer; + this.timer = attrs.timer || new j$.Timer(); if (!this.queueableFn.fn) { this.pend(); @@ -721,6 +744,7 @@ getJasmineRequireObj().Spec = function(j$) { * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. + * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} */ this.result = { id: this.id, @@ -730,7 +754,8 @@ getJasmineRequireObj().Spec = function(j$) { passedExpectations: [], deprecationWarnings: [], pendingReason: '', - duration: null + duration: null, + properties: null }; } @@ -747,6 +772,11 @@ getJasmineRequireObj().Spec = function(j$) { } }; + Spec.prototype.setSpecProperty = function(key, value) { + this.result.properties = this.result.properties || {}; + this.result.properties[key] = value; + }; + Spec.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; @@ -769,6 +799,7 @@ getJasmineRequireObj().Spec = function(j$) { fn: function(done) { self.queueableFn.fn = null; self.result.status = self.status(excluded, failSpecWithNoExp); + self.result.duration = self.timer.elapsed(); self.resultCallback(self.result, done); } }; @@ -784,7 +815,6 @@ getJasmineRequireObj().Spec = function(j$) { self.onException.apply(self, arguments); }, onComplete: function() { - self.result.duration = self.timer.elapsed(); onComplete( self.result.status === 'failed' && new j$.StopExecutionError('spec failed') @@ -1569,12 +1599,22 @@ getJasmineRequireObj().Env = function(j$) { this.deprecated = function(deprecation) { var runnable = currentRunnable() || topSuite; + var context; + + if (runnable === topSuite) { + context = ''; + } else if (runnable === currentSuite()) { + context = ' (in suite: ' + runnable.getFullName() + ')'; + } else { + context = ' (in spec: ' + runnable.getFullName() + ')'; + } + runnable.addDeprecationWarning(deprecation); if ( typeof console !== 'undefined' && typeof console.error === 'function' ) { - console.error('DEPRECATION:', deprecation); + console.error('DEPRECATION: ' + deprecation + context); } }; @@ -1716,7 +1756,8 @@ getJasmineRequireObj().Env = function(j$) { queueRunnerFactory ); - this.execute = function(runnablesToRun) { + // Both params are optional. + this.execute = function(runnablesToRun, onComplete) { installGlobalErrors(); if (!runnablesToRun) { @@ -1824,7 +1865,11 @@ getJasmineRequireObj().Env = function(j$) { failedExpectations: topSuite.result.failedExpectations, deprecationWarnings: topSuite.result.deprecationWarnings }, - function() {} + function() { + if (onComplete) { + onComplete(); + } + } ); }); } @@ -1963,6 +2008,7 @@ getJasmineRequireObj().Env = function(j$) { id: getNextSuiteId(), description: description, parentSuite: currentDeclarationSuite, + timer: new j$.Timer(), expectationFactory: expectationFactory, asyncExpectationFactory: suiteAsyncExpectationFactory, expectationResultFactory: expectationResultFactory, @@ -2141,6 +2187,40 @@ getJasmineRequireObj().Env = function(j$) { return spec; }; + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} + * @name Env#setSpecProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + this.setSpecProperty = function(key, value) { + if (!currentRunnable() || currentRunnable() == currentSuite()) { + throw new Error( + "'setSpecProperty' was used when there was no current spec" + ); + } + currentRunnable().setSpecProperty(key, value); + }; + + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} + * @name Env#setSuiteProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + this.setSuiteProperty = function(key, value) { + if (!currentSuite()) { + throw new Error( + "'setSuiteProperty' was used when there was no current suite" + ); + } + currentSuite().setSuiteProperty(key, value); + }; + this.expect = function(actual) { if (!currentRunnable()) { throw new Error( @@ -2258,7 +2338,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) { * @hideconstructor */ function JsApiReporter(options) { - var timer = options.timer || j$.noopTimer, + var timer = options.timer || new j$.Timer(), status = 'loaded'; this.started = false; @@ -2837,7 +2917,9 @@ getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) { for (i = 0; i < props.length; i++) { k = props[i]; - if (k !== 'length') { + // Skip length (dealt with above), and anything that collides with + // MatchesUtil e.g. an Array.prototype.contains method added by user code + if (k !== 'length' && !self[k]) { copyAndDeprecate(self, Array.prototype, k); } } @@ -2868,7 +2950,7 @@ getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) { }); } - props = Object.getOwnPropertyDescriptors(Array.prototype); + props = Object.getOwnPropertyDescriptors(Array.prototype); // eslint-disable-line compat/compat a = []; for (k in props) { @@ -4151,7 +4233,17 @@ getJasmineRequireObj().GlobalErrors = function(j$) { handlers.push(listener); }; - this.popListener = function popListener() { + this.popListener = function popListener(listener) { + if (!listener) { + throw new Error('popListener expects a listener'); + } + + if (listener !== handlers[handlers.length - 1]) { + throw new Error( + 'popListener was passed a different listener than the current one' + ); + } + handlers.pop(); }; } @@ -4159,6 +4251,33 @@ getJasmineRequireObj().GlobalErrors = function(j$) { return GlobalErrors; }; +/* eslint-disable compat/compat */ +getJasmineRequireObj().toBePending = function(j$) { + /** + * Expect a promise to be pending, i.e. the promise is neither resolved nor rejected. + * @function + * @async + * @name async-matchers#toBePending + * @since 3.6 + * @example + * await expectAsync(aPromise).toBePending(); + */ + return function toBePending() { + return { + compare: function(actual) { + if (!j$.isPromiseLike(actual)) { + throw new Error('Expected toBePending to be called on a promise.'); + } + var want = {}; + return Promise.race([actual, Promise.resolve(want)]).then( + function(got) { return {pass: want === got}; }, + function() { return {pass: false}; } + ); + } + }; + }; +}; + getJasmineRequireObj().toBeRejected = function(j$) { /** * Expect a promise to be rejected. @@ -4490,12 +4609,7 @@ getJasmineRequireObj().DiffBuilder = function (j$) { }; function dereferencePath(objectPath, actual, expected, pp) { - var i, asymmetricResult - - for (i = 0; i < objectPath.components.length; i++) { - actual = actual[objectPath.components[i]]; - expected = expected[objectPath.components[i]]; - + function handleAsymmetricExpected() { if (j$.isAsymmetricEqualityTester_(expected) && j$.isFunction_(expected.valuesForDiff_)) { var asymmetricResult = expected.valuesForDiff_(actual, pp); expected = asymmetricResult.self; @@ -4503,6 +4617,15 @@ getJasmineRequireObj().DiffBuilder = function (j$) { } } + var i; + handleAsymmetricExpected(); + + for (i = 0; i < objectPath.components.length; i++) { + actual = actual[objectPath.components[i]]; + expected = expected[objectPath.components[i]]; + handleAsymmetricExpected(); + } + return {actual: actual, expected: expected}; } @@ -4513,7 +4636,6 @@ getJasmineRequireObj().MatchersUtil = function(j$) { * _Note:_ Do not construct this directly. Jasmine will construct one and * pass it to matchers and asymmetric equality testers. * @name MatchersUtil - * @since 2.0.0 * @classdesc Utilities for use in implementing matchers * @constructor */ @@ -4525,6 +4647,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) { * taking into account the current set of custom value formatters. * @function * @name MatchersUtil#pp + * @since 3.6.0 * @param {*} value The value to pretty-print * @return {string} The pretty-printed value */ @@ -4536,6 +4659,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) { * logic as {@link MatchersUtil#equals}. * @function * @name MatchersUtil#contains + * @since 2.0.0 * @param {*} haystack The collection to search * @param {*} needle The value to search for * @param [customTesters] An array of custom equality testers. Deprecated. @@ -4635,6 +4759,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) { * Determines whether two values are deeply equal to each other. * @function * @name MatchersUtil#equals + * @since 2.0.0 * @param {*} a The first value to compare * @param {*} b The second value to compare * @param [customTesters] An array of custom equality testers. Deprecated. @@ -5208,6 +5333,7 @@ getJasmineRequireObj().ObjectPath = function(j$) { getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { var availableMatchers = [ + 'toBePending', 'toBeResolved', 'toBeRejected', 'toBeResolvedTo', @@ -5837,6 +5963,76 @@ getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { return toHaveBeenCalledBefore; }; +getJasmineRequireObj().toHaveBeenCalledOnceWith = function (j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledOnceWith(...arguments)'); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments. + * @function + * @name matchers#toHaveBeenCalledOnceWith + * @since 3.6.0 + * @param {...Object} - The arguments to look for + * @example + * expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2); + */ + function toHaveBeenCalledOnceWith(util) { + return { + compare: function () { + var args = Array.prototype.slice.call(arguments, 0), + actual = args[0], + expectedArgs = args.slice(1); + + if (!j$.isSpy(actual)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + util.pp(actual) + '.')); + } + + var prettyPrintedCalls = actual.calls.allArgs().map(function (argsForCall) { + return ' ' + util.pp(argsForCall); + }); + + if (actual.calls.count() === 1 && util.contains(actual.calls.allArgs(), expectedArgs)) { + return { + pass: true, + message: 'Expected spy ' + actual.and.identity + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n' + + ' ' + util.pp(expectedArgs) + '\n' + + 'But the actual call was:\n' + + prettyPrintedCalls.join(',\n') + '.\n\n' + }; + } + + function getDiffs() { + return actual.calls.allArgs().map(function (argsForCall, callIx) { + var diffBuilder = new j$.DiffBuilder(); + util.equals(argsForCall, expectedArgs, diffBuilder); + return diffBuilder.getMessage(); + }); + } + + function butString() { + switch (actual.calls.count()) { + case 0: + return 'But it was never called.\n\n'; + case 1: + return 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n' + getDiffs().join('\n') + '\n\n'; + default: + return 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n'; + } + } + + return { + pass: false, + message: 'Expected spy ' + actual.and.identity + ' to have been called only once, and with given args:\n' + + ' ' + util.pp(expectedArgs) + '\n' + + butString() + }; + } + }; + } + + return toHaveBeenCalledOnceWith; +}; + getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledTimes()'); @@ -5984,6 +6180,49 @@ getJasmineRequireObj().toHaveClass = function(j$) { return toHaveClass; }; +getJasmineRequireObj().toHaveSize = function(j$) { + /** + * {@link expect} the actual size to be equal to the expected, using array-like length or object keys size. + * @function + * @name matchers#toHaveSize + * @since 3.6.0 + * @param {Object} expected - Expected size + * @example + * array = [1,2]; + * expect(array).toHaveSize(2); + */ + function toHaveSize() { + return { + compare: function(actual, expected) { + var result = { + pass: false + }; + + if (j$.isA_('WeakSet', actual) || j$.isWeakMap(actual) || j$.isDataView(actual)) { + throw new Error('Cannot get size of ' + actual + '.'); + } + + if (j$.isSet(actual) || j$.isMap(actual)) { + result.pass = actual.size === expected; + } else if (isLength(actual.length)) { + result.pass = actual.length === expected; + } else { + result.pass = Object.keys(actual).length === expected; + } + + return result; + } + }; + } + + var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // eslint-disable-line compat/compat + function isLength(value) { + return (typeof value == 'number') && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER; + } + + return toHaveSize; +}; + getJasmineRequireObj().toMatch = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toMatch( || )'); @@ -6827,6 +7066,8 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) { }; getJasmineRequireObj().QueueRunner = function(j$) { + var nextid = 1; + function StopExecutionError() {} StopExecutionError.prototype = new Error(); j$.StopExecutionError = StopExecutionError; @@ -6846,6 +7087,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { function emptyFn() {} function QueueRunner(attrs) { + this.id_ = nextid++; var queueableFns = attrs.queueableFns || []; this.queueableFns = queueableFns.concat(attrs.cleanupFns || []); this.firstCleanupIx = queueableFns.length; @@ -6948,7 +7190,8 @@ getJasmineRequireObj().QueueRunner = function(j$) { }), errored = false, queueableFn = self.queueableFns[iterativeIndex], - timeoutId; + timeoutId, + maybeThenable; next.fail = function nextFail() { self.fail.apply(null, arguments); @@ -6976,7 +7219,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { try { if (queueableFn.fn.length === 0) { - var maybeThenable = queueableFn.fn.call(self.userContext); + maybeThenable = queueableFn.fn.call(self.userContext); if (maybeThenable && j$.isFunction_(maybeThenable.then)) { maybeThenable.then(next, onPromiseRejection); @@ -6984,7 +7227,8 @@ getJasmineRequireObj().QueueRunner = function(j$) { return { completedSynchronously: false }; } } else { - queueableFn.fn.call(self.userContext, next); + maybeThenable = queueableFn.fn.call(self.userContext, next); + this.diagnoseConflictingAsync_(queueableFn.fn, maybeThenable); completedSynchronously = false; return { completedSynchronously: false }; } @@ -7037,6 +7281,29 @@ getJasmineRequireObj().QueueRunner = function(j$) { }); }; + QueueRunner.prototype.diagnoseConflictingAsync_ = function(fn, retval) { + if (retval && j$.isFunction_(retval.then)) { + // Issue a warning that matches the user's code + if (j$.isAsyncFunction_(fn)) { + this.deprecated( + 'An asynchronous before/it/after ' + + 'function was defined with the async keyword but also took a ' + + 'done callback. This is not supported and will stop working in' + + ' the future. Either remove the done callback (recommended) or ' + + 'remove the async keyword.' + ); + } else { + this.deprecated( + 'An asynchronous before/it/after ' + + 'function took a done callback but also returned a promise. ' + + 'This is not supported and will stop working in the future. ' + + 'Either remove the done callback (recommended) or change the ' + + 'function to not return a promise.' + ); + } + } + }; + return QueueRunner; }; @@ -7285,6 +7552,30 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.afterAll.apply(env, arguments); }, + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} + * @name setSpecProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + setSpecProperty: function(key, value) { + return env.setSpecProperty(key, value); + }, + + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} + * @name setSuiteProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + setSuiteProperty: function(key, value) { + return env.setSuiteProperty(key, value); + }, + /** * Create an expectation for a spec. * @name expect @@ -7444,7 +7735,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 3.6.0 * @function * @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise. - * @see custom_object_formatter + * @see custom_object_formatters */ jasmine.addCustomObjectFormatter = function(formatter) { return env.addCustomObjectFormatter(formatter); @@ -8182,10 +8473,10 @@ getJasmineRequireObj().SpyStrategy = function(j$) { * @name SpyStrategy#throwError * @since 2.0.0 * @function - * @param {Error|String} something Thing to throw + * @param {Error|Object|String} something Thing to throw */ SpyStrategy.prototype.throwError = function(something) { - var error = something instanceof Error ? something : new Error(something); + var error = j$.isString_(something) ? new Error(something) : something; this.plan = function() { throw error; }; @@ -8200,7 +8491,13 @@ getJasmineRequireObj().SpyStrategy = function(j$) { * @param {Function} fn The function to invoke with the passed parameters. */ SpyStrategy.prototype.callFake = function(fn) { - if (!(j$.isFunction_(fn) || j$.isAsyncFunction_(fn))) { + if ( + !( + j$.isFunction_(fn) || + j$.isAsyncFunction_(fn) || + j$.isGeneratorFunction_(fn) + ) + ) { throw new Error( 'Argument passed to callFake should be a function, got ' + fn ); @@ -8368,7 +8665,7 @@ getJasmineRequireObj().Suite = function(j$) { this.beforeAllFns = []; this.afterAllFns = []; - this.timer = attrs.timer || j$.noopTimer; + this.timer = attrs.timer || new j$.Timer(); this.children = []; @@ -8381,6 +8678,7 @@ getJasmineRequireObj().Suite = function(j$) { * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. + * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} */ this.result = { id: this.id, @@ -8388,10 +8686,16 @@ getJasmineRequireObj().Suite = function(j$) { fullName: this.getFullName(), failedExpectations: [], deprecationWarnings: [], - duration: null + duration: null, + properties: null }; } + Suite.prototype.setSuiteProperty = function(key, value) { + this.result.properties = this.result.properties || {}; + this.result.properties[key] = value; + }; + Suite.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; @@ -8571,15 +8875,6 @@ getJasmineRequireObj().Timer = function() { return Timer; }; -getJasmineRequireObj().noopTimer = function() { - return { - start: function() {}, - elapsed: function() { - return 0; - } - }; -}; - getJasmineRequireObj().TreeProcessor = function() { function TreeProcessor(attrs) { var tree = attrs.tree, @@ -8855,5 +9150,5 @@ getJasmineRequireObj().UserContext = function(j$) { }; getJasmineRequireObj().version = function() { - return '3.5.0'; + return '3.6.0'; }; diff --git a/lib/jasmine-core/version.rb b/lib/jasmine-core/version.rb index b757b251..5dd3e13b 100644 --- a/lib/jasmine-core/version.rb +++ b/lib/jasmine-core/version.rb @@ -4,6 +4,6 @@ # module Jasmine module Core - VERSION = "3.5.0" + VERSION = "3.6.0" end end diff --git a/package.json b/package.json index bc602f6f..0ef7ddd8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jasmine-core", "license": "MIT", - "version": "3.5.0", + "version": "3.6.0", "repository": { "type": "git", "url": "https://github.com/jasmine/jasmine.git" @@ -13,7 +13,7 @@ "bdd" ], "scripts": { - "posttest": "eslint src/**/*.js spec/**/*.js && prettier --check src/**/*.js spec/**/*.js", + "posttest": "eslint \"src/**/*.js\" \"spec/**/*.js\" && prettier --check src/**/*.js spec/**/*.js", "test": "grunt --stack execSpecsInNode", "cleanup": "prettier --write src/**/*.js spec/**/*.js", "build": "grunt buildDistribution", @@ -28,7 +28,8 @@ "devDependencies": { "acorn": "^6.0.0", "ejs": "^2.5.5", - "eslint": "^5.16.0", + "eslint": "^6.8.0", + "eslint-plugin-compat": "^3.8.0", "express": "^4.16.4", "fast-check": "^1.21.0", "fast-glob": "^2.2.6", @@ -39,7 +40,7 @@ "grunt-css-url-embed": "^1.11.1", "grunt-sass": "^3.0.2", "jasmine": "^3.4.0", - "jasmine-browser-runner": "0.3.0", + "jasmine-browser-runner": "^0.4.0", "jsdom": "^15.0.0", "load-grunt-tasks": "^4.0.0", "node-sass": "^4.11.0", @@ -52,6 +53,12 @@ "singleQuote": true }, "eslintConfig": { + "extends": [ + "plugin:compat/recommended" + ], + "parserOptions": { + "ecmaVersion": 5 + }, "rules": { "quotes": [ "error", @@ -67,10 +74,6 @@ } ], "block-spacing": "error", - "comma-dangle": [ - "error", - "never" - ], "func-call-spacing": [ "error", "never" @@ -85,5 +88,13 @@ ], "space-before-blocks": "error" } - } + }, + "browserslist": [ + "Safari >= 8", + "last 2 Chrome versions", + "last 2 Firefox versions", + "Firefox 68", + "last 2 Edge versions", + "IE >= 10" + ] } diff --git a/release_notes/3.6.0.md b/release_notes/3.6.0.md new file mode 100644 index 00000000..cfa8628d --- /dev/null +++ b/release_notes/3.6.0.md @@ -0,0 +1,146 @@ +# Jasmine Core 3.6 Release Notes + +## Summary + +This is a maintenance release of Jasmine with a number of new features and fixes. + +## Highlights + +* Added support for custom object formatters + * Allows customizing how an object is stringified in matcher failure messages + * [Tutorial](https://jasmine.github.io/tutorials/custom_object_formatter) + * [API reference](https://jasmine.github.io/api/3.6/jasmine.html#.addCustomObjectFormatter) + +* Don't require matchers and asymmetric equality testers to pass custom object formatters back to Jasmine + - Supports custom object formatters. + - Makes it easier to write high quality matchers and asymmetric equality testers. + - The old API will still work until 4.0. + +* Properly import jasmineRequire object before using + - Improves compatibility with Webpack + - Merges [#1766](https://github.com/jasmine/jasmine/pull/1766) from @amilligan + +* Added a toHaveBeenCalledOnceWith matcher + - Merges [#1801](https://github.com/jasmine/jasmine/pull/1801) from @Maximaximum + - Fixes [#1717](https://github.com/jasmine/jasmine/issues/1717) + +* Added a toHaveSize matcher + - Merges [#1796](https://github.com/jasmine/jasmine/pull/1796) from @wokier + +* Added a toBePending async matcher + - Merges [#1808](https://github.com/jasmine/jasmine/pull/1808) from @DCtheTall + - Fixes [#1803](https://github.com/jasmine/jasmine/issues/1803) + +* Added support for user-defined spec/suite properties + - Allows specs/suites to pass data to custom reporters + - Merges [#1763](https://github.com/jasmine/jasmine/pull/1763) from @johnjbarton + +* Route unhandled promise rejections to onerror + - Merges [#1778](https://github.com/jasmine/jasmine/pull/1778) from @johnjbarton + - Fixes [#1777](https://github.com/jasmine/jasmine/issues/1777) + + +## Internal notes + +* Use a version of eslint that works on Node 8 + +* Check for syntax and standard library objects that don't work in IE + +* Run eslint against all files + +* Add Additional Test for equals Matcher + - Merges [#1829](https://github.com/jasmine/jasmine/pull/1829) from @tobiasschweizer + - Fixes [#1821](https://github.com/jasmine/jasmine/issues/1821) + +* Depend on head of jasmine-browser to fix IE failures in CI + +* Fixed test failure in Firefox 74 + +* Added test for resolveTo/rejectWith with empty parameters + - Merges [#1802](https://github.com/jasmine/jasmine/pull/1802) from @chivesrs + +* Removed unnecessary uses of new in tests + +* Realigned the browser testing matrix to match current reality + - Use Windows instead of Linux so we can get current browsers from Sauce. + - Test against the version of Firefox that corresponds to ESR as well as + latest. + - Test the latest Edge rather than a specific older version. + - Test Safari 8 and 13 instead of 8, 9 and 10. What works in those versions + is likely to work in the ones in between. + +* Don't leak global error handlers between Jasmine's own tests + +* Added basic property tests for matchersUtil.equals + +* Added integration tests for existing matcher interfaces + +* Added integration tests for asymmetric equality testers + +* Test IE before other browsers on Travis + + +## Other Changes + +* Show diffs involving root-level asymmetric equality testers + - Fixes [#1831](https://github.com/jasmine/jasmine/issues/1831) + +* Fixed references to master in docs + +* Allow spy throwError to throw an Object + - Merges [#1822](https://github.com/jasmine/jasmine/pull/1822) from @terencehonles + +* Added missing periods to README + - Merges [#1828](https://github.com/jasmine/jasmine/pull/1828) from @dirkpuge + + +* Expose setSpec/SuiteProperty on interface + - Merges [#1820](https://github.com/jasmine/jasmine/pull/1820) from @johnjbarton + +* Prevent undesired reloads when karma-jasmine-html-reporter is used + - Merges [#1807](https://github.com/jasmine/jasmine/pull/1807) from @parloti + - Fixes [#1775](https://github.com/jasmine/jasmine/issues/1775) + +* Correctly report spec and suite duration + - Fixes [#1676](https://github.com/jasmine/jasmine/issues/1676). + +* Added jsdocs for MatchersUtil + +* Allow the .callThrough spy strategy to call constructor functions without errors + - Merges [#1782](https://github.com/jasmine/jasmine/pull/1782) from @enelson + - Fixes [#1760](https://github.com/jasmine/jasmine/issues/1760) + +* Inject a per-runable pretty printer into MatchersUtil + - Supports custom object formatters + +* Include stack traces in unhandled promise rejection messages + +* Describe the naming for the function it + - Merges [#1772](https://github.com/jasmine/jasmine/pull/1772) from @johnlinp + +* Correctly extract error messages from stack traces that don't start with `Error` + - Merges [#1776](https://github.com/jasmine/jasmine/pull/1776) from @vhermannitk + - Fixes [#1771](https://github.com/jasmine/jasmine/issues/1771) + +* Fixed objectContaining to not match when the expected is the empty object and the actual is a non-object + +* Fixed toEqual(0, Number.MIN_VALUE) to fail instead of passing + - Merges [#1764](https://github.com/jasmine/jasmine/pull/1764) from @dubzzz + +* Fixed comparison between ObjectContaining and non-objects on IE + +* Provide better diffs for object graphs that include `objectContaining` + +* Indent multiline failure messages in the output of `withContext` + * This makes it easier to see where each failure message begins and ends. + +* Report async expectations that complete after the runable completes + - See [#1752](https://github.com/jasmine/jasmine/issues/1752). + +* Treat NodeJS assertion failures as expectation failures + - Merges [#1678](https://github.com/jasmine/jasmine/pull/1678) from @apla + + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ diff --git a/spec/.eslintrc.js b/spec/.eslintrc.js new file mode 100644 index 00000000..bfbec213 --- /dev/null +++ b/spec/.eslintrc.js @@ -0,0 +1,17 @@ +module.exports = { + "ignorePatterns": [ + "support/ci.js", + "support/jasmine-browser.js" + ], + rules: { + // Relax rules for now to allow for the quirks of the test suite + // TODO: We should probably remove these & fix the resulting errors + "quotes": "off", + "semi": "off", + "key-spacing": "off", + "space-before-blocks": "off", + "no-unused-vars": "off", + "no-trailing-spaces": "off", + "block-spacing": "off", + } +} diff --git a/spec/core/AsyncExpectationSpec.js b/spec/core/AsyncExpectationSpec.js index a480e0ca..7cd1dcc8 100644 --- a/spec/core/AsyncExpectationSpec.js +++ b/spec/core/AsyncExpectationSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ describe('AsyncExpectation', function() { beforeEach(function() { jasmineUnderTest.Expectation.addAsyncCoreMatchers( diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index ea6f4bc2..53b7dc05 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -401,20 +401,16 @@ describe('Env', function() { expectationFactory('actual', specInstance); }); - env.addReporter({ - jasmineDone: function() { - expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([ - customObjectFormatter - ]); - expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({ - customTesters: [customEqualityTester], - pp: prettyPrinter - }); - done(); - } + env.execute(null, function() { + expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([ + customObjectFormatter + ]); + expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({ + customTesters: [customEqualityTester], + pp: prettyPrinter + }); + done(); }); - - env.execute(); }); it('creates an asyncExpectationFactory that uses the current custom equality testers and object formatters', function(done) { @@ -438,19 +434,15 @@ describe('Env', function() { asyncExpectationFactory('actual', specInstance); }); - env.addReporter({ - jasmineDone: function() { - expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([ - customObjectFormatter - ]); - expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({ - customTesters: [customEqualityTester], - pp: prettyPrinter - }); - done(); - } + env.execute(null, function() { + expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([ + customObjectFormatter + ]); + expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({ + customTesters: [customEqualityTester], + pp: prettyPrinter + }); + done(); }); - - env.execute(); }); }); diff --git a/spec/core/ExceptionsSpec.js b/spec/core/ExceptionsSpec.js index a040f6a9..aee9844c 100644 --- a/spec/core/ExceptionsSpec.js +++ b/spec/core/ExceptionsSpec.js @@ -29,8 +29,7 @@ describe('Exceptions:', function() { done(); }; - env.addReporter({ jasmineDone: expectations }); - env.execute(); + env.execute(null, expectations); }); it('should handle exceptions thrown directly in top-level describe blocks and continue', function(done) { @@ -53,7 +52,6 @@ describe('Exceptions:', function() { done(); }; - env.addReporter({ jasmineDone: expectations }); - env.execute(); + env.execute(null, expectations); }); }); diff --git a/spec/core/GlobalErrorsSpec.js b/spec/core/GlobalErrorsSpec.js index 3effa537..16657a7f 100644 --- a/spec/core/GlobalErrorsSpec.js +++ b/spec/core/GlobalErrorsSpec.js @@ -58,7 +58,7 @@ describe('GlobalErrors', function() { errors.pushListener(handler1); errors.pushListener(handler2); - errors.popListener(); + errors.popListener(handler2); fakeGlobal.onerror('foo'); @@ -66,6 +66,23 @@ describe('GlobalErrors', function() { expect(handler2).not.toHaveBeenCalled(); }); + it('throws when no listener is passed to #popListener', function() { + var errors = new jasmineUnderTest.GlobalErrors({}); + expect(function() { + errors.popListener(); + }).toThrowError('popListener expects a listener'); + }); + + it('throws when the argument to #popListener is not the current listener', function() { + var errors = new jasmineUnderTest.GlobalErrors({}); + errors.pushListener(function() {}); + expect(function() { + errors.popListener(function() {}); + }).toThrowError( + 'popListener was passed a different listener than the current one' + ); + }); + it('uninstalls itself, putting back a previous callback', function() { var originalCallback = jasmine.createSpy('error'), fakeGlobal = { onerror: originalCallback }, diff --git a/spec/core/PrettyPrintSpec.js b/spec/core/PrettyPrintSpec.js index 6c43cbd4..de6a11d6 100644 --- a/spec/core/PrettyPrintSpec.js +++ b/spec/core/PrettyPrintSpec.js @@ -19,7 +19,7 @@ describe('PrettyPrinter', function() { describe('stringify sets', function() { it('should stringify sets properly', function() { jasmine.getEnv().requireFunctioningSets(); - var set = new Set(); + var set = new Set(); // eslint-disable-line compat/compat set.add(1); set.add(2); var pp = jasmineUnderTest.makePrettyPrinter(); @@ -32,7 +32,7 @@ describe('PrettyPrinter', function() { try { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2; - var set = new Set(); + var set = new Set(); // eslint-disable-line compat/compat set.add('a'); set.add('b'); set.add('c'); @@ -47,7 +47,7 @@ describe('PrettyPrinter', function() { describe('stringify maps', function() { it('should stringify maps properly', function() { jasmine.getEnv().requireFunctioningMaps(); - var map = new Map(); + var map = new Map(); // eslint-disable-line compat/compat map.set(1, 2); var pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(map)).toEqual('Map( [ 1, 2 ] )'); @@ -59,7 +59,7 @@ describe('PrettyPrinter', function() { try { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2; - var map = new Map(); + var map = new Map(); // eslint-disable-line compat/compat map.set('a', 1); map.set('b', 2); map.set('c', 3); diff --git a/spec/core/QueueRunnerSpec.js b/spec/core/QueueRunnerSpec.js index ac18b81d..7333b02a 100644 --- a/spec/core/QueueRunnerSpec.js +++ b/spec/core/QueueRunnerSpec.js @@ -512,6 +512,50 @@ describe('QueueRunner', function() { expect(onExceptionCallback).toHaveBeenCalledWith('foo'); expect(queueableFn2.fn).toHaveBeenCalled(); }); + + it('issues a deprecation if the function also takes a parameter', function() { + var queueableFn = { + fn: function(done) { + return new StubPromise(); + } + }, + deprecated = jasmine.createSpy('deprecated'), + queueRunner = new jasmineUnderTest.QueueRunner({ + queueableFns: [queueableFn], + deprecated: deprecated + }), + env = jasmineUnderTest.getEnv(); + + queueRunner.execute(); + + expect(deprecated).toHaveBeenCalledWith( + 'An asynchronous ' + + 'before/it/after function took a done callback but also returned a ' + + 'promise. This is not supported and will stop working in the future. ' + + 'Either remove the done callback (recommended) or change the function ' + + 'to not return a promise.' + ); + }); + + it('issues a more specific deprecation if the function is `async`', function() { + jasmine.getEnv().requireAsyncAwait(); + eval('var fn = async function(done){};'); + var deprecated = jasmine.createSpy('deprecated'), + queueRunner = new jasmineUnderTest.QueueRunner({ + queueableFns: [{ fn: fn }], + deprecated: deprecated + }); + + queueRunner.execute(); + + expect(deprecated).toHaveBeenCalledWith( + 'An asynchronous ' + + 'before/it/after function was defined with the async keyword but ' + + 'also took a done callback. This is not supported and will stop ' + + 'working in the future. Either remove the done callback ' + + '(recommended) or remove the async keyword.' + ); + }); }); it('passes the error instance to exception handlers in HTML browsers', function() { diff --git a/spec/core/SpecSpec.js b/spec/core/SpecSpec.js index 8219a1cd..fa530049 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -227,7 +227,8 @@ describe('Spec', function() { passedExpectations: [], deprecationWarnings: [], pendingReason: '', - duration: null + duration: jasmine.any(Number), + properties: null }, 'things' ); @@ -273,8 +274,34 @@ describe('Spec', function() { }); it('should report the duration of the test', function() { + var timer = jasmine.createSpyObj('timer', { start: null, elapsed: 77000 }), + spec = new jasmineUnderTest.Spec({ + queueableFn: { fn: jasmine.createSpy('spec body') }, + catchExceptions: function() { + return false; + }, + resultCallback: function(result) { + duration = result.duration; + }, + queueRunnerFactory: function(config) { + config.queueableFns.forEach(function(qf) { + qf.fn(); + }); + config.cleanupFns.forEach(function(qf) { + qf.fn(); + }); + config.onComplete(); + }, + timer: timer + }), + duration = undefined; + + spec.execute(function() {}); + expect(duration).toBe(77000); + }); + + it('should report properties set during the test', function() { var done = jasmine.createSpy('done callback'), - timer = jasmine.createSpyObj('timer', { start: null, elapsed: 77000 }), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: jasmine.createSpy('spec body') }, catchExceptions: function() { @@ -283,11 +310,11 @@ describe('Spec', function() { resultCallback: function() {}, queueRunnerFactory: function(attrs) { attrs.onComplete(); - }, - timer: timer + } }); + spec.setSpecProperty('a', 4); spec.execute(done); - expect(spec.result.duration).toBe(77000); + expect(spec.result.properties).toEqual({ a: 4 }); }); it('#status returns passing by default', function() { diff --git a/spec/core/SpyStrategySpec.js b/spec/core/SpyStrategySpec.js index e861dc8b..85624083 100644 --- a/spec/core/SpyStrategySpec.js +++ b/spec/core/SpyStrategySpec.js @@ -70,7 +70,7 @@ describe('SpyStrategy', function() { expect(originalFn).not.toHaveBeenCalled(); }); - it('allows a non-Error to be thrown, wrapping it into an exception when executed', function() { + it('allows a string to be thrown, wrapping it into an exception when executed', function() { var originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); @@ -82,6 +82,18 @@ describe('SpyStrategy', function() { expect(originalFn).not.toHaveBeenCalled(); }); + it('allows a non-Error to be thrown when executed', function() { + var originalFn = jasmine.createSpy('original'), + spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); + + spyStrategy.throwError({ code: 'ESRCH' }); + + expect(function() { + spyStrategy.exec(); + }).toThrow({ code: 'ESRCH' }); + expect(originalFn).not.toHaveBeenCalled(); + }); + it('allows a fake function to be called instead', function() { var originalFn = jasmine.createSpy('original'), fakeFn = jasmine.createSpy('fake').and.returnValue(67), @@ -140,6 +152,28 @@ describe('SpyStrategy', function() { .catch(done.fail); }); + it('allows an empty resolved promise to be returned', function(done) { + jasmine.getEnv().requirePromises(); + + var originalFn = jasmine.createSpy('original'), + getPromise = function() { + return Promise; + }, + spyStrategy = new jasmineUnderTest.SpyStrategy({ + fn: originalFn, + getPromise: getPromise + }); + + spyStrategy.resolveTo(); + spyStrategy + .exec() + .then(function(returnValue) { + expect(returnValue).toBe(); + done(); + }) + .catch(done.fail); + }); + it('fails if promises are not available', function() { var originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); @@ -176,6 +210,29 @@ describe('SpyStrategy', function() { .catch(done.fail); }); + it('allows an empty rejected promise to be returned', function(done) { + jasmine.getEnv().requirePromises(); + + var originalFn = jasmine.createSpy('original'), + getPromise = function() { + return Promise; + }, + spyStrategy = new jasmineUnderTest.SpyStrategy({ + fn: originalFn, + getPromise: getPromise + }); + + spyStrategy.rejectWith(); + spyStrategy + .exec() + .then(done.fail) + .catch(function(error) { + expect(error).toBe(); + done(); + }) + .catch(done.fail); + }); + it('allows a non-Error to be rejected', function(done) { jasmine.getEnv().requirePromises(); @@ -277,6 +334,17 @@ describe('SpyStrategy', function() { }).toThrowError(/^Argument passed to callFake should be a function, got/); }); + it('allows generator functions to be passed to callFake strategy', function() { + jasmine.getEnv().requireGeneratorFunctions(); + + var generator = jasmine.getEnv().makeGeneratorFunction('yield "ok";'), + spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: function() {} }); + + spyStrategy.callFake(generator); + + expect(spyStrategy.exec().next().value).toEqual('ok'); + }); + it('allows a return to plan stubbing after another strategy', function() { var originalFn = jasmine.createSpy('original'), fakeFn = jasmine.createSpy('fake').and.returnValue(67), diff --git a/spec/core/UtilSpec.js b/spec/core/UtilSpec.js index 759ef8ce..8e05c6ab 100644 --- a/spec/core/UtilSpec.js +++ b/spec/core/UtilSpec.js @@ -40,7 +40,7 @@ describe('jasmineUnderTest.util', function() { beforeEach(function() { jasmine.getEnv().requirePromises(); - mockNativePromise = new Promise(function(res, rej) {}); + mockNativePromise = new Promise(function(res, rej) {}); // eslint-disable-line compat/compat mockPromiseLikeObject = new mockPromiseLike(); }); diff --git a/spec/core/asymmetricEqualityTesterArgCompatShimSpec.js b/spec/core/asymmetricEqualityTesterArgCompatShimSpec.js index fd58def6..02cccbd4 100644 --- a/spec/core/asymmetricEqualityTesterArgCompatShimSpec.js +++ b/spec/core/asymmetricEqualityTesterArgCompatShimSpec.js @@ -63,7 +63,6 @@ describe('asymmetricEqualityTesterArgCompatShim', function() { it('provides and deprecates properties of Array.prototype', function() { var keys = [ 'concat', - 'constructor', 'every', 'filter', 'forEach', @@ -82,8 +81,6 @@ describe('asymmetricEqualityTesterArgCompatShim', function() { 'some', 'sort', 'splice', - 'toLocaleString', - 'toString', 'unshift' ], optionalKeys = [ @@ -142,4 +139,46 @@ describe('asymmetricEqualityTesterArgCompatShim', function() { expect(deprecated).not.toHaveBeenCalled(); }); + + describe('When Array.prototype additions collide with MatchersUtil methods', function() { + function keys() { + return [ + 'contains', + 'buildFailureMessage', + 'asymmetricDiff_', + 'asymmetricMatch_', + 'equals', + 'eq_' + ]; + } + + beforeEach(function() { + keys().forEach(function(k) { + expect(Array.prototype[k]) + .withContext('Array.prototype already had ' + k) + .toBeUndefined(); + Array.prototype[k] = function() {}; + }); + }); + + afterEach(function() { + keys().forEach(function(k) { + delete Array.prototype[k]; + }); + }); + + it('uses the MatchersUtil methods', function() { + var matchersUtil = new jasmineUnderTest.MatchersUtil({}), + shim = jasmineUnderTest.asymmetricEqualityTesterArgCompatShim( + matchersUtil, + [] + ); + + keys().forEach(function(k) { + expect(shim[k]) + .withContext(k + ' was overwritten') + .toBe(jasmineUnderTest.MatchersUtil.prototype[k]); + }); + }); + }); }); diff --git a/spec/core/asymmetric_equality/AnySpec.js b/spec/core/asymmetric_equality/AnySpec.js index 1739ae6f..8e28c306 100644 --- a/spec/core/asymmetric_equality/AnySpec.js +++ b/spec/core/asymmetric_equality/AnySpec.js @@ -34,7 +34,7 @@ describe("Any", function() { var any = new jasmineUnderTest.Any(Map); - expect(any.asymmetricMatch(new Map())).toBe(true); + expect(any.asymmetricMatch(new Map())).toBe(true); // eslint-disable-line compat/compat }); it("matches a Set", function() { @@ -42,23 +42,23 @@ describe("Any", function() { var any = new jasmineUnderTest.Any(Set); - expect(any.asymmetricMatch(new Set())).toBe(true); + expect(any.asymmetricMatch(new Set())).toBe(true); // eslint-disable-line compat/compat }); it("matches a TypedArray", function() { jasmine.getEnv().requireFunctioningTypedArrays(); - var any = new jasmineUnderTest.Any(Uint32Array); + var any = new jasmineUnderTest.Any(Uint32Array); // eslint-disable-line compat/compat - expect(any.asymmetricMatch(new Uint32Array([]))).toBe(true); + expect(any.asymmetricMatch(new Uint32Array([]))).toBe(true); // eslint-disable-line compat/compat }); it("matches a Symbol", function() { jasmine.getEnv().requireFunctioningSymbols(); - var any = new jasmineUnderTest.Any(Symbol); + var any = new jasmineUnderTest.Any(Symbol); // eslint-disable-line compat/compat - expect(any.asymmetricMatch(Symbol())).toBe(true); + expect(any.asymmetricMatch(Symbol())).toBe(true); // eslint-disable-line compat/compat }); it("matches another constructed object", function() { diff --git a/spec/core/asymmetric_equality/AnythingSpec.js b/spec/core/asymmetric_equality/AnythingSpec.js index 216757eb..4e251b91 100644 --- a/spec/core/asymmetric_equality/AnythingSpec.js +++ b/spec/core/asymmetric_equality/AnythingSpec.js @@ -28,7 +28,7 @@ describe("Anything", function() { var anything = new jasmineUnderTest.Anything(); - expect(anything.asymmetricMatch(new Map())).toBe(true); + expect(anything.asymmetricMatch(new Map())).toBe(true); // eslint-disable-line compat/compat }); it("matches a Set", function() { @@ -36,7 +36,7 @@ describe("Anything", function() { var anything = new jasmineUnderTest.Anything(); - expect(anything.asymmetricMatch(new Set())).toBe(true); + expect(anything.asymmetricMatch(new Set())).toBe(true); // eslint-disable-line compat/compat }); it("matches a TypedArray", function() { @@ -44,7 +44,7 @@ describe("Anything", function() { var anything = new jasmineUnderTest.Anything(); - expect(anything.asymmetricMatch(new Uint32Array([]))).toBe(true); + expect(anything.asymmetricMatch(new Uint32Array([]))).toBe(true); // eslint-disable-line compat/compat }); it("matches a Symbol", function() { @@ -52,7 +52,7 @@ describe("Anything", function() { var anything = new jasmineUnderTest.Anything(); - expect(anything.asymmetricMatch(Symbol())).toBe(true); + expect(anything.asymmetricMatch(Symbol())).toBe(true); // eslint-disable-line compat/compat }); it("doesn't match undefined", function() { diff --git a/spec/core/asymmetric_equality/EmptySpec.js b/spec/core/asymmetric_equality/EmptySpec.js index 902b5001..48271ce5 100644 --- a/spec/core/asymmetric_equality/EmptySpec.js +++ b/spec/core/asymmetric_equality/EmptySpec.js @@ -24,20 +24,20 @@ describe("Empty", function () { it("matches an empty map", function () { jasmine.getEnv().requireFunctioningMaps(); var empty = new jasmineUnderTest.Empty(); - var fullMap = new Map(); + var fullMap = new Map(); // eslint-disable-line compat/compat fullMap.set('thing', 2); - expect(empty.asymmetricMatch(new Map())).toBe(true); + expect(empty.asymmetricMatch(new Map())).toBe(true); // eslint-disable-line compat/compat expect(empty.asymmetricMatch(fullMap)).toBe(false); }); it("matches an empty set", function () { jasmine.getEnv().requireFunctioningSets(); var empty = new jasmineUnderTest.Empty(); - var fullSet = new Set(); + var fullSet = new Set(); // eslint-disable-line compat/compat fullSet.add(3); - expect(empty.asymmetricMatch(new Set())).toBe(true); + expect(empty.asymmetricMatch(new Set())).toBe(true); // eslint-disable-line compat/compat expect(empty.asymmetricMatch(fullSet)).toBe(false); }); @@ -45,7 +45,7 @@ describe("Empty", function () { jasmine.getEnv().requireFunctioningTypedArrays(); var empty = new jasmineUnderTest.Empty(); - expect(empty.asymmetricMatch(new Int16Array())).toBe(true); - expect(empty.asymmetricMatch(new Int16Array([1,2]))).toBe(false); + expect(empty.asymmetricMatch(new Int16Array())).toBe(true); // eslint-disable-line compat/compat + expect(empty.asymmetricMatch(new Int16Array([1,2]))).toBe(false); // eslint-disable-line compat/compat }); }); diff --git a/spec/core/asymmetric_equality/MapContainingSpec.js b/spec/core/asymmetric_equality/MapContainingSpec.js index c9ea83a6..dc89218d 100644 --- a/spec/core/asymmetric_equality/MapContainingSpec.js +++ b/spec/core/asymmetric_equality/MapContainingSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ describe('MapContaining', function() { function MapI(iterable) { // for IE11 var map = new Map(); diff --git a/spec/core/asymmetric_equality/NotEmptySpec.js b/spec/core/asymmetric_equality/NotEmptySpec.js index 04c05966..d00ee013 100644 --- a/spec/core/asymmetric_equality/NotEmptySpec.js +++ b/spec/core/asymmetric_equality/NotEmptySpec.js @@ -24,9 +24,9 @@ describe("NotEmpty", function () { it("matches a non empty map", function () { jasmine.getEnv().requireFunctioningMaps(); var notEmpty = new jasmineUnderTest.NotEmpty(); - var fullMap = new Map(); + var fullMap = new Map(); // eslint-disable-line compat/compat fullMap.set('one', 1); - var emptyMap = new Map(); + var emptyMap = new Map(); // eslint-disable-line compat/compat expect(notEmpty.asymmetricMatch(fullMap)).toBe(true); expect(notEmpty.asymmetricMatch(emptyMap)).toBe(false); @@ -35,9 +35,9 @@ describe("NotEmpty", function () { it("matches a non empty set", function () { jasmine.getEnv().requireFunctioningSets(); var notEmpty = new jasmineUnderTest.NotEmpty(); - var filledSet = new Set(); + var filledSet = new Set(); // eslint-disable-line compat/compat filledSet.add(1); - var emptySet = new Set(); + var emptySet = new Set(); // eslint-disable-line compat/compat expect(notEmpty.asymmetricMatch(filledSet)).toBe(true); expect(notEmpty.asymmetricMatch(emptySet)).toBe(false); @@ -47,7 +47,7 @@ describe("NotEmpty", function () { jasmine.getEnv().requireFunctioningTypedArrays(); var notEmpty = new jasmineUnderTest.NotEmpty(); - expect(notEmpty.asymmetricMatch(new Int16Array([1,2,3]))).toBe(true); - expect(notEmpty.asymmetricMatch(new Int16Array())).toBe(false); + expect(notEmpty.asymmetricMatch(new Int16Array([1,2,3]))).toBe(true); // eslint-disable-line compat/compat + expect(notEmpty.asymmetricMatch(new Int16Array())).toBe(false); // eslint-disable-line compat/compat }); }); diff --git a/spec/core/asymmetric_equality/ObjectContainingSpec.js b/spec/core/asymmetric_equality/ObjectContainingSpec.js index 8cad948d..97df2555 100644 --- a/spec/core/asymmetric_equality/ObjectContainingSpec.js +++ b/spec/core/asymmetric_equality/ObjectContainingSpec.js @@ -149,7 +149,7 @@ describe("ObjectContaining", function() { }); }); - it("includes keys that are present in only sample", function() { + it("includes keys that are present only in sample", function() { var sample = {a: 1, b: 2}, other = {a: 3}, containing = new jasmineUnderTest.ObjectContaining(sample), diff --git a/spec/core/asymmetric_equality/SetContainingSpec.js b/spec/core/asymmetric_equality/SetContainingSpec.js index 50ee9d5b..3f7b1b16 100644 --- a/spec/core/asymmetric_equality/SetContainingSpec.js +++ b/spec/core/asymmetric_equality/SetContainingSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ describe('SetContaining', function() { function SetI(iterable) { // for IE11 var set = new Set(); diff --git a/spec/core/baseSpec.js b/spec/core/baseSpec.js index 4fce2254..fbdf9d9d 100644 --- a/spec/core/baseSpec.js +++ b/spec/core/baseSpec.js @@ -51,4 +51,15 @@ describe('base helpers', function() { expect(jasmineUnderTest.isAsymmetricEqualityTester_(obj)).toBe(true); }); }); + + describe('isSet', function() { + it('returns true when the object is a Set', function() { + jasmine.getEnv().requireFunctioningSets(); + expect(jasmineUnderTest.isSet(new Set())).toBe(true); // eslint-disable-line compat/compat + }); + + it('returns false when the object is not a Set', function() { + expect(jasmineUnderTest.isSet({})).toBe(false); + }); + }); }); diff --git a/spec/core/integration/AsymmetricEqualityTestersSpec.js b/spec/core/integration/AsymmetricEqualityTestersSpec.js index 1ccf2801..518c5bde 100644 --- a/spec/core/integration/AsymmetricEqualityTestersSpec.js +++ b/spec/core/integration/AsymmetricEqualityTestersSpec.js @@ -19,8 +19,8 @@ describe('Asymmetric equality testers (Integration)', function () { .toBeUndefined(); }; - env.addReporter({specDone: specExpectations, jasmineDone: done}); - env.execute(); + env.addReporter({specDone: specExpectations}); + env.execute(null, done); }); } @@ -43,8 +43,8 @@ describe('Asymmetric equality testers (Integration)', function () { .not.toEqual(''); }; - env.addReporter({specDone: specExpectations, jasmineDone: done}); - env.execute(); + env.addReporter({specDone: specExpectations}); + env.execute(null, done); }); } diff --git a/spec/core/integration/CustomAsyncMatchersSpec.js b/spec/core/integration/CustomAsyncMatchersSpec.js index 174ddd90..05ed9b3b 100644 --- a/spec/core/integration/CustomAsyncMatchersSpec.js +++ b/spec/core/integration/CustomAsyncMatchersSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ describe('Custom Async Matchers (Integration)', function() { var env; @@ -27,8 +28,8 @@ describe('Custom Async Matchers (Integration)', function() { expect(result.status).toEqual('passed'); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it('uses the negative compare function for a negative comparison, if provided', function(done) { @@ -51,8 +52,8 @@ describe('Custom Async Matchers (Integration)', function() { expect(result.status).toEqual('passed'); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it('generates messages with the same rules as built in matchers absent a custom message', function(done) { @@ -76,8 +77,8 @@ describe('Custom Async Matchers (Integration)', function() { expect(result.failedExpectations[0].message).toEqual("Expected 'a' to be real."); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it("passes the jasmine utility to the matcher factory", function (done) { @@ -106,8 +107,8 @@ describe('Custom Async Matchers (Integration)', function() { ); }; - env.addReporter({specDone: specExpectations, jasmineDone: done}); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); // TODO: remove this in the next major release. @@ -177,8 +178,8 @@ describe('Custom Async Matchers (Integration)', function() { expect(result.failedExpectations).toEqual([]); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it('logs a deprecation once per matcher if the matcher factory takes two arguments', function (done) { diff --git a/spec/core/integration/CustomMatchersSpec.js b/spec/core/integration/CustomMatchersSpec.js index 65ea9f00..35e252ba 100644 --- a/spec/core/integration/CustomMatchersSpec.js +++ b/spec/core/integration/CustomMatchersSpec.js @@ -37,9 +37,9 @@ describe("Custom Matchers (Integration)", function () { expect(firstSpecResult.failedExpectations[0].message).toEqual("matcherForSpec: actual: zzz; expected: yyy"); done(); }; - env.addReporter({ specDone:specDoneSpy, jasmineDone: expectations}); + env.addReporter({ specDone:specDoneSpy }); - env.execute(); + env.execute(null, expectations); }); it("passes the spec if the custom matcher passes", function(done) { @@ -57,8 +57,8 @@ describe("Custom Matchers (Integration)", function () { expect(result.status).toEqual('passed'); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it("passes the spec if the custom equality matcher passes for types nested inside asymmetric equality testers", function(done) { @@ -81,8 +81,8 @@ describe("Custom Matchers (Integration)", function () { expect(result.status).toEqual('passed'); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it("supports asymmetric equality testers that take a list of custom equality testers", function(done) { @@ -112,8 +112,8 @@ describe("Custom Matchers (Integration)", function () { expect(result.status).toEqual('passed'); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it("displays an appropriate failure message if a custom equality matcher fails", function(done) { @@ -139,8 +139,8 @@ describe("Custom Matchers (Integration)", function () { ); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it("uses the negative compare function for a negative comparison, if provided", function(done) { @@ -161,8 +161,8 @@ describe("Custom Matchers (Integration)", function () { expect(result.status).toEqual('passed'); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it("generates messages with the same rules as built in matchers absent a custom message", function(done) { @@ -184,8 +184,8 @@ describe("Custom Matchers (Integration)", function () { expect(result.failedExpectations[0].message).toEqual("Expected 'a' to be real."); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it("passes the expected and actual arguments to the comparison function", function(done) { @@ -209,8 +209,8 @@ describe("Custom Matchers (Integration)", function () { expect(argumentSpy).toHaveBeenCalledWith(true, "arg1", "arg2"); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); it("passes the jasmine utility to the matcher factory", function (done) { @@ -237,8 +237,8 @@ describe("Custom Matchers (Integration)", function () { ); }; - env.addReporter({specDone: specExpectations, jasmineDone: done}); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); // TODO: remove this in the next major release. @@ -306,8 +306,8 @@ describe("Custom Matchers (Integration)", function () { expect(result.failedExpectations).toEqual([]); }; - env.addReporter({specDone: specExpectations, jasmineDone: done}); - env.execute(); + env.addReporter({specDone: specExpectations}); + env.execute(null, done); }); it('logs a deprecation once per matcher if the matcher factory takes two arguments', function(done) { diff --git a/spec/core/integration/CustomObjectFormatterSpec.js b/spec/core/integration/CustomObjectFormatterSpec.js index 10ed4d71..3337f452 100644 --- a/spec/core/integration/CustomObjectFormatterSpec.js +++ b/spec/core/integration/CustomObjectFormatterSpec.js @@ -25,9 +25,9 @@ describe("Custom object formatters", function() { expect(specResults[1].failedExpectations[0].message).toEqual("Expected 42 to be undefined."); done(); }; - env.addReporter({ specDone:specDone, jasmineDone: expectations}); + env.addReporter({ specDone:specDone }); - env.execute(); + env.execute(null, expectations); }); it("scopes custom object formatters to a suite", function(done) { @@ -54,9 +54,9 @@ describe("Custom object formatters", function() { expect(specResults[1].failedExpectations[0].message).toEqual("Expected custom(42) to be undefined."); done(); }; - env.addReporter({ specDone:specDone, jasmineDone: expectations}); + env.addReporter({ specDone:specDone }); - env.execute(); + env.execute(null, expectations); }); it("throws an exception if you try to add a custom object formatter outside a runable", function() { diff --git a/spec/core/integration/CustomSpyStrategiesSpec.js b/spec/core/integration/CustomSpyStrategiesSpec.js index 7ab4d28d..e05b0773 100644 --- a/spec/core/integration/CustomSpyStrategiesSpec.js +++ b/spec/core/integration/CustomSpyStrategiesSpec.js @@ -15,6 +15,7 @@ describe('Custom Spy Strategies (Integration)', function() { .and.returnValue(42); var strategy = jasmine.createSpy('custom strategy') .and.returnValue(plan); + var jasmineDone = jasmine.createSpy('jasmineDone'); env.describe('suite defining a custom spy strategy', function() { env.beforeEach(function() { @@ -33,13 +34,14 @@ describe('Custom Spy Strategies (Integration)', function() { expect(env.createSpy('something').and.frobnicate).toBeUndefined(); }); - function jasmineDone(result) { + function expectations() { + var result = jasmineDone.calls.argsFor(0)[0]; expect(result.overallStatus).toEqual('passed'); done(); } env.addReporter({ jasmineDone: jasmineDone }); - env.execute(); + env.execute(null, expectations); }); it('allows adding more strategies local to a spec', function(done) { @@ -47,6 +49,7 @@ describe('Custom Spy Strategies (Integration)', function() { .and.returnValue(42); var strategy = jasmine.createSpy('custom strategy') .and.returnValue(plan); + var jasmineDone = jasmine.createSpy('jasmineDone'); env.it('spec defining a custom spy strategy', function() { env.addSpyStrategy('frobnicate', strategy); @@ -60,13 +63,14 @@ describe('Custom Spy Strategies (Integration)', function() { expect(env.createSpy('something').and.frobnicate).toBeUndefined(); }); - function jasmineDone(result) { + function expectations() { + var result = jasmineDone.calls.argsFor(0)[0]; expect(result.overallStatus).toEqual('passed'); done(); } env.addReporter({ jasmineDone: jasmineDone }); - env.execute(); + env.execute(null, expectations); }); it('allows using custom strategies on a per-argument basis', function(done) { @@ -74,6 +78,7 @@ describe('Custom Spy Strategies (Integration)', function() { .and.returnValue(42); var strategy = jasmine.createSpy('custom strategy') .and.returnValue(plan); + var jasmineDone = jasmine.createSpy('jasmineDone'); env.it('spec defining a custom spy strategy', function() { env.addSpyStrategy('frobnicate', strategy); @@ -91,13 +96,14 @@ describe('Custom Spy Strategies (Integration)', function() { expect(env.createSpy('something').and.frobnicate).toBeUndefined(); }); - function jasmineDone(result) { + function expectations() { + var result = jasmineDone.calls.argsFor(0)[0]; expect(result.overallStatus).toEqual('passed'); done(); } env.addReporter({ jasmineDone: jasmineDone }); - env.execute(); + env.execute(null, expectations); }); it('allows multiple custom strategies to be used', function(done) { @@ -105,7 +111,9 @@ describe('Custom Spy Strategies (Integration)', function() { strategy1 = jasmine.createSpy('strat 1').and.returnValue(plan1), plan2 = jasmine.createSpy('plan 2').and.returnValue(24), strategy2 = jasmine.createSpy('strat 2').and.returnValue(plan2), - specDone = jasmine.createSpy('specDone'); + specDone = jasmine.createSpy('specDone'), + jasmineDone = jasmine.createSpy('jasmineDone'); + env.beforeEach(function() { env.addSpyStrategy('frobnicate', strategy1); @@ -130,13 +138,14 @@ describe('Custom Spy Strategies (Integration)', function() { expect(plan2).toHaveBeenCalled(); }); - function jasmineDone(result) { + function expectations() { + var result = jasmineDone.calls.argsFor(0)[0]; expect(result.overallStatus).toEqual('passed'); expect(specDone.calls.count()).toBe(2); done(); } env.addReporter({ jasmineDone: jasmineDone, specDone: specDone }); - env.execute(); + env.execute(null, expectations); }); }); diff --git a/spec/core/integration/DefaultSpyStrategySpec.js b/spec/core/integration/DefaultSpyStrategySpec.js index 584b25b2..50a11544 100644 --- a/spec/core/integration/DefaultSpyStrategySpec.js +++ b/spec/core/integration/DefaultSpyStrategySpec.js @@ -29,13 +29,15 @@ describe('Default Spy Strategy (Integration)', function() { expect(spy()).toBeUndefined(); }); - function jasmineDone(result) { + function expectations() { + var result = jasmineDone.calls.argsFor(0)[0]; expect(result.overallStatus).toEqual('passed'); done(); } + var jasmineDone = jasmine.createSpy('jasmineDone'); env.addReporter({ jasmineDone: jasmineDone }); - env.execute(); + env.execute(null, expectations); }); it('uses the default spy strategy defined when the spy is created', function (done) { @@ -61,12 +63,14 @@ describe('Default Spy Strategy (Integration)', function() { expect(d.and.isConfigured()).toBe(false); }); - function jasmineDone(result) { + function expectations() { + var result = jasmineDone.calls.argsFor(0)[0]; expect(result.overallStatus).toEqual('passed'); done(); } + var jasmineDone = jasmine.createSpy('jasmineDone'); env.addReporter({ jasmineDone: jasmineDone }); - env.execute(); + env.execute(null, expectations); }); }); diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index e032120a..a03aad84 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -22,7 +22,6 @@ describe("Env integration", function() { done(); }; - env.addReporter({ jasmineDone: assertions}); env.configure({random: false}); env.describe("A Suite", function() { @@ -34,7 +33,7 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); }); it("Nested Suites execute as expected", function(done) { @@ -50,7 +49,6 @@ describe("Env integration", function() { done(); }; - env.addReporter({ jasmineDone: assertions }); env.configure({random: false}); env.describe("Outer suite", function() { @@ -67,7 +65,7 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); }); it("Multiple top-level Suites execute as expected", function(done) { @@ -84,7 +82,6 @@ describe("Env integration", function() { done(); }; - env.addReporter({ jasmineDone: assertions }); env.configure({random: false}); @@ -108,57 +105,13 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); }); it('explicitly fails a spec', function(done) { var specDone = jasmine.createSpy('specDone'); - env.addReporter({ - specDone: specDone, - jasmineDone: function() { - expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ - description: 'has a default message', - failedExpectations: [jasmine.objectContaining({ - message: 'Failed' - })] - })); - expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ - description: 'specifies a message', - failedExpectations: [jasmine.objectContaining({ - message: 'Failed: messy message' - })] - })); - expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ - description: 'has a message and stack trace from an Error', - failedExpectations: [jasmine.objectContaining({ - message: 'Failed: error message', - stack: { - asymmetricMatch: function(other) { - if (!other) { - // IE doesn't give us a stacktrace so just ignore it. - return true; - } - var split = other.split('\n'), - firstLine = split[0]; - if (firstLine.indexOf('error message') >= 0) { - // Chrome inserts the message and a newline before the first stacktrace line. - firstLine = split[1]; - } - return firstLine.indexOf('EnvSpec') >= 0; - } - } - })] - })); - expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ - description: 'pretty prints objects', - failedExpectations: [jasmine.objectContaining({ - message: 'Failed: Object({ prop: \'value\', arr: [ \'works\', true ] })' - })] - })); - done(); - } - }); + env.addReporter({specDone: specDone}); env.describe('failing', function() { env.it('has a default message', function() { @@ -178,15 +131,51 @@ describe("Env integration", function() { }) }); - env.execute(); + env.execute(null, function() { + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'has a default message', + failedExpectations: [jasmine.objectContaining({ + message: 'Failed' + })] + })); + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'specifies a message', + failedExpectations: [jasmine.objectContaining({ + message: 'Failed: messy message' + })] + })); + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'has a message and stack trace from an Error', + failedExpectations: [jasmine.objectContaining({ + message: 'Failed: error message', + stack: { + asymmetricMatch: function(other) { + if (!other) { + // IE doesn't give us a stacktrace so just ignore it. + return true; + } + var split = other.split('\n'), + firstLine = split[0]; + if (firstLine.indexOf('error message') >= 0) { + // Chrome inserts the message and a newline before the first stacktrace line. + firstLine = split[1]; + } + return firstLine.indexOf('EnvSpec') >= 0; + } + } + })] + })); + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'pretty prints objects', + failedExpectations: [jasmine.objectContaining({ + message: 'Failed: Object({ prop: \'value\', arr: [ \'works\', true ] })' + })] + })); + done(); + }); }); it("produces an understandable error message when 'fail' is used outside of a current spec", function(done) { - var reporter = jasmine.createSpyObj('fakeReporter', ['jasmineDone']); - - reporter.jasmineDone.and.callFake(done); - env.addReporter(reporter); - env.describe("A Suite", function() { env.it("an async spec that is actually synchronous", function(underTestCallback) { underTestCallback(); @@ -194,12 +183,11 @@ describe("Env integration", function() { expect(function() { env.fail(); }).toThrowError(/'fail' was used when there was no current spec/); }); - env.execute(); + env.execute(null, done); }); it("calls associated befores/specs/afters with the same 'this'", function(done) { - env.addReporter({jasmineDone: done}); env.configure({random: false}); env.describe("tests", function() { var firstTimeThrough = true, firstSpecContext, secondSpecContext; @@ -231,12 +219,10 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, done); }); it("calls associated befores/its/afters with the same 'this' for an async spec", function(done) { - env.addReporter({jasmineDone: done}); - env.describe("with an async spec", function() { var specContext; @@ -255,22 +241,13 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, done); }); it("calls associated beforeAlls/afterAlls only once per suite", function(done) { var before = jasmine.createSpy('beforeAll'), after = jasmine.createSpy('afterAll'); - env.addReporter({ - jasmineDone: function() { - expect(after).toHaveBeenCalled(); - expect(after.calls.count()).toBe(1); - expect(before.calls.count()).toBe(1); - done(); - } - }); - env.describe("with beforeAll and afterAll", function() { env.it("spec", function() { expect(before).toHaveBeenCalled(); @@ -286,22 +263,18 @@ describe("Env integration", function() { env.afterAll(after); }); - env.execute(); + env.execute(null, function() { + expect(after).toHaveBeenCalled(); + expect(after.calls.count()).toBe(1); + expect(before.calls.count()).toBe(1); + done(); + }); }); it("calls associated beforeAlls/afterAlls only once per suite for async", function(done) { var before = jasmine.createSpy('beforeAll'), after = jasmine.createSpy('afterAll'); - env.addReporter({ - jasmineDone: function() { - expect(after).toHaveBeenCalled(); - expect(after.calls.count()).toBe(1); - expect(before.calls.count()).toBe(1); - done(); - } - }); - env.describe("with beforeAll and afterAll", function() { env.it("spec", function() { expect(before).toHaveBeenCalled(); @@ -324,12 +297,15 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, function() { + expect(after).toHaveBeenCalled(); + expect(after.calls.count()).toBe(1); + expect(before.calls.count()).toBe(1); + done(); + }); }); it("calls associated beforeAlls/afterAlls with the cascaded 'this'", function(done) { - env.addReporter({jasmineDone: done}); - env.describe("with beforeAll and afterAll", function() { env.beforeAll(function() { this.x = 1; @@ -376,14 +352,11 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, done); }); it("tags top-level afterAll failures with a type", function(done) { - env.addReporter({jasmineDone: function(result) { - expect(result.failedExpectations[0].globalErrorType).toEqual('afterAll'); - done(); - }}); + var jasmineDone = jasmine.createSpy('jasmineDone'); env.it('has a spec', function() {}); @@ -391,20 +364,22 @@ describe("Env integration", function() { throw 'nope'; }); - env.execute(); + env.addReporter({ jasmineDone: jasmineDone }); + env.execute(null, function() {{ + var result = jasmineDone.calls.argsFor(0)[0]; + expect(result.failedExpectations[0].globalErrorType).toEqual('afterAll'); + done(); + }}); }); it("does not tag suite afterAll failures with a type", function(done) { var reporter = { - jasmineDone: function() { - expect(reporter.suiteDone).toHaveBeenCalled(); - done(); - }, suiteDone: jasmine.createSpy('suiteDone').and.callFake(function(result) { expect(result.failedExpectations[0].globalErrorType).toBeFalsy(); }) }; + env.addReporter(reporter); env.describe('a suite', function() { @@ -415,20 +390,23 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, function() { + expect(reporter.suiteDone).toHaveBeenCalled(); + done(); + }); }); it("when the beforeAll fails, error at suite level", function (done) { - var reporter = jasmine.createSpyObj('fakeReporter', [ "specDone", "suiteDone", "jasmineDone" ]); + var reporter = jasmine.createSpyObj('fakeReporter', [ "specDone", "suiteDone" ]); - reporter.jasmineDone.and.callFake(function() { + var assertions = function() { expect(reporter.specDone.calls.count()).toEqual(2); expect(reporter.specDone).toHaveFailedExpectationsForRunnable('A suite spec that will pass', []); expect(reporter.specDone).toHaveFailedExpectationsForRunnable('A suite nesting another spec to pass', []); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('A suite', ['Expected 1 to be 2.']); done(); - }); + }; env.addReporter(reporter); @@ -446,7 +424,7 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); }); it("copes with async failures after done has been called", function(done) { @@ -457,13 +435,13 @@ describe("Env integration", function() { spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); env.cleanup_(); env = new jasmineUnderTest.Env(); - var reporter = jasmine.createSpyObj('fakeReporter', [ "specDone", "jasmineDone", "suiteDone" ]); + var reporter = jasmine.createSpyObj('fakeReporter', [ "specDone", "suiteDone" ]); - reporter.jasmineDone.and.callFake(function() { + var assertions = function() { expect(reporter.specDone).not.toHaveFailedExpectationsForRunnable('A suite fails', ['fail thrown']); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('A suite', ['fail thrown']); done(); - }); + }; env.addReporter(reporter); @@ -484,20 +462,20 @@ describe("Env integration", function() { env.it('is not run', function() {}); }); - env.execute(); + env.execute(null, assertions); }); describe('suiteDone reporting', function(){ it("reports when an afterAll fails an expectation", function(done) { - var reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone','suiteDone']); + var reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); - reporter.jasmineDone.and.callFake(function() { + var assertions = function() { expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('my suite', [ 'Expected 1 to equal 2.', 'Expected 2 to equal 3.' ]); done(); - }); + }; env.addReporter(reporter); @@ -511,19 +489,19 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); }); it("if there are no specs, it still reports correctly", function(done) { - var reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone','suiteDone']); + var reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); - reporter.jasmineDone.and.callFake(function() { + var assertions = function() { expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('outer suite', [ 'Expected 1 to equal 2.', 'Expected 2 to equal 3.' ]); done(); - }); + }; env.addReporter(reporter); @@ -538,19 +516,19 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); }); it("reports when afterAll throws an exception", function(done) { var error = new Error('After All Exception'), - reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone','suiteDone']); + reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); - reporter.jasmineDone.and.callFake(function() { + var assertions = function() { expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('my suite', [ (/^Error: After All Exception/) ]); done(); - }); + }; env.addReporter(reporter); @@ -563,18 +541,18 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); }); it("reports when an async afterAll fails an expectation", function(done) { - var reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone','suiteDone']); + var reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); - reporter.jasmineDone.and.callFake(function() { + var assertions = function() { expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('my suite', [ 'Expected 1 to equal 2.' ]); done(); - }); + }; env.addReporter(reporter); @@ -588,20 +566,20 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); }); it("reports when an async afterAll throws an exception", function(done) { var error = new Error('After All Exception'), - reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone','suiteDone']); + reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); - reporter.jasmineDone.and.callFake(function() { + var assertions = function() { expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('my suite', [ (/^Error: After All Exception/) ]); done(); - }); + }; env.addReporter(reporter); @@ -614,19 +592,63 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, assertions); + }); + + it('reports the duration of the suite', function(done) { + var duration; + + env.addReporter({ + suiteDone: function(result) { + expect(duration).toBeUndefined(); + duration = result.duration; + } + }); + + env.describe('my suite', function() { + env.it('takes time', function(done) { + // We can't just use the mock clock here because the timer is designed + // to record real time even when the mock clock is installed. + setTimeout(done, 10); + }) + }); + + env.execute(null, function() { + expect(duration).toBeGreaterThanOrEqual(10); + done(); + }); + }); + }); + + describe('specDone reporting', function() { + it('reports the duration of the spec', function(done) { + var duration; + + env.addReporter({ + specDone: function(result) { + expect(duration).toBeUndefined(); + duration = result.duration; + } + }); + + env.describe('my suite', function() { + env.it('takes time', function(done) { + // We can't just use the mock clock here because the timer is designed + // to record real time even when the mock clock is installed. + setTimeout(done, 10); + }) + }); + + env.execute(null, function() { + expect(duration).toBeGreaterThanOrEqual(10); + done(); + }); }); }); it('reports expectation failures in global beforeAll', function(done) { var reporter = jasmine.createSpyObj(['specDone', 'jasmineDone']); - reporter.jasmineDone.and.callFake(function(results) { - expect(results.failedExpectations).toEqual([jasmine.objectContaining({ message: 'Expected 1 to be 0.' })]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('is a spec', []); - done(); - }); - env.beforeAll(function() { env.expect(1).toBe(0); }); @@ -637,17 +659,17 @@ describe("Env integration", function() { env.addReporter(reporter); - env.execute(); + env.execute(null, function() { + var results = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(results.failedExpectations).toEqual([jasmine.objectContaining({ message: 'Expected 1 to be 0.' })]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('is a spec', []); + done(); + }); }); it('reports expectation failures in global afterAll', function(done) { var reporter = jasmine.createSpyObj(['jasmineDone']); - reporter.jasmineDone.and.callFake(function(results) { - expect(results.failedExpectations).toEqual([jasmine.objectContaining({ message: 'Expected 1 to be 0.' })]); - done(); - }); - env.afterAll(function() { env.expect(1).toBe(0); }); @@ -658,7 +680,11 @@ describe("Env integration", function() { env.addReporter(reporter); - env.execute(); + env.execute(null, function() { + var results = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(results.failedExpectations).toEqual([jasmine.objectContaining({ message: 'Expected 1 to be 0.' })]); + done(); + }); }); it("Allows specifying which specs and suites to run", function(done) { @@ -667,16 +693,7 @@ describe("Env integration", function() { firstSpec, secondSuite; - var assertions = function() { - expect(calls).toEqual([ - 'third spec', - 'first spec' - ]); - expect(suiteCallback).toHaveBeenCalled(); - done(); - }; - - env.addReporter({jasmineDone: assertions, suiteDone: suiteCallback}); + env.addReporter({ suiteDone: suiteCallback}); env.describe("first suite", function() { firstSpec = env.it("first spec", function() { @@ -693,7 +710,14 @@ describe("Env integration", function() { }); }); - env.execute([secondSuite.id, firstSpec.id]); + env.execute([secondSuite.id, firstSpec.id], function() { + expect(calls).toEqual([ + 'third spec', + 'first spec' + ]); + expect(suiteCallback).toHaveBeenCalled(); + done(); + }); }); it('runs before and after all functions for runnables provided to .execute()', function(done) { @@ -701,18 +725,6 @@ describe("Env integration", function() { first_spec, second_spec; - var assertions = function() { - expect(calls).toEqual([ - "before", - "first spec", - "second spec", - "after" - ]); - done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.describe("first suite", function() { env.beforeAll(function() { calls.push("before"); @@ -728,7 +740,15 @@ describe("Env integration", function() { }); }); - env.execute([first_spec.id, second_spec.id]); + env.execute([first_spec.id, second_spec.id], function() { + expect(calls).toEqual([ + "before", + "first spec", + "second spec", + "after" + ]); + done(); + }); }); it("Allows filtering out specs and suites to run programmatically", function(done) { @@ -737,17 +757,7 @@ describe("Env integration", function() { firstSpec, secondSuite; - var assertions = function() { - expect(calls.length).toEqual(2); - expect(calls).toEqual(jasmine.arrayContaining([ - 'first spec', - 'second spec' - ])); - expect(suiteCallback).toHaveBeenCalled(); - done(); - }; - - env.addReporter({jasmineDone: assertions, suiteDone: suiteCallback}); + env.addReporter({suiteDone: suiteCallback}); env.describe("first suite", function() { env.it("first spec", function() { @@ -770,7 +780,15 @@ describe("Env integration", function() { } }); - env.execute(); + env.execute(null, function() { + expect(calls.length).toEqual(2); + expect(calls).toEqual(jasmine.arrayContaining([ + 'first spec', + 'second spec' + ])); + expect(suiteCallback).toHaveBeenCalled(); + done(); + }); }); it("Functions can be spied on and have their calls tracked", function (done) { @@ -782,8 +800,6 @@ describe("Env integration", function() { } }; - env.addReporter({jasmineDone: done}); - env.it("works with spies", function() { var spy = env.spyOn(subject, 'spiedFunc').and.returnValue("stubbed result"); @@ -833,7 +849,7 @@ describe("Env integration", function() { }).toThrowError('You must use the new keyword.'); }); - env.execute(); + env.execute(null, done); }); it('can be configured to allow respying on functions', function (done) { @@ -844,7 +860,6 @@ describe("Env integration", function() { }; env.allowRespy(true); - env.addReporter({ jasmineDone: done }); env.describe('test suite', function() { env.it('spec 0', function() { @@ -858,7 +873,7 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, done); }); it('removes all spies added in a spec after the spec is complete', function(done) { @@ -877,15 +892,11 @@ describe("Env integration", function() { env.it('spec 1', secondSpec); }); - var assertions = function() { + env.execute(null, function() { expect(firstSpec).toHaveBeenCalled(); expect(secondSpec).toHaveBeenCalled(); done(); - }; - - env.addReporter({ jasmineDone: assertions }); - - env.execute(); + }); }); it('removes all spies added in a suite after the suite is complete', function(done) { @@ -912,9 +923,7 @@ describe("Env integration", function() { }); }); - env.addReporter({ jasmineDone: done }); - - env.execute(); + env.execute(null, done); }); it('removes a spy from the top suite after the run is complete', function(done) { @@ -931,14 +940,10 @@ describe("Env integration", function() { expect(jasmineUnderTest.isSpy(testObj.foo)).toBe(true); }); - env.addReporter({ - jasmineDone: function() { - expect(jasmineUnderTest.isSpy(testObj.foo)).toBe(false); - done(); - } + env.execute(null, function() { + expect(jasmineUnderTest.isSpy(testObj.foo)).toBe(false); + done(); }); - - env.execute(); }); it("Mock clock can be installed and used in tests", function(done) { @@ -949,14 +954,6 @@ describe("Env integration", function() { env.cleanup_(); env = new jasmineUnderTest.Env({global: { setTimeout: globalSetTimeout, clearTimeout: clearTimeout, setImmediate: function(cb) { setTimeout(cb, 0); } }}); - var assertions = function() { - expect(delayedFunctionForMockClock).toHaveBeenCalled(); - expect(globalSetTimeout).toHaveBeenCalledWith(delayedFunctionForGlobalClock, 100); - - done(); - }; - - env.addReporter({ jasmineDone: assertions }); env.configure({random: false}); env.describe("tests", function() { @@ -974,17 +971,15 @@ describe("Env integration", function() { expect(globalSetTimeout).not.toHaveBeenCalled(); expect(delayedFunctionForMockClock).not.toHaveBeenCalled(); - env.execute(); + env.execute(null, function() { + expect(delayedFunctionForMockClock).toHaveBeenCalled(); + expect(globalSetTimeout).toHaveBeenCalledWith(delayedFunctionForGlobalClock, 100); + done(); + }); }); it("should run async specs in order, waiting for them to complete", function(done) { - var reporter = jasmine.createSpyObj('reporter', ['jasmineDone']), - mutatedVar; - - reporter.jasmineDone.and.callFake(function() { - done(); - }); - env.addReporter(reporter); + var mutatedVar; env.describe("tests", function() { env.beforeEach(function() { @@ -1003,7 +998,7 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, done); }); describe("with a mock clock", function() { @@ -1016,9 +1011,9 @@ describe("Env integration", function() { setTimeout: function(cb, t) { var stack = jasmine.util.errorWithStack().stack; if (stack.indexOf('ClearStack') >= 0) { - realSetTimeout(cb, t); + return realSetTimeout(cb, t); } else { - setTimeout(cb, t); + return setTimeout(cb, t); } }, clearTimeout: clearTimeout, @@ -1043,7 +1038,7 @@ describe("Env integration", function() { it("should wait a default interval before failing specs that haven't called done yet", function(done) { createMockedEnv(); - var reporter = jasmine.createSpyObj('fakeReporter', [ "specDone", "jasmineDone" ]); + var reporter = jasmine.createSpyObj('fakeReporter', [ "specDone" ]); reporter.specDone.and.callFake(function(result) { expect(result).toEqual(jasmine.objectContaining({status: 'failed'})); @@ -1052,12 +1047,6 @@ describe("Env integration", function() { }, 0); }); - reporter.jasmineDone.and.callFake(function() { - expect(reporter.specDone.calls.count()).toEqual(1); - jasmine.clock().tick(1); - realSetTimeout(done); - }); - env.addReporter(reporter); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 8414; @@ -1067,12 +1056,16 @@ describe("Env integration", function() { jasmine.clock().tick(1); }); - env.execute(); + env.execute(null, function() { + expect(reporter.specDone.calls.count()).toEqual(1); + jasmine.clock().tick(1); + realSetTimeout(done); + }); }); it("should not use the mock clock for asynchronous timeouts", function(done){ createMockedEnv(); - var reporter = jasmine.createSpyObj('fakeReporter', [ "specDone", "jasmineDone" ]), + var reporter = jasmine.createSpyObj('fakeReporter', [ "specDone" ]), clock = env.clock; reporter.specDone.and.callFake(function() { @@ -1081,13 +1074,6 @@ describe("Env integration", function() { }, 0); }); - reporter.jasmineDone.and.callFake(function() { - expect(reporter.specDone).toHaveBeenCalledTimes(1); - expect(reporter.specDone.calls.argsFor(0)[0]).toEqual(jasmine.objectContaining({status: 'passed'})); - jasmine.clock().tick(1); - realSetTimeout(done); - }); - env.addReporter(reporter); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 5; @@ -1105,25 +1091,18 @@ describe("Env integration", function() { realSetTimeout(innerDone); }); - env.execute(); + env.execute(null, function() { + expect(reporter.specDone).toHaveBeenCalledTimes(1); + expect(reporter.specDone.calls.argsFor(0)[0]).toEqual(jasmine.objectContaining({status: 'passed'})); + jasmine.clock().tick(1); + realSetTimeout(done); + }); }); it('should wait a custom interval before reporting async functions that fail to complete', function(done) { createMockedEnv(); var reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(r) { - expect(r.failedExpectations).toEqual([]); - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite beforeAll', [ /^Error: Timeout - Async function did not complete within 5000ms \(custom timeout\)/ ]); - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite afterAll', [ /^Error: Timeout - Async function did not complete within 2000ms \(custom timeout\)/ ]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite beforeEach times out', [/^Error: Timeout - Async function did not complete within 1000ms \(custom timeout\)/]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite afterEach times out', [ /^Error: Timeout - Async function did not complete within 4000ms \(custom timeout\)/ ]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite it times out', [ /^Error: Timeout - Async function did not complete within 6000ms \(custom timeout\)/ ]); - - jasmine.clock().tick(1); - realSetTimeout(done); - }); - env.addReporter(reporter); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 10000; @@ -1204,35 +1183,25 @@ describe("Env integration", function() { }, 6000); }); - env.execute(); + env.execute(null, function() { + var r = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(r.failedExpectations).toEqual([]); + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite beforeAll', [ /^Error: Timeout - Async function did not complete within 5000ms \(custom timeout\)/ ]); + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('suite afterAll', [ /^Error: Timeout - Async function did not complete within 2000ms \(custom timeout\)/ ]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite beforeEach times out', [/^Error: Timeout - Async function did not complete within 1000ms \(custom timeout\)/]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite afterEach times out', [ /^Error: Timeout - Async function did not complete within 4000ms \(custom timeout\)/ ]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite it times out', [ /^Error: Timeout - Async function did not complete within 6000ms \(custom timeout\)/ ]); + + jasmine.clock().tick(1); + realSetTimeout(done); + }); }); }); it('explicitly fails an async spec', function(done) { var specDone = jasmine.createSpy('specDone'); - env.addReporter({ - specDone: specDone, - jasmineDone: function() { - expect(specDone).toHaveFailedExpectationsForRunnable('failing has a default message', - ['Failed'] - ); - expect(specDone).toHaveFailedExpectationsForRunnable('failing specifies a message', - ['Failed: messy message'] - ); - expect(specDone).toHaveFailedExpectationsForRunnable('failing fails via the done callback', - ['Failed: done failed'] - ); - expect(specDone).toHaveFailedExpectationsForRunnable('failing has a message from an Error', - ['Failed: error message'] - ); - expect(specDone).toHaveFailedExpectationsForRunnable('failing has a message from an Error to done', - ['Failed: done error'] - ); - - setTimeout(done); - } - }); + env.addReporter({specDone: specDone}); env.describe('failing', function() { env.it('has a default message', function(innerDone) { @@ -1269,20 +1238,31 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, function() { + expect(specDone).toHaveFailedExpectationsForRunnable('failing has a default message', + ['Failed'] + ); + expect(specDone).toHaveFailedExpectationsForRunnable('failing specifies a message', + ['Failed: messy message'] + ); + expect(specDone).toHaveFailedExpectationsForRunnable('failing fails via the done callback', + ['Failed: done failed'] + ); + expect(specDone).toHaveFailedExpectationsForRunnable('failing has a message from an Error', + ['Failed: error message'] + ); + expect(specDone).toHaveFailedExpectationsForRunnable('failing has a message from an Error to done', + ['Failed: done error'] + ); + + setTimeout(done); + }); }); describe('focused tests', function() { it('should only run the focused tests', function(done) { var calls = []; - var assertions = function() { - expect(calls).toEqual(['focused']); - done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.describe('a suite', function() { env.fit('is focused', function() { calls.push('focused'); @@ -1293,19 +1273,15 @@ describe("Env integration", function() { }) }); - env.execute(); + env.execute(null, function() { + expect(calls).toEqual(['focused']); + done(); + }); }); it('should only run focused suites', function(done){ var calls = []; - var assertions = function() { - expect(calls).toEqual(['focused']); - done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.fdescribe('a focused suite', function() { env.it('is focused', function() { calls.push('focused'); @@ -1318,20 +1294,30 @@ describe("Env integration", function() { }) }); - env.execute(); + env.execute(null, function() { + expect(calls).toEqual(['focused']); + done(); + }); }); it('should run focused tests inside an xdescribe', function(done) { var reporter = jasmine.createSpyObj('fakeReporter', [ "jasmineStarted", - "jasmineDone", "suiteStarted", "suiteDone", "specStarted", "specDone" ]); - reporter.jasmineDone.and.callFake(function() { + env.addReporter(reporter); + + env.xdescribe("xd suite", function() { + env.fit("with a fit spec", function() { + env.expect(true).toBe(false); + }); + }); + + env.execute(null, function() { expect(reporter.jasmineStarted).toHaveBeenCalledWith({ totalSpecsDefined: 1, order: jasmine.any(jasmineUnderTest.Order) @@ -1344,29 +1330,28 @@ describe("Env integration", function() { done(); }); - - env.addReporter(reporter); - - env.xdescribe("xd suite", function() { - env.fit("with a fit spec", function() { - env.expect(true).toBe(false); - }); - }); - - env.execute(); }); it('should run focused suites inside an xdescribe', function(done) { var reporter = jasmine.createSpyObj('fakeReporter', [ "jasmineStarted", - "jasmineDone", "suiteStarted", "suiteDone", "specStarted", "specDone" ]); - reporter.jasmineDone.and.callFake(function() { + env.addReporter(reporter); + + env.xdescribe("xd suite", function() { + env.fdescribe("fd suite", function() { + env.it("with a spec", function() { + env.expect(true).toBe(false); + }); + }); + }); + + env.execute(null, function() { expect(reporter.jasmineStarted).toHaveBeenCalledWith({ totalSpecsDefined: 1, order: jasmine.any(jasmineUnderTest.Order) @@ -1379,32 +1364,42 @@ describe("Env integration", function() { done(); }); - - env.addReporter(reporter); - - env.xdescribe("xd suite", function() { - env.fdescribe("fd suite", function() { - env.it("with a spec", function() { - env.expect(true).toBe(false); - }); - }); - }); - - env.execute(); }); }); it("should report as expected", function(done) { var reporter = jasmine.createSpyObj('fakeReporter', [ "jasmineStarted", - "jasmineDone", "suiteStarted", "suiteDone", "specStarted", "specDone" ]); - reporter.jasmineDone.and.callFake(function() { + env.addReporter(reporter); + + env.describe("A Suite", function() { + env.it("with a top level spec", function() { + env.expect(true).toBe(true); + }); + env.describe("with a nested suite", function() { + env.xit("with an x'ed spec", function() { + env.expect(true).toBe(true); + }); + env.it("with a spec", function() { + env.expect(true).toBe(false); + }); + }); + + env.describe('with only non-executable specs', function() { + env.it('is pending'); + env.xit('is xed', function() { + env.expect(true).toBe(true); + }); + }); + }); + + env.execute(null, function() { expect(reporter.jasmineStarted).toHaveBeenCalledWith({ totalSpecsDefined: 5, order: jasmine.any(jasmineUnderTest.Order) @@ -1440,31 +1435,6 @@ describe("Env integration", function() { done(); }); - - env.addReporter(reporter); - - env.describe("A Suite", function() { - env.it("with a top level spec", function() { - env.expect(true).toBe(true); - }); - env.describe("with a nested suite", function() { - env.xit("with an x'ed spec", function() { - env.expect(true).toBe(true); - }); - env.it("with a spec", function() { - env.expect(true).toBe(false); - }); - }); - - env.describe('with only non-executable specs', function() { - env.it('is pending'); - env.xit('is xed', function() { - env.expect(true).toBe(true); - }); - }); - }); - - env.execute(); }); it("should report the random seed at the beginning and end of execution", function(done) { @@ -1478,36 +1448,23 @@ describe("Env integration", function() { ]); env.configure({random: true, seed: '123456'}); - reporter.jasmineDone.and.callFake(function(doneArg) { + env.addReporter(reporter); + env.configure({random: true}); + env.execute(null, function() { expect(reporter.jasmineStarted).toHaveBeenCalled(); var startedArg = reporter.jasmineStarted.calls.argsFor(0)[0]; expect(startedArg.order.random).toEqual(true); expect(startedArg.order.seed).toEqual('123456'); + var doneArg = reporter.jasmineDone.calls.argsFor(0)[0]; expect(doneArg.order.random).toEqual(true); expect(doneArg.order.seed).toEqual('123456'); done(); }); - - env.addReporter(reporter); - env.configure({random: true}); - env.execute(); }); it('should report pending spec messages', function(done) { - var reporter = jasmine.createSpyObj('fakeReporter', [ - 'specDone', - 'jasmineDone' - ]); - - reporter.jasmineDone.and.callFake(function() { - var specStatus = reporter.specDone.calls.argsFor(0)[0]; - - expect(specStatus.status).toBe('pending'); - expect(specStatus.pendingReason).toBe('with a message'); - - done(); - }); + var reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); env.addReporter(reporter); @@ -1515,7 +1472,14 @@ describe("Env integration", function() { env.pending('with a message'); }); - env.execute(); + env.execute(null, function() { + var specStatus = reporter.specDone.calls.argsFor(0)[0]; + + expect(specStatus.status).toBe('pending'); + expect(specStatus.pendingReason).toBe('with a message'); + + done(); + }); }); it('should report pending spec messages from promise-returning functions', function(done) { @@ -1531,19 +1495,7 @@ describe("Env integration", function() { reject(this.exception); }; - var reporter = jasmine.createSpyObj('fakeReporter', [ - 'specDone', - 'jasmineDone' - ]); - - reporter.jasmineDone.and.callFake(function() { - var specStatus = reporter.specDone.calls.argsFor(0)[0]; - - expect(specStatus.status).toBe('pending'); - expect(specStatus.pendingReason).toBe('with a message'); - - done(); - }); + var reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); env.addReporter(reporter); @@ -1553,7 +1505,14 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, function() { + var specStatus = reporter.specDone.calls.argsFor(0)[0]; + + expect(specStatus.status).toBe('pending'); + expect(specStatus.pendingReason).toBe('with a message'); + + done(); + }); }); it('should report using fallback reporter', function(done) { @@ -1580,26 +1539,12 @@ describe("Env integration", function() { it('should report xdescribes as expected', function(done) { var reporter = jasmine.createSpyObj('fakeReporter', [ "jasmineStarted", - "jasmineDone", "suiteStarted", "suiteDone", "specStarted", "specDone" ]); - reporter.jasmineDone.and.callFake(function() { - expect(reporter.jasmineStarted).toHaveBeenCalledWith({ - totalSpecsDefined: 1, - order: jasmine.any(jasmineUnderTest.Order) - }); - - expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({ status: 'pending' })); - expect(reporter.suiteDone).toHaveBeenCalledWith(jasmine.objectContaining({ description: 'xd out', status: 'pending' })); - expect(reporter.suiteDone.calls.count()).toBe(4); - - done(); - }); - env.addReporter(reporter); env.describe("A Suite", function() { @@ -1614,7 +1559,18 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, function() { + expect(reporter.jasmineStarted).toHaveBeenCalledWith({ + totalSpecsDefined: 1, + order: jasmine.any(jasmineUnderTest.Order) + }); + + expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({ status: 'pending' })); + expect(reporter.suiteDone).toHaveBeenCalledWith(jasmine.objectContaining({ description: 'xd out', status: 'pending' })); + expect(reporter.suiteDone.calls.count()).toBe(4); + + done(); + }); }); it("should be possible to get full name from a spec", function() { @@ -1639,20 +1595,7 @@ describe("Env integration", function() { }); it("Custom equality testers should be per spec", function(done) { - var reporter = jasmine.createSpyObj('fakeReporter', [ - "jasmineDone", - "specDone" - ]); - - reporter.jasmineDone.and.callFake(function() { - var firstSpecResult = reporter.specDone.calls.first().args[0], - secondSpecResult = reporter.specDone.calls.mostRecent().args[0]; - - expect(firstSpecResult.status).toEqual("passed"); - expect(secondSpecResult.status).toEqual("failed"); - - done(); - }); + var reporter = jasmine.createSpyObj('fakeReporter', ["specDone"]); env.addReporter(reporter); env.configure({random: false}); @@ -1668,26 +1611,20 @@ describe("Env integration", function() { }); }); - env.execute(); - }); - - it("Custom equality testers should be per suite", function(done) { - var reporter = jasmine.createSpyObj('fakeReporter', [ - "jasmineDone", - "specDone" - ]); - - reporter.jasmineDone.and.callFake(function() { + env.execute(null, function() { var firstSpecResult = reporter.specDone.calls.first().args[0], - secondSpecResult = reporter.specDone.calls.argsFor(0)[0], - thirdSpecResult = reporter.specDone.calls.mostRecent().args[0]; + secondSpecResult = reporter.specDone.calls.mostRecent().args[0]; expect(firstSpecResult.status).toEqual("passed"); - expect(secondSpecResult.status).toEqual("passed"); - expect(thirdSpecResult.status).toEqual("failed"); + expect(secondSpecResult.status).toEqual("failed"); done(); }); + }); + + it("Custom equality testers should be per suite", function(done) { + var reporter = jasmine.createSpyObj('fakeReporter', ["specDone"]); + env.addReporter(reporter); env.configure({random: false}); @@ -1710,24 +1647,21 @@ describe("Env integration", function() { }); }); - env.execute(); - }); - - it("Custom equality testers for toContain should be per spec", function(done) { - var reporter = jasmine.createSpyObj('fakeReporter', [ - "jasmineDone", - "specDone" - ]); - - reporter.jasmineDone.and.callFake(function() { + env.execute(null, function() { var firstSpecResult = reporter.specDone.calls.first().args[0], - secondSpecResult = reporter.specDone.calls.mostRecent().args[0]; + secondSpecResult = reporter.specDone.calls.argsFor(0)[0], + thirdSpecResult = reporter.specDone.calls.mostRecent().args[0]; expect(firstSpecResult.status).toEqual("passed"); - expect(secondSpecResult.status).toEqual("failed"); + expect(secondSpecResult.status).toEqual("passed"); + expect(thirdSpecResult.status).toEqual("failed"); done(); }); + }); + + it("Custom equality testers for toContain should be per spec", function(done) { + var reporter = jasmine.createSpyObj('fakeReporter', ["specDone"]); env.addReporter(reporter); env.configure({random: false}); @@ -1743,15 +1677,18 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, function() { + var firstSpecResult = reporter.specDone.calls.first().args[0], + secondSpecResult = reporter.specDone.calls.mostRecent().args[0]; + + expect(firstSpecResult.status).toEqual("passed"); + expect(secondSpecResult.status).toEqual("failed"); + + done(); + }); }); it("produces an understandable error message when an 'expect' is used outside of a current spec", function(done) { - var reporter = jasmine.createSpyObj('fakeReporter', ['jasmineDone']); - - reporter.jasmineDone.and.callFake(done); - env.addReporter(reporter); - env.describe("A Suite", function() { env.it("an async spec that is actually synchronous", function(underTestCallback) { underTestCallback(); @@ -1759,26 +1696,11 @@ describe("Env integration", function() { expect(function() { env.expect('a').toEqual('a'); }).toThrowError(/'expect' was used when there was no current spec/); }); - env.execute(); + env.execute(null, done); }); it("Custom equality testers for toContain should be per suite", function(done) { - var reporter = jasmine.createSpyObj('fakeReporter', [ - "jasmineDone", - "specDone" - ]); - - reporter.jasmineDone.and.callFake(function() { - var firstSpecResult = reporter.specDone.calls.first().args[0], - secondSpecResult = reporter.specDone.calls.argsFor(1)[0], - thirdSpecResult = reporter.specDone.calls.mostRecent().args[0]; - - expect(firstSpecResult.status).toEqual("passed"); - expect(secondSpecResult.status).toEqual("passed"); - expect(thirdSpecResult.status).toEqual("failed"); - - done(); - }); + var reporter = jasmine.createSpyObj('fakeReporter', ["specDone"]); env.addReporter(reporter); env.configure({random: false}); @@ -1801,7 +1723,17 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, function() { + var firstSpecResult = reporter.specDone.calls.first().args[0], + secondSpecResult = reporter.specDone.calls.argsFor(1)[0], + thirdSpecResult = reporter.specDone.calls.mostRecent().args[0]; + + expect(firstSpecResult.status).toEqual("passed"); + expect(secondSpecResult.status).toEqual("passed"); + expect(thirdSpecResult.status).toEqual("failed"); + + done(); + }); }); it("Custom matchers should be per spec", function(done) { @@ -1820,9 +1752,7 @@ describe("Env integration", function() { }); }); - env.addReporter({jasmineDone: done}); - - env.execute(); + env.execute(null, done); }); it("Custom matchers should be per suite", function(done) { @@ -1848,9 +1778,7 @@ describe("Env integration", function() { }); }); - env.addReporter({jasmineDone: done}); - - env.execute(); + env.execute(null, done); }); it('throws an exception if you try to create a spy outside of a runnable', function (done) { @@ -1866,14 +1794,10 @@ describe("Env integration", function() { env.it('has a test', function() {}); }); - var assertions = function() { + env.execute(null, function() { expect(exception.message).toBe('Spies must be created in a before function or a spec'); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it('throws an exception if you try to add a matcher outside of a runnable', function (done) { @@ -1889,14 +1813,10 @@ describe("Env integration", function() { env.it('has a test', function() {}); }); - var assertions = function() { + env.execute(null, function() { expect(exception.message).toBe('Matchers must be added in a before function or a spec'); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it('throws an exception if you try to add a custom equality outside of a runnable', function (done) { @@ -1912,28 +1832,86 @@ describe("Env integration", function() { env.it('has a test', function() {}); }); - var assertions = function() { + env.execute(null, function() { expect(exception.message).toBe('Custom Equalities must be added in a before function or a spec'); done(); - }; + }); + }); - env.addReporter({jasmineDone: assertions}); + it('reports test properties on specs', function(done) { + var env = new jasmineUnderTest.Env(), + reporter = jasmine.createSpyObj('reporter', ['suiteDone', 'specDone']); - env.execute(); + reporter.specDone.and.callFake(function(e) { + expect(e.properties).toEqual({a: 'Bee'}); + }); + + env.addReporter(reporter); + env.it('calls setSpecProperty', function() { + env.setSpecProperty('a', 'Bee') + }); + env.execute(null, function() { + expect(reporter.specDone).toHaveBeenCalled(); + done(); + }); + }); + + it('throws an exception if you try to setSpecProperty outside of a spec', function (done) { + var env = new jasmineUnderTest.Env(), + exception; + + env.describe("a suite", function () { + env.it('a spec'); + try { + env.setSpecProperty('a prop', 'val'); + } catch(e) { + exception = e; + } + }); + + env.execute(null, function() { + expect(exception.message).toBe("'setSpecProperty' was used when there was no current spec"); + done(); + }); + }); + + it('reports test properties on suites', function(done) { + var env = new jasmineUnderTest.Env(), + reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); + + reporter.suiteDone.and.callFake(function(e) { + expect(e.properties).toEqual({b: 'Sweet'}); + }); + + env.addReporter(reporter); + env.describe('calls setSuiteProperty', function() { + env.beforeEach(function() { + env.setSuiteProperty('b', 'Sweet'); + }); + env.it('a passing spec', function() { + expect.nothing(); + }); + }); + + env.execute(null, function() { + expect(reporter.suiteDone).toHaveBeenCalled(); + done(); + }); + }); + + it('throws an exception if you try to setSuiteProperty outside of a suite', function (done) { + var env = new jasmineUnderTest.Env(); + + try { + env.setSuiteProperty('a', 'Bee'); + } catch(e) { + expect(e.message).toBe("'setSuiteProperty' was used when there was no current suite"); + done(); + } }); it("should associate errors thrown from async code with the correct runnable", function(done) { - var reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone','suiteDone','specDone']); - - reporter.jasmineDone.and.callFake(function() { - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('async suite', [ - /^(((Uncaught )?(exception: )?Error: suite( thrown)?)|(suite thrown))$/ - ]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite async spec', [ - /^(((Uncaught )?(exception: )?Error: spec( thrown)?)|(spec thrown))$/ - ]); - done(); - }); + var reporter = jasmine.createSpyObj('fakeReport', ['suiteDone','specDone']); env.addReporter(reporter); @@ -1951,31 +1929,19 @@ describe("Env integration", function() { }, 10); }); - env.execute(); + env.execute(null, function() { + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('async suite', [ + /^(((Uncaught )?(exception: )?Error: suite( thrown)?)|(suite thrown))$/ + ]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite async spec', [ + /^(((Uncaught )?(exception: )?Error: spec( thrown)?)|(spec thrown))$/ + ]); + done(); + }); }); it('should throw on suites/specs/befores/afters nested in methods other than \'describe\'', function(done) { - var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - - reporter.jasmineDone.and.callFake(function() { - var msg = /\'.*\' should only be used in \'describe\' function/; - - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite describe', [msg]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite xdescribe', [msg]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite fdescribe', [msg]); - - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec it', [msg]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec xit', [msg]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec fit', [msg]); - - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('beforeAll', [msg]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('beforeEach spec', [msg]); - - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('afterAll', [msg]); - expect(reporter.specDone).toHaveFailedExpectationsForRunnable('afterEach spec', [msg]); - - done(); - }); + var reporter = jasmine.createSpyObj('reporter', ['suiteDone', 'specDone']); env.addReporter(reporter); @@ -2011,7 +1977,25 @@ describe("Env integration", function() { env.it('spec', function() {}); }); - env.execute(); + env.execute(null, function() { + var msg = /\'.*\' should only be used in \'describe\' function/; + + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite describe', [msg]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite xdescribe', [msg]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('suite fdescribe', [msg]); + + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec it', [msg]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec xit', [msg]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec fit', [msg]); + + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('beforeAll', [msg]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('beforeEach spec', [msg]); + + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('afterAll', [msg]); + expect(reporter.specDone).toHaveFailedExpectationsForRunnable('afterEach spec', [msg]); + + done(); + }); }); it('reports errors that occur during loading', function(done) { @@ -2026,7 +2010,12 @@ describe("Env integration", function() { env = new jasmineUnderTest.Env(); var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { + env.addReporter(reporter); + global.onerror('Uncaught SyntaxError: Unexpected end of input', 'borkenSpec.js', 42, undefined, {stack: 'a stack'}); + global.onerror('Uncaught Error: ENOCHEESE'); + + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.failedExpectations).toEqual([ { passed: false, @@ -2048,12 +2037,6 @@ describe("Env integration", function() { done(); }); - - env.addReporter(reporter); - global.onerror('Uncaught SyntaxError: Unexpected end of input', 'borkenSpec.js', 42, undefined, {stack: 'a stack'}); - global.onerror('Uncaught Error: ENOCHEESE'); - - env.execute(); }); describe('If suppressLoadErrors: true was passed', function() { @@ -2074,16 +2057,15 @@ describe("Env integration", function() { env = new jasmineUnderTest.Env({suppressLoadErrors: true}); var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { + env.addReporter(reporter); + global.onerror('Uncaught Error: ENOCHEESE'); + + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.failedExpectations).toEqual([]); expect(originalOnerror).toHaveBeenCalledWith('Uncaught Error: ENOCHEESE'); done(); }); - - env.addReporter(reporter); - global.onerror('Uncaught Error: ENOCHEESE'); - - env.execute(); }); }); @@ -2092,14 +2074,13 @@ describe("Env integration", function() { it('is "passed"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { + env.addReporter(reporter); + env.it('passes', function() {}); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('passed'); done(); }); - - env.addReporter(reporter); - env.it('passes', function() {}); - env.execute(); }); }); @@ -2107,16 +2088,15 @@ describe("Env integration", function() { it('is "failed"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { - expect(e.overallStatus).toEqual('failed'); - done(); - }); - env.addReporter(reporter); env.it('fails', function() { env.expect(true).toBe(false); }); - env.execute(); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(e.overallStatus).toEqual('failed'); + done(); + }) }); }); @@ -2134,23 +2114,21 @@ describe("Env integration", function() { }); it('should report "failed" status if "failSpecWithNoExpectations" is enabled', function(done) { - reporter.jasmineDone.and.callFake(function(e) { + env.configure({ failSpecWithNoExpectations: true }); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('failed'); done(); }); - - env.configure({ failSpecWithNoExpectations: true }); - env.execute(); }); it('should report "passed" status if "failSpecWithNoExpectations" is disabled', function(done) { - reporter.jasmineDone.and.callFake(function(e) { + env.configure({ failSpecWithNoExpectations: false }); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('passed'); done(); }); - - env.configure({ failSpecWithNoExpectations: false }); - env.execute(); }); }); @@ -2158,17 +2136,16 @@ describe("Env integration", function() { it('is "failed"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { - expect(e.overallStatus).toEqual('failed'); - done(); - }); - env.addReporter(reporter); env.beforeAll(function() { throw new Error('nope'); }); env.it('does not run', function() {}); - env.execute(); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(e.overallStatus).toEqual('failed'); + done(); + }); }); }); @@ -2176,11 +2153,6 @@ describe("Env integration", function() { it('is "failed"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { - expect(e.overallStatus).toEqual('failed'); - done(); - }); - env.addReporter(reporter); env.describe('something', function() { env.beforeAll(function() { @@ -2188,7 +2160,11 @@ describe("Env integration", function() { }); env.it('does not run', function() {}); }); - env.execute(); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(e.overallStatus).toEqual('failed'); + done(); + }); }); }); @@ -2196,17 +2172,16 @@ describe("Env integration", function() { it('is "failed"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { - expect(e.overallStatus).toEqual('failed'); - done(); - }); - env.addReporter(reporter); env.afterAll(function() { throw new Error('nope'); }); env.it('does not run', function() {}); - env.execute(); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(e.overallStatus).toEqual('failed'); + done(); + }); }); }); @@ -2214,11 +2189,6 @@ describe("Env integration", function() { it('is "failed"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { - expect(e.overallStatus).toEqual('failed'); - done(); - }); - env.addReporter(reporter); env.describe('something', function() { env.afterAll(function() { @@ -2226,7 +2196,11 @@ describe("Env integration", function() { }); env.it('does not run', function() {}); }); - env.execute(); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(e.overallStatus).toEqual('failed'); + done(); + }); }); }); @@ -2244,13 +2218,15 @@ describe("Env integration", function() { reporter.jasmineDone.and.callFake(function(e) { expect(e.overallStatus).toEqual('failed'); - done(); }); env.addReporter(reporter); env.it('passes', function() {}); global.onerror('Uncaught Error: ENOCHEESE'); - env.execute(); + env.execute(null, function() { + expect(reporter.jasmineDone).toHaveBeenCalled(); + done(); + }); }); }); @@ -2258,14 +2234,13 @@ describe("Env integration", function() { it('is "incomplete"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { + env.addReporter(reporter); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('incomplete'); expect(e.incompleteReason).toEqual('No specs found'); done(); }); - - env.addReporter(reporter); - env.execute(); }); }); @@ -2273,15 +2248,14 @@ describe("Env integration", function() { it('is "incomplete"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { + env.addReporter(reporter); + env.fit('is focused', function() {}); + env.execute(null, function(e) { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('incomplete'); expect(e.incompleteReason).toEqual('fit() or fdescribe() was found'); done(); }); - - env.addReporter(reporter); - env.fit('is focused', function() {}); - env.execute(); }); }); @@ -2289,17 +2263,16 @@ describe("Env integration", function() { it('is "incomplete"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { - expect(e.overallStatus).toEqual('incomplete'); - expect(e.incompleteReason).toEqual('fit() or fdescribe() was found'); - done(); - }); - env.addReporter(reporter); env.fdescribe('something focused', function() { env.it('does a thing', function() {}); }); - env.execute(); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(e.overallStatus).toEqual('incomplete'); + expect(e.incompleteReason).toEqual('fit() or fdescribe() was found'); + done(); + }); }); }); @@ -2307,17 +2280,16 @@ describe("Env integration", function() { it('is "failed"', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - reporter.jasmineDone.and.callFake(function(e) { - expect(e.overallStatus).toEqual('failed'); - expect(e.incompleteReason).toBeUndefined(); - done(); - }); - env.addReporter(reporter); env.fit('is focused', function() { env.expect(true).toBe(false); }); - env.execute(); + env.execute(null, function() { + var e = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(e.overallStatus).toEqual('failed'); + expect(e.incompleteReason).toBeUndefined(); + done(); + }); }); }); }); @@ -2325,31 +2297,9 @@ describe("Env integration", function() { it('should report deprecation warnings on the correct specs and suites', function(done) { var reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']); - // prevent deprecation from being displayed + // prevent deprecation from being displayed, as well as letting us observe calls spyOn(console, "error"); - reporter.jasmineDone.and.callFake(function(result) { - expect(result.deprecationWarnings).toEqual([ - jasmine.objectContaining({ message: 'top level deprecation' }) - ]); - - expect(reporter.suiteDone).toHaveBeenCalledWith(jasmine.objectContaining({ - fullName: 'suite', - deprecationWarnings: [ - jasmine.objectContaining({ message: 'suite level deprecation' }) - ] - })); - - expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({ - fullName: 'suite spec', - deprecationWarnings: [ - jasmine.objectContaining({ message: 'spec level deprecation' }) - ] - })); - - done(); - }); - env.addReporter(reporter); env.deprecated('top level deprecation'); @@ -2364,7 +2314,31 @@ describe("Env integration", function() { }); }); - env.execute(); + env.execute(null, function() { + var result = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(result.deprecationWarnings).toEqual([ + jasmine.objectContaining({ message: 'top level deprecation' }) + ]); + expect(console.error).toHaveBeenCalledWith('DEPRECATION: top level deprecation'); + + expect(reporter.suiteDone).toHaveBeenCalledWith(jasmine.objectContaining({ + fullName: 'suite', + deprecationWarnings: [ + jasmine.objectContaining({ message: 'suite level deprecation' }) + ] + })); + expect(console.error).toHaveBeenCalledWith('DEPRECATION: suite level deprecation (in suite: suite)'); + + expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + fullName: 'suite spec', + deprecationWarnings: [ + jasmine.objectContaining({ message: 'spec level deprecation' }) + ] + })); + expect(console.error).toHaveBeenCalledWith('DEPRECATION: spec level deprecation (in spec: suite spec)'); + + done(); + }); }); it('should report deprecation stack with an error object', function(done) { @@ -2379,7 +2353,22 @@ describe("Env integration", function() { // prevent deprecation from being displayed spyOn(console, "error"); - reporter.jasmineDone.and.callFake(function(result) { + env.addReporter(reporter); + + env.deprecated(topLevelError); + + env.describe('suite', function() { + env.beforeAll(function() { + env.deprecated(suiteLevelError); + }); + + env.it('spec', function() { + env.deprecated(specLevelError); + }); + }); + + env.execute(null, function() { + var result = reporter.jasmineDone.calls.argsFor(0)[0]; expect(result.deprecationWarnings).toEqual([ jasmine.objectContaining({ message: topLevelError.message, @@ -2409,59 +2398,24 @@ describe("Env integration", function() { done(); }); - - env.addReporter(reporter); - - env.deprecated(topLevelError); - - env.describe('suite', function() { - env.beforeAll(function() { - env.deprecated(suiteLevelError); - }); - - env.it('spec', function() { - env.deprecated(specLevelError); - }); - }); - - env.execute(); }); it('supports async matchers', function(done) { jasmine.getEnv().requirePromises(); var specDone = jasmine.createSpy('specDone'), - suiteDone = jasmine.createSpy('suiteDone'); + suiteDone = jasmine.createSpy('suiteDone'), + jasmineDone = jasmine.createSpy('jasmineDone'); env.addReporter({ specDone: specDone, suiteDone: suiteDone, - jasmineDone: function(result) { - expect(result.failedExpectations).toEqual([jasmine.objectContaining({ - message: 'Expected [object Promise] to be rejected.' - })]); - - expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ - description: 'has an async failure', - failedExpectations: [jasmine.objectContaining({ - message: 'Expected [object Promise] to be rejected.' - })] - })); - - expect(suiteDone).toHaveBeenCalledWith(jasmine.objectContaining({ - description: 'a suite', - failedExpectations: [jasmine.objectContaining({ - message: 'Expected [object Promise] to be rejected.' - })] - })); - - done(); - } + jasmineDone: jasmineDone }); function fail(innerDone) { var resolve; - var p = new Promise(function(res, rej) { resolve = res }); + var p = new Promise(function(res, rej) { resolve = res }); // eslint-disable-line compat/compat env.expectAsync(p).toBeRejected().then(innerDone); resolve(); } @@ -2472,7 +2426,28 @@ describe("Env integration", function() { env.it('has an async failure', fail); }); - env.execute(); + env.execute(null, function() { + var result = jasmineDone.calls.argsFor(0)[0]; + expect(result.failedExpectations).toEqual([jasmine.objectContaining({ + message: 'Expected [object Promise] to be rejected.' + })]); + + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'has an async failure', + failedExpectations: [jasmine.objectContaining({ + message: 'Expected [object Promise] to be rejected.' + })] + })); + + expect(suiteDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'a suite', + failedExpectations: [jasmine.objectContaining({ + message: 'Expected [object Promise] to be rejected.' + })] + })); + + done(); + }); }); it('provides custom equality testers to async matchers', function(done) { @@ -2480,24 +2455,21 @@ describe("Env integration", function() { var specDone = jasmine.createSpy('specDone'); - env.addReporter({ - specDone: specDone, - jasmineDone: function() { - expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ - description: 'has an async failure', - failedExpectations: [] - })); - done(); - } - }); + env.addReporter({specDone: specDone}); env.it('has an async failure', function() { env.addCustomEqualityTester(function() { return true; }); - var p = Promise.resolve('something'); + var p = Promise.resolve('something'); // eslint-disable-line compat/compat return env.expectAsync(p).toBeResolvedTo('something else'); }); - env.execute(); + env.execute(null, function() { + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + description: 'has an async failure', + failedExpectations: [] + })); + done(); + }); }); it('includes useful stack frames in async matcher failures', function(done) { @@ -2505,32 +2477,30 @@ describe("Env integration", function() { var specDone = jasmine.createSpy('specDone'); - env.addReporter({ - specDone: specDone, - jasmineDone: function() { - expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ - failedExpectations: [jasmine.objectContaining({ - stack: jasmine.stringMatching('EnvSpec.js') - })] - })); - done(); - } - }); + env.addReporter({specDone: specDone}); env.it('has an async failure', function() { env.addCustomEqualityTester(function() { return true; }); - var p = Promise.resolve(); + var p = Promise.resolve(); // eslint-disable-line compat/compat return env.expectAsync(p).toBeRejected(); }); - env.execute(); + env.execute(null, function() { + expect(specDone).toHaveBeenCalledWith(jasmine.objectContaining({ + failedExpectations: [jasmine.objectContaining({ + stack: jasmine.stringMatching('EnvSpec.js') + })] + })); + done(); + }); }); it('reports an error when an async expectation occurs after the spec finishes', function(done) { jasmine.getEnv().requirePromises(); var resolve, - promise = new Promise(function(res) { resolve = res; }); + jasmineDone = jasmine.createSpy('jasmineDone'), + promise = new Promise(function(res) { resolve = res; }); // eslint-disable-line compat/compat env.describe('a suite', function() { env.it('does not wait', function() { @@ -2545,40 +2515,42 @@ describe("Env integration", function() { specDone: function() { resolve(); }, - jasmineDone: function (result) { - expect(result.failedExpectations).toEqual([ - jasmine.objectContaining({ - passed: false, - globalErrorType: 'lateExpectation', - message: 'Spec "a suite does not wait" ran a "toBeResolved" expectation ' + - 'after it finished.\n' + - 'Did you forget to return or await the result of expectAsync?', - matcherName: 'toBeResolved' - }), - jasmine.objectContaining({ - passed: false, - globalErrorType: 'lateExpectation', - message: 'Spec "a suite does not wait" ran a "toBeResolvedTo" expectation ' + - 'after it finished.\n' + - 'Message: "Expected a promise to be resolved to \'something else\' ' + - 'but it was resolved to undefined."\n' + - 'Did you forget to return or await the result of expectAsync?', - matcherName: 'toBeResolvedTo' - }) - ]); - - done(); - } + jasmineDone: jasmineDone }); - env.execute(); + env.execute(null, function () { + var result = jasmineDone.calls.argsFor(0)[0]; + expect(result.failedExpectations).toEqual([ + jasmine.objectContaining({ + passed: false, + globalErrorType: 'lateExpectation', + message: 'Spec "a suite does not wait" ran a "toBeResolved" expectation ' + + 'after it finished.\n' + + 'Did you forget to return or await the result of expectAsync?', + matcherName: 'toBeResolved' + }), + jasmine.objectContaining({ + passed: false, + globalErrorType: 'lateExpectation', + message: 'Spec "a suite does not wait" ran a "toBeResolvedTo" expectation ' + + 'after it finished.\n' + + 'Message: "Expected a promise to be resolved to \'something else\' ' + + 'but it was resolved to undefined."\n' + + 'Did you forget to return or await the result of expectAsync?', + matcherName: 'toBeResolvedTo' + }) + ]); + + done(); + }); }); it('reports an error when an async expectation occurs after the suite finishes', function(done) { jasmine.getEnv().requirePromises(); var resolve, - promise = new Promise(function(res) { resolve = res; }); + jasmineDone = jasmine.createSpy('jasmineDone'), + promise = new Promise(function(res) { resolve = res; }); // eslint-disable-line compat/compat env.describe('a suite', function() { env.afterAll(function() { @@ -2594,23 +2566,24 @@ describe("Env integration", function() { suiteDone: function() { resolve(); }, - jasmineDone: function (result) { - expect(result.failedExpectations).toEqual([ - jasmine.objectContaining({ - passed: false, - globalErrorType: 'lateExpectation', - message: 'Suite "a suite" ran a "toBeResolved" expectation ' + - 'after it finished.\n' + - 'Did you forget to return or await the result of expectAsync?', - matcherName: 'toBeResolved' - }) - ]); - - done(); - } + jasmineDone: jasmineDone }); - env.execute(); + env.execute(null, function () { + var result = jasmineDone.calls.argsFor(0)[0]; + expect(result.failedExpectations).toEqual([ + jasmine.objectContaining({ + passed: false, + globalErrorType: 'lateExpectation', + message: 'Suite "a suite" ran a "toBeResolved" expectation ' + + 'after it finished.\n' + + 'Did you forget to return or await the result of expectAsync?', + matcherName: 'toBeResolved' + }) + ]); + + done(); + }); }); it("supports asymmetric equality testers that take a matchersUtil", function(done) { @@ -2638,7 +2611,89 @@ describe("Env integration", function() { expect(result.status).toEqual('passed'); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); + }); + + describe('The optional callback argument to #execute', function() { + beforeEach(function() { + this.savedInterval = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL; + }); + + afterEach(function() { + jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = this.savedInterval; + }); + + it('is called after reporter events are dispatched', function(done) { + var reporter = jasmine.createSpyObj( + 'reporter', + ['specDone', 'suiteDone', 'jasmineDone'] + ); + + env.addReporter(reporter); + env.describe('suite', function () { + env.it('spec', function() {}); + }); + + env.execute(null, function() { + expect(reporter.specDone).toHaveBeenCalled(); + expect(reporter.suiteDone).toHaveBeenCalled(); + expect(reporter.jasmineDone).toHaveBeenCalled(); + done(); + }); + }); + + it('is called after the stack is cleared', function(done) { + var realClearStack = jasmineUnderTest.getClearStack(jasmineUnderTest.getGlobal()), + clearStackSpy = jasmine.createSpy('clearStack').and.callFake(realClearStack); + spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(clearStackSpy); + + // Create a new env that has the clearStack defined above + env.cleanup_(); + env = new jasmineUnderTest.Env(); + + env.describe('suite', function () { + env.it('spec', function() {}); + }); + + env.execute(null, function() { + expect(clearStackSpy).toHaveBeenCalled(); // (many times) + clearStackSpy.calls.reset(); + setTimeout(function() { + expect(clearStackSpy).not.toHaveBeenCalled(); + done(); + }); + }); + }); + + it('is called after QueueRunner timeouts are cleared', function(done) { + var setTimeoutSpy = spyOn(jasmineUnderTest.getGlobal(), 'setTimeout') + .and.callThrough(); + var clearTimeoutSpy = spyOn(jasmineUnderTest.getGlobal(), 'clearTimeout') + .and.callThrough(); + + jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 123456; // a distinctive value + + env = new jasmineUnderTest.Env(); + + env.describe('suite', function () { + env.it('spec', function () { + }); + }); + + env.execute(null, function () { + var timeoutIds = setTimeoutSpy.calls.all() + .filter(function(call) { return call.args[1] === 123456; }) + .map(function(call) { return call.returnValue; }); + + expect(timeoutIds.length).toBeGreaterThan(0); + + timeoutIds.forEach(function(timeoutId) { + expect(clearTimeoutSpy).toHaveBeenCalledWith(timeoutId); + }); + + done(); + }); + }); }); }); diff --git a/spec/core/integration/MatchersSpec.js b/spec/core/integration/MatchersSpec.js index 7f19a95c..8174faf8 100644 --- a/spec/core/integration/MatchersSpec.js +++ b/spec/core/integration/MatchersSpec.js @@ -28,8 +28,8 @@ describe('Matchers (Integration)', function() { .toBeUndefined(); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); } @@ -47,12 +47,15 @@ describe('Matchers (Integration)', function() { expect(result.failedExpectations[0].message) .withContext('Failed with a thrown error rather than a matcher failure') .not.toMatch(/^Error: /); + expect(result.failedExpectations[0].message) + .withContext('Failed with a thrown type error rather than a matcher failure') + .not.toMatch(/^TypeError: /); expect(result.failedExpectations[0].matcherName).withContext('Matcher name') .not.toEqual(''); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); } @@ -72,8 +75,8 @@ describe('Matchers (Integration)', function() { .toEqual(config.expectedMessage); }; - env.addReporter({specDone: specExpectations, jasmineDone: done}); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); } @@ -98,8 +101,8 @@ describe('Matchers (Integration)', function() { .toBeUndefined(); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); } @@ -123,8 +126,8 @@ describe('Matchers (Integration)', function() { .not.toEqual(''); }; - env.addReporter({ specDone: specExpectations, jasmineDone: done }); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); } @@ -146,8 +149,8 @@ describe('Matchers (Integration)', function() { .toEqual(config.expectedMessage); }; - env.addReporter({specDone: specExpectations, jasmineDone: done}); - env.execute(); + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); }); } @@ -332,11 +335,11 @@ describe('Matchers (Integration)', function() { describe('toBeResolved', function() { verifyPassesAsync(function(env) { - return env.expectAsync(Promise.resolve()).toBeResolved(); + return env.expectAsync(Promise.resolve()).toBeResolved(); // eslint-disable-line compat/compat }); verifyFailsAsync(function(env) { - return env.expectAsync(Promise.reject()).toBeResolved(); + return env.expectAsync(Promise.reject()).toBeResolved(); // eslint-disable-line compat/compat }); }); @@ -345,11 +348,11 @@ describe('Matchers (Integration)', function() { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); - return env.expectAsync(Promise.resolve('5')).toBeResolvedTo(5); + return env.expectAsync(Promise.resolve('5')).toBeResolvedTo(5); // eslint-disable-line compat/compat }); verifyFailsAsync(function(env) { - return env.expectAsync(Promise.resolve('foo')).toBeResolvedTo('bar'); + return env.expectAsync(Promise.resolve('foo')).toBeResolvedTo('bar'); // eslint-disable-line compat/compat }); verifyFailsWithCustomObjectFormattersAsync({ @@ -357,7 +360,7 @@ describe('Matchers (Integration)', function() { return '|' + val + '|'; }, expectations: function(env) { - return env.expectAsync(Promise.resolve('x')).toBeResolvedTo('y'); + return env.expectAsync(Promise.resolve('x')).toBeResolvedTo('y'); // eslint-disable-line compat/compat }, expectedMessage: 'Expected a promise to be resolved to |y| ' + 'but it was resolved to |x|.' @@ -366,11 +369,11 @@ describe('Matchers (Integration)', function() { describe('toBeRejected', function() { verifyPassesAsync(function(env) { - return env.expectAsync(Promise.reject('nope')).toBeRejected(); + return env.expectAsync(Promise.reject('nope')).toBeRejected(); // eslint-disable-line compat/compat }); verifyFailsAsync(function(env) { - return env.expectAsync(Promise.resolve()).toBeRejected(); + return env.expectAsync(Promise.resolve()).toBeRejected(); // eslint-disable-line compat/compat }); }); @@ -379,11 +382,11 @@ describe('Matchers (Integration)', function() { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); - return env.expectAsync(Promise.reject('5')).toBeRejectedWith(5); + return env.expectAsync(Promise.reject('5')).toBeRejectedWith(5); // eslint-disable-line compat/compat }); verifyFailsAsync(function(env) { - return env.expectAsync(Promise.resolve()).toBeRejectedWith('nope'); + return env.expectAsync(Promise.resolve()).toBeRejectedWith('nope'); // eslint-disable-line compat/compat }); verifyFailsWithCustomObjectFormattersAsync({ @@ -391,7 +394,7 @@ describe('Matchers (Integration)', function() { return '|' + val + '|'; }, expectations: function(env) { - return env.expectAsync(Promise.reject('x')).toBeRejectedWith('y'); + return env.expectAsync(Promise.reject('x')).toBeRejectedWith('y'); // eslint-disable-line compat/compat }, expectedMessage: 'Expected a promise to be rejected with |y| ' + 'but it was rejected with |x|.' @@ -400,11 +403,11 @@ describe('Matchers (Integration)', function() { describe('toBeRejectedWithError', function() { verifyPassesAsync(function(env) { - return env.expectAsync(Promise.reject(new Error())).toBeRejectedWithError(Error); + return env.expectAsync(Promise.reject(new Error())).toBeRejectedWithError(Error); // eslint-disable-line compat/compat }); verifyFailsAsync(function(env) { - return env.expectAsync(Promise.resolve()).toBeRejectedWithError(Error); + return env.expectAsync(Promise.resolve()).toBeRejectedWithError(Error); // eslint-disable-line compat/compat }); verifyFailsWithCustomObjectFormattersAsync({ @@ -412,7 +415,7 @@ describe('Matchers (Integration)', function() { return '|' + val + '|'; }, expectations: function(env) { - return env.expectAsync(Promise.reject('foo')).toBeRejectedWithError('foo'); + return env.expectAsync(Promise.reject('foo')).toBeRejectedWithError('foo'); // eslint-disable-line compat/compat }, expectedMessage: 'Expected a promise to be rejected with Error: |foo| ' + 'but it was rejected with |foo|.' @@ -477,9 +480,9 @@ describe('Matchers (Integration)', function() { verifyFailsWithCustomObjectFormatters({ formatter: function(val) { if (val === 5) { - return "five" + return 'five'; } else if (val === 4) { - return "four" + return 'four'; } }, expectations: function(env) { @@ -489,6 +492,16 @@ describe('Matchers (Integration)', function() { }); }); + describe('toHaveSize', function() { + verifyPasses(function(env) { + env.expect(['a','b']).toHaveSize(2); + }); + + verifyFails(function(env) { + env.expect(['a','b']).toHaveSize(1); + }); + }); + describe('toHaveBeenCalled', function() { verifyPasses(function(env) { var spy = env.createSpy('spy'); @@ -560,6 +573,22 @@ describe('Matchers (Integration)', function() { }); }); + describe('toHaveBeenCalledOnceWith', function() { + verifyPasses(function(env) { + var spy = env.createSpy(); + spy('5', 3); + env.addCustomEqualityTester(function(a, b) { + return a.toString() === b.toString(); + }); + env.expect(spy).toHaveBeenCalledOnceWith(5, 3); + }); + + verifyFails(function(env) { + var spy = env.createSpy(); + env.expect(spy).toHaveBeenCalledOnceWith(5, 3); + }); + }); + describe('toHaveClass', function() { beforeEach(function() { this.domHelpers = jasmine.getEnv().domHelpers(); diff --git a/spec/core/integration/SpecRunningSpec.js b/spec/core/integration/SpecRunningSpec.js index d522cf86..33459d02 100644 --- a/spec/core/integration/SpecRunningSpec.js +++ b/spec/core/integration/SpecRunningSpec.js @@ -66,17 +66,14 @@ describe("spec running", function () { expect(bar).toEqual(0); expect(baz).toEqual(0); expect(quux).toEqual(0); - var assertions = function() { + + env.execute(null, function() { expect(foo).toEqual(1); expect(bar).toEqual(1); expect(baz).toEqual(1); expect(quux).toEqual(1); done(); - }; - - env.addReporter({ jasmineDone: assertions }); - - env.execute(); + }); }); it("should permit nested describes", function(done) { @@ -136,7 +133,7 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { var expected = [ "topSuite beforeEach", "outer beforeEach", @@ -168,11 +165,7 @@ describe("spec running", function () { ]; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("should run multiple befores and afters ordered so functions declared later are treated as more specific", function(done) { @@ -232,7 +225,7 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { var expected = [ "runner beforeAll1", "runner beforeAll2", @@ -250,11 +243,7 @@ describe("spec running", function () { ]; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it('should run beforeAlls before beforeEachs and afterAlls after afterEachs', function(done) { @@ -298,7 +287,7 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { var expected = [ "runner beforeAll", "inner beforeAll", @@ -312,10 +301,7 @@ describe("spec running", function () { ]; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.execute(); + }); }); it('should run beforeAlls and afterAlls in the order declared when runnablesToRun is provided', function(done) { @@ -365,7 +351,7 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute([spec2.id, spec.id], function() { var expected = [ "runner beforeAll", "inner beforeAll", @@ -385,10 +371,7 @@ describe("spec running", function () { ]; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.execute([spec2.id, spec.id]); + }); }); it('only runs *Alls once in a focused suite', function(done){ @@ -406,13 +389,10 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual(['beforeAll', 'spec', 'afterAll']); done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.execute(); + }); }); describe('focused runnables', function() { @@ -435,7 +415,7 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { var expected = [ 'beforeAll', 'beforeEach', @@ -449,10 +429,7 @@ describe("spec running", function () { ]; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.execute(); + }); }); it('focused specs in focused suites cause non-focused siblings to not run', function(done){ @@ -467,14 +444,11 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { var expected = ['focused spec']; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.execute(); + }); }); it('focused suites in focused suites cause non-focused siblings to not run', function(done){ @@ -491,14 +465,11 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { var expected = ['inner spec']; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.execute(); + }); }); it('focused runnables unfocus ancestor focused suites', function(done) { @@ -515,14 +486,11 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { var expected = ['focused spec']; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - env.execute(); + }); }); }); @@ -534,14 +502,10 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { expect(specInADisabledSuite).not.toHaveBeenCalled(); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("shouldn't run before/after functions in disabled suites", function(done) { @@ -556,14 +520,10 @@ describe("spec running", function () { env.it('spec inside a disabled suite', shouldNotRun); }); - var assertions = function() { + env.execute(null, function() { expect(shouldNotRun).not.toHaveBeenCalled(); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("should allow top level suites to be disabled", function(done) { @@ -577,15 +537,11 @@ describe("spec running", function () { env.it('another spec', otherSpec); }); - var assertions = function() { + env.execute(null, function() { expect(specInADisabledSuite).not.toHaveBeenCalled(); expect(otherSpec).toHaveBeenCalled(); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("should set all pending specs to pending when a suite is run", function(done) { @@ -594,31 +550,20 @@ describe("spec running", function () { pendingSpec = env.it("I am a pending spec"); }); - var assertions = function() { + env.execute(null, function() { expect(pendingSpec.status()).toBe("pending"); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("should recover gracefully when there are errors in describe functions", function(done) { var specs = [], - reporter = jasmine.createSpyObj(['specDone', 'suiteDone', 'jasmineDone']); + reporter = jasmine.createSpyObj(['specDone', 'suiteDone']); reporter.specDone.and.callFake(function(result) { specs.push(result.fullName); }); - reporter.jasmineDone.and.callFake(function() { - expect(specs).toEqual(['outer1 inner1 should thingy', 'outer1 inner2 should other thingy', 'outer2 should xxx']); - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('outer1 inner1', [/inner error/]); - expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('outer1', [/outer error/]); - done(); - }); - expect(function() { env.describe("outer1", function() { env.describe("inner1", function() { @@ -647,7 +592,12 @@ describe("spec running", function () { }); env.addReporter(reporter); - env.execute(); + env.execute(null, function() { + expect(specs).toEqual(['outer1 inner1 should thingy', 'outer1 inner2 should other thingy', 'outer2 should xxx']); + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('outer1 inner1', [/inner error/]); + expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('outer1', [/outer error/]); + done(); + }); }); it("re-enters suites that have no *Alls", function(done) { @@ -668,14 +618,10 @@ describe("spec running", function () { actions.push("spec3"); }); - env.addReporter({ - jasmineDone: function() { - expect(actions).toEqual(["spec2", "spec3", "spec1"]); - done(); - } + env.execute([spec2.id, spec3.id, spec1.id], function() { + expect(actions).toEqual(["spec2", "spec3", "spec1"]); + done(); }); - - env.execute([spec2.id, spec3.id, spec1.id]); }); it("refuses to re-enter suites with a beforeAll", function() { @@ -698,16 +644,10 @@ describe("spec running", function () { actions.push("spec3"); }); - env.addReporter({ - jasmineDone: function() { - expect(actions).toEqual([]); - done(); - } - }); - expect(function() { env.execute([spec2.id, spec3.id, spec1.id]); }).toThrowError(/beforeAll/); + expect(actions).toEqual([]); }); it("refuses to re-enter suites with a afterAll", function() { @@ -730,16 +670,10 @@ describe("spec running", function () { actions.push("spec3"); }); - env.addReporter({ - jasmineDone: function() { - expect(actions).toEqual([]); - done(); - } - }); - expect(function() { env.execute([spec2.id, spec3.id, spec1.id]); }).toThrowError(/afterAll/); + expect(actions).toEqual([]); }); it("should run the tests in a consistent order when a seed is supplied", function(done) { @@ -800,7 +734,7 @@ describe("spec running", function () { }); }); - var assertions = function() { + env.execute(null, function() { var expected = [ 'topSuite beforeEach', 'outer beforeEach', @@ -832,11 +766,7 @@ describe("spec running", function () { ]; expect(actions).toEqual(expected); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); describe("When throwOnExpectationFailure is set", function() { @@ -870,18 +800,14 @@ describe("spec running", function () { env.configure({oneFailurePerSpec: true}); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual([ 'outer beforeEach', 'inner afterEach', 'outer afterEach' ]); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("skips to cleanup functions after done.fail is called", function(done) { @@ -905,17 +831,13 @@ describe("spec running", function () { env.configure({oneFailurePerSpec: true}); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual([ 'beforeEach', 'afterEach' ]); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("skips to cleanup functions when an async function times out", function(done) { @@ -937,17 +859,13 @@ describe("spec running", function () { env.configure({oneFailurePerSpec: true}); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual([ 'beforeEach', 'afterEach' ]); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("skips to cleanup functions after an error with deprecations", function(done) { @@ -982,7 +900,7 @@ describe("spec running", function () { env.throwOnExpectationFailure(true); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual([ 'outer beforeEach', 'inner afterEach', @@ -990,11 +908,7 @@ describe("spec running", function () { ]); expect(env.deprecated).toHaveBeenCalled(); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("skips to cleanup functions after done.fail is called with deprecations", function(done) { @@ -1020,18 +934,14 @@ describe("spec running", function () { env.throwOnExpectationFailure(true); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual([ 'beforeEach', 'afterEach' ]); expect(env.deprecated).toHaveBeenCalled(); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); it("skips to cleanup functions when an async function times out with deprecations", function(done) { @@ -1055,18 +965,14 @@ describe("spec running", function () { env.throwOnExpectationFailure(true); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual([ 'beforeEach', 'afterEach' ]); expect(env.deprecated).toHaveBeenCalled(); done(); - }; - - env.addReporter({jasmineDone: assertions}); - - env.execute(); + }); }); }); @@ -1089,13 +995,10 @@ describe("spec running", function () { env.configure({random: false, failFast: true}); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual(['fails']); done(); - }; - - env.addReporter({ jasmineDone: assertions }); - env.execute(); + }); }); it("does not run further specs when one fails when configured with deprecated option", function(done) { @@ -1119,14 +1022,11 @@ describe("spec running", function () { env.configure({random: false}); env.stopOnSpecFailure(true); - var assertions = function() { + env.execute(null, function() { expect(actions).toEqual(['fails']); expect(env.deprecated).toHaveBeenCalled(); done(); - }; - - env.addReporter({ jasmineDone: assertions }); - env.execute(); + }); }); }); }); diff --git a/spec/core/matchers/DiffBuilderSpec.js b/spec/core/matchers/DiffBuilderSpec.js index 6d82c3f8..01220ce0 100644 --- a/spec/core/matchers/DiffBuilderSpec.js +++ b/spec/core/matchers/DiffBuilderSpec.js @@ -133,4 +133,50 @@ describe("DiffBuilder", function () { expect(diffBuilder.getMessage()).toEqual(expectedMsg); }); + + it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ at the root', function() { + var prettyPrinter = jasmineUnderTest.makePrettyPrinter([]), + diffBuilder = new jasmineUnderTest.DiffBuilder({prettyPrinter: prettyPrinter}), + expectedMsg = 'Expected $.foo = 1 to equal 2.\n' + + "Expected $.baz = undefined to equal 3."; + + + diffBuilder.setRoots( + {foo: 1, bar: 2}, + jasmine.objectContaining({foo: 2, baz: 3}) + ); + + diffBuilder.withPath('foo', function() { + diffBuilder.recordMismatch(); + }); + diffBuilder.withPath('baz', function() { + diffBuilder.recordMismatch(); + }); + + expect(diffBuilder.getMessage()).toEqual(expectedMsg); + }); + + it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ below the root', function() { + var prettyPrinter = jasmineUnderTest.makePrettyPrinter([]), + diffBuilder = new jasmineUnderTest.DiffBuilder({prettyPrinter: prettyPrinter}), + expectedMsg = 'Expected $.x.foo = 1 to equal 2.\n' + + "Expected $.x.baz = undefined to equal 3."; + + + diffBuilder.setRoots( + {x: {foo: 1, bar: 2}}, + {x: jasmine.objectContaining({foo: 2, baz: 3})} + ); + + diffBuilder.withPath('x', function() { + diffBuilder.withPath('foo', function () { + diffBuilder.recordMismatch(); + }); + diffBuilder.withPath('baz', function () { + diffBuilder.recordMismatch(); + }); + }); + + expect(diffBuilder.getMessage()).toEqual(expectedMsg); + }); }); diff --git a/spec/core/matchers/async/toBePendingSpec.js b/spec/core/matchers/async/toBePendingSpec.js new file mode 100644 index 00000000..285414f1 --- /dev/null +++ b/spec/core/matchers/async/toBePendingSpec.js @@ -0,0 +1,52 @@ +/* eslint-disable compat/compat */ +describe('toBePending', function() { + it('passes if the actual promise is pending', function() { + jasmine.getEnv().requirePromises(); + + var matchersUtil = new jasmineUnderTest.MatchersUtil(), + matcher = jasmineUnderTest.asyncMatchers.toBePending(matchersUtil), + actual = new Promise(function() {}); + + return matcher.compare(actual).then(function(result) { + expect(result).toEqual(jasmine.objectContaining({pass: true})); + }); + }); + + it('fails if the actual promise is resolved', function() { + jasmine.getEnv().requirePromises(); + + var matchersUtil = new jasmineUnderTest.MatchersUtil(), + matcher = jasmineUnderTest.asyncMatchers.toBePending(matchersUtil), + actual = Promise.resolve(); + + return matcher.compare(actual).then(function(result) { + expect(result).toEqual(jasmine.objectContaining({pass: false})); + }); + }); + + it('fails if the actual promise is rejected', function() { + jasmine.getEnv().requirePromises(); + + var matchersUtil = new jasmineUnderTest.MatchersUtil(), + matcher = jasmineUnderTest.asyncMatchers.toBePending(matchersUtil), + actual = Promise.reject(new Error('promise was rejected')); + + return matcher.compare(actual).then(function(result) { + expect(result).toEqual(jasmine.objectContaining({pass: false})); + }); + }); + + it('fails if actual is not a promise', function() { + var matchersUtil = new jasmineUnderTest.MatchersUtil(), + matcher = jasmineUnderTest.asyncMatchers.toBePending(matchersUtil), + actual = 'not a promise'; + + function f() { + return matcher.compare(actual); + } + + expect(f).toThrowError( + 'Expected toBePending to be called on a promise.' + ); + }); +}); diff --git a/spec/core/matchers/async/toBeRejectedSpec.js b/spec/core/matchers/async/toBeRejectedSpec.js index 61bd2a5a..6ecf9ebc 100644 --- a/spec/core/matchers/async/toBeRejectedSpec.js +++ b/spec/core/matchers/async/toBeRejectedSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ describe('toBeRejected', function() { it('passes if the actual is rejected', function() { jasmine.getEnv().requirePromises(); diff --git a/spec/core/matchers/async/toBeRejectedWithErrorSpec.js b/spec/core/matchers/async/toBeRejectedWithErrorSpec.js index 97260d59..d09a05fd 100644 --- a/spec/core/matchers/async/toBeRejectedWithErrorSpec.js +++ b/spec/core/matchers/async/toBeRejectedWithErrorSpec.js @@ -1,8 +1,9 @@ +/* eslint-disable compat/compat */ describe('#toBeRejectedWithError', function () { it('passes when Error type matches', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new TypeError('foo')); @@ -17,7 +18,7 @@ describe('#toBeRejectedWithError', function () { it('passes when Error type and message matches', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new TypeError('foo')); @@ -32,7 +33,7 @@ describe('#toBeRejectedWithError', function () { it('passes when Error matches and is exactly Error', function() { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new Error()); @@ -48,7 +49,7 @@ describe('#toBeRejectedWithError', function () { it('passes when Error message matches a string', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new Error('foo')); @@ -63,7 +64,7 @@ describe('#toBeRejectedWithError', function () { it('passes when Error message matches a RegExp', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new Error('foo')); @@ -78,7 +79,7 @@ describe('#toBeRejectedWithError', function () { it('passes when Error message is empty', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new Error()); @@ -93,7 +94,7 @@ describe('#toBeRejectedWithError', function () { it('passes when no arguments', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new Error()); @@ -108,7 +109,7 @@ describe('#toBeRejectedWithError', function () { it('fails when resolved', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.resolve(new Error('foo')); @@ -123,7 +124,7 @@ describe('#toBeRejectedWithError', function () { it('fails when rejected with non Error type', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject('foo'); @@ -138,7 +139,7 @@ describe('#toBeRejectedWithError', function () { it('fails when Error type mismatches', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new Error('foo')); @@ -153,7 +154,7 @@ describe('#toBeRejectedWithError', function () { it('fails when Error message mismatches', function () { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = Promise.reject(new Error('foo')); @@ -166,7 +167,7 @@ describe('#toBeRejectedWithError', function () { }); it('fails if actual is not a promise', function() { - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError(matchersUtil), actual = 'not a promise'; diff --git a/spec/core/matchers/async/toBeRejectedWithSpec.js b/spec/core/matchers/async/toBeRejectedWithSpec.js index 2c02a03a..69003c59 100644 --- a/spec/core/matchers/async/toBeRejectedWithSpec.js +++ b/spec/core/matchers/async/toBeRejectedWithSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ describe('#toBeRejectedWith', function () { it('should return true if the promise is rejected with the expected value', function () { jasmine.getEnv().requirePromises(); diff --git a/spec/core/matchers/async/toBeResolvedSpec.js b/spec/core/matchers/async/toBeResolvedSpec.js index a6ab25ba..8dbff1c9 100644 --- a/spec/core/matchers/async/toBeResolvedSpec.js +++ b/spec/core/matchers/async/toBeResolvedSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ describe('toBeResolved', function() { it('passes if the actual is resolved', function() { jasmine.getEnv().requirePromises(); diff --git a/spec/core/matchers/async/toBeResolvedToSpec.js b/spec/core/matchers/async/toBeResolvedToSpec.js index c68eb0e0..ee57abe1 100644 --- a/spec/core/matchers/async/toBeResolvedToSpec.js +++ b/spec/core/matchers/async/toBeResolvedToSpec.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ describe('#toBeResolvedTo', function() { it('passes if the promise is resolved to the expected value', function() { jasmine.getEnv().requirePromises(); @@ -14,7 +15,7 @@ describe('#toBeResolvedTo', function() { it('fails if the promise is rejected', function() { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = Promise.reject('AsyncExpectationSpec error'); @@ -29,7 +30,7 @@ describe('#toBeResolvedTo', function() { it('fails if the promise is resolved to a different value', function() { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = Promise.resolve({foo: 17}); @@ -44,7 +45,7 @@ describe('#toBeResolvedTo', function() { it('builds its message correctly when negated', function() { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: new jasmineUnderTest.makePrettyPrinter()}), + var matchersUtil = new jasmineUnderTest.MatchersUtil({pp: jasmineUnderTest.makePrettyPrinter()}), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = Promise.resolve(true); diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index dbbefce9..825729a3 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -112,6 +112,11 @@ describe("matchersUtil", function() { expect(matchersUtil.equals(123, 456)).toBe(false); }); + it("fails for a Number and a String that have equivalent values", function() { + var matchersUtil = new jasmineUnderTest.MatchersUtil(); + expect(matchersUtil.equals(123, "123")).toBe(false); + }); + it("passes for Dates that are equivalent", function() { var matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Date("Jan 1, 1970"), new Date("Jan 1, 1970"))).toBe(true); @@ -280,8 +285,8 @@ describe("matchersUtil", function() { it("passes for equivalent Promises (GitHub issue #1314)", function() { if (typeof Promise === 'undefined') { return; } - var p1 = new Promise(function () {}), - p2 = new Promise(function () {}), + var p1 = new Promise(function () {}), // eslint-disable-line compat/compat + p2 = new Promise(function () {}), // eslint-disable-line compat/compat matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(p1, p1)).toBe(true); @@ -416,10 +421,10 @@ describe("matchersUtil", function() { jasmine.getEnv().requireFunctioningMaps(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var obj = new Map(); + var obj = new Map(); // eslint-disable-line compat/compat obj.set(1, 2); obj.set('foo', 'bar'); - var containing = new jasmineUnderTest.MapContaining(new Map()); + var containing = new jasmineUnderTest.MapContaining(new Map()); // eslint-disable-line compat/compat containing.sample.set('foo', 'bar'); expect(matchersUtil.equals(obj, containing)).toBe(true); @@ -430,10 +435,10 @@ describe("matchersUtil", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var obj = new Set(); + var obj = new Set(); // eslint-disable-line compat/compat obj.add(1); obj.add('foo'); - var containing = new jasmineUnderTest.SetContaining(new Set()); + var containing = new jasmineUnderTest.SetContaining(new Set()); // eslint-disable-line compat/compat containing.sample.add(1); expect(matchersUtil.equals(obj, containing)).toBe(true); @@ -610,17 +615,17 @@ describe("matchersUtil", function() { it("passes when comparing two empty sets", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - expect(matchersUtil.equals(new Set(), new Set())).toBe(true); + expect(matchersUtil.equals(new Set(), new Set())).toBe(true); // eslint-disable-line compat/compat }); it("passes when comparing identical sets", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var setA = new Set(); + var setA = new Set(); // eslint-disable-line compat/compat setA.add(6); setA.add(5); - var setB = new Set(); + var setB = new Set(); // eslint-disable-line compat/compat setB.add(6); setB.add(5); @@ -631,10 +636,10 @@ describe("matchersUtil", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var setA = new Set(); + var setA = new Set(); // eslint-disable-line compat/compat setA.add(3); setA.add(6); - var setB = new Set(); + var setB = new Set(); // eslint-disable-line compat/compat setB.add(6); setB.add(3); @@ -645,24 +650,23 @@ describe("matchersUtil", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var setA1 = new Set(); + var setA1 = new Set(); // eslint-disable-line compat/compat setA1.add(['a',3]); setA1.add([6,1]); - var setA2 = new Set(); + var setA2 = new Set(); // eslint-disable-line compat/compat setA1.add(['y',3]); setA1.add([6,1]); - var setA = new Set(); + var setA = new Set(); // eslint-disable-line compat/compat setA.add(setA1); setA.add(setA2); - - var setB1 = new Set(); + var setB1 = new Set(); // eslint-disable-line compat/compat setB1.add([6,1]); setB1.add(['a',3]); - var setB2 = new Set(); + var setB2 = new Set(); // eslint-disable-line compat/compat setB1.add([6,1]); setB1.add(['y',3]); - var setB = new Set(); + var setB = new Set(); // eslint-disable-line compat/compat setB.add(setB1); setB.add(setB2); @@ -673,10 +677,10 @@ describe("matchersUtil", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var setA = new Set(); + var setA = new Set(); // eslint-disable-line compat/compat setA.add([[1,2], [3,4]]); setA.add([[5,6], [7,8]]); - var setB = new Set(); + var setB = new Set(); // eslint-disable-line compat/compat setB.add([[5,6], [7,8]]); setB.add([[1,2], [3,4]]); @@ -686,11 +690,11 @@ describe("matchersUtil", function() { it("fails for sets with different elements", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var setA = new Set(); + var setA = new Set(); // eslint-disable-line compat/compat setA.add(6); setA.add(3); setA.add(5); - var setB = new Set(); + var setB = new Set(); // eslint-disable-line compat/compat setB.add(6); setB.add(4); setB.add(5); @@ -701,10 +705,10 @@ describe("matchersUtil", function() { it("fails for sets of different size", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var setA = new Set(); + var setA = new Set(); // eslint-disable-line compat/compat setA.add(6); setA.add(3); - var setB = new Set(); + var setB = new Set(); // eslint-disable-line compat/compat setB.add(6); setB.add(4); setB.add(5); @@ -715,15 +719,15 @@ describe("matchersUtil", function() { it("passes when comparing two empty maps", function() { jasmine.getEnv().requireFunctioningMaps(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - expect(matchersUtil.equals(new Map(), new Map())).toBe(true); + expect(matchersUtil.equals(new Map(), new Map())).toBe(true); // eslint-disable-line compat/compat }); it("passes when comparing identical maps", function() { jasmine.getEnv().requireFunctioningMaps(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var mapA = new Map(); + var mapA = new Map(); // eslint-disable-line compat/compat mapA.set(6, 5); - var mapB = new Map(); + var mapB = new Map(); // eslint-disable-line compat/compat mapB.set(6, 5); expect(matchersUtil.equals(mapA, mapB)).toBe(true); }); @@ -731,10 +735,10 @@ describe("matchersUtil", function() { it("passes when comparing identical maps with different insertion order", function() { jasmine.getEnv().requireFunctioningMaps(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var mapA = new Map(); + var mapA = new Map(); // eslint-disable-line compat/compat mapA.set("a", 3); mapA.set(6, 1); - var mapB = new Map(); + var mapB = new Map(); // eslint-disable-line compat/compat mapB.set(6, 1); mapB.set("a", 3); expect(matchersUtil.equals(mapA, mapB)).toBe(true); @@ -743,10 +747,10 @@ describe("matchersUtil", function() { it("fails for maps with different elements", function() { jasmine.getEnv().requireFunctioningMaps(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var mapA = new Map(); + var mapA = new Map(); // eslint-disable-line compat/compat mapA.set(6, 3); mapA.set(5, 1); - var mapB = new Map(); + var mapB = new Map(); // eslint-disable-line compat/compat mapB.set(6, 4); mapB.set(5, 1); @@ -756,9 +760,9 @@ describe("matchersUtil", function() { it("fails for maps of different size", function() { jasmine.getEnv().requireFunctioningMaps(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var mapA = new Map(); + var mapA = new Map(); // eslint-disable-line compat/compat mapA.set(6, 3); - var mapB = new Map(); + var mapB = new Map(); // eslint-disable-line compat/compat mapB.set(6, 4); mapB.set(5, 1); expect(matchersUtil.equals(mapA, mapB)).toBe(false); @@ -829,7 +833,7 @@ describe("matchersUtil", function() { expect(diffBuilder.setRoots).toHaveBeenCalledWith(actual, expected); expect(diffBuilder.withPath).toHaveBeenCalledWith('x', jasmine.any(Function)); - expect(diffBuilder.recordMismatch). toHaveBeenCalledWith(); + expect(diffBuilder.recordMismatch).toHaveBeenCalledWith(); }); it("records both objects when the tester does not implement valuesForDiff", function() { @@ -846,7 +850,7 @@ describe("matchersUtil", function() { expect(diffBuilder.setRoots).toHaveBeenCalledWith(actual, expected); expect(diffBuilder.withPath).toHaveBeenCalledWith('x', jasmine.any(Function)); - expect(diffBuilder.recordMismatch). toHaveBeenCalledWith(); + expect(diffBuilder.recordMismatch).toHaveBeenCalledWith(); }); }); @@ -981,7 +985,7 @@ describe("matchersUtil", function() { var matchersUtil = new jasmineUnderTest.MatchersUtil(); var setItem = {'foo': 'bar'}; - var set = new Set(); + var set = new Set(); // eslint-disable-line compat/compat set.add(setItem); expect(matchersUtil.contains(set, setItem)).toBe(true); @@ -992,7 +996,7 @@ describe("matchersUtil", function() { jasmine.getEnv().requireFunctioningSets(); var matchersUtil = new jasmineUnderTest.MatchersUtil(); - var set = new Set(); + var set = new Set(); // eslint-disable-line compat/compat set.add({'foo': 'bar'}); expect(matchersUtil.contains(set, {'foo': 'bar'})).toBe(false); diff --git a/spec/core/matchers/toEqualSpec.js b/spec/core/matchers/toEqualSpec.js index 3a249364..db7f3ffb 100644 --- a/spec/core/matchers/toEqualSpec.js +++ b/spec/core/matchers/toEqualSpec.js @@ -261,8 +261,8 @@ describe("toEqual", function() { it("reports mismatches between arrays of different types", function() { jasmine.getEnv().requireFunctioningTypedArrays(); - var actual = new Uint32Array([1, 2, 3]), - expected = new Uint16Array([1, 2, 3]), + var actual = new Uint32Array([1, 2, 3]), // eslint-disable-line compat/compat + expected = new Uint16Array([1, 2, 3]), // eslint-disable-line compat/compat message = "Expected Uint32Array [ 1, 2, 3 ] to equal Uint16Array [ 1, 2, 3 ]."; expect(compareEquals(actual, expected).message).toEqual(message); @@ -429,17 +429,30 @@ describe("toEqual", function() { expect(compareEquals(actual, expected).message).toEqual(message); }); - it("reports mismatches involving objectContaining and an object", function() { - var actual = {x: {a: 1, b: 4, c: 3, extra: 'ignored'}}; - var expected = {x: jasmineUnderTest.objectContaining({a: 1, b: 2, c: 3})}; - expect(compareEquals(actual, expected).message).toEqual('Expected $.x.b = 4 to equal 2.'); + it('reports mismatches between an object and objectContaining', function() { + var actual = {a: 1, b: 4, c: 3, extra: 'ignored'}; + var expected = jasmineUnderTest.objectContaining({a: 1, b: 2, c: 3, d: 4}); + expect(compareEquals(actual, expected).message) + .toEqual( + 'Expected $.b = 4 to equal 2.\n' + + 'Expected $.d = undefined to equal 4.' + ); }); it("reports mismatches between a non-object and objectContaining", function() { - var actual = {x: 1}; - var expected = {x: jasmineUnderTest.objectContaining({a: 1})}; + var actual = 1; + var expected = jasmineUnderTest.objectContaining({a: 1}); expect(compareEquals(actual, expected).message).toEqual( - "Expected $.x = 1 to equal ''." + "Expected 1 to equal ''." + ); + }); + + it("reports mismatches involving a nested objectContaining", function() { + var actual = {x: {a: 1, b: 4, c: 3, extra: 'ignored'}}; + var expected = {x: jasmineUnderTest.objectContaining({a: 1, b: 2, c: 3, d: 4})}; + expect(compareEquals(actual, expected).message).toEqual( + 'Expected $.x.b = 4 to equal 2.\n' + + 'Expected $.x.d = undefined to equal 4.' ); }); @@ -448,9 +461,9 @@ describe("toEqual", function() { it("reports mismatches between Sets", function() { jasmine.getEnv().requireFunctioningSets(); - var actual = new Set(); + var actual = new Set(); // eslint-disable-line compat/compat actual.add(1); - var expected = new Set(); + var expected = new Set(); // eslint-disable-line compat/compat expected.add(2); var message = 'Expected Set( 1 ) to equal Set( 2 ).'; @@ -460,9 +473,9 @@ describe("toEqual", function() { it("reports deep mismatches within Sets", function() { jasmine.getEnv().requireFunctioningSets(); - var actual = new Set(); + var actual = new Set(); // eslint-disable-line compat/compat actual.add({x: 1}); - var expected = new Set(); + var expected = new Set(); // eslint-disable-line compat/compat expected.add({x: 2}); var message = 'Expected Set( Object({ x: 1 }) ) to equal Set( Object({ x: 2 }) ).'; @@ -472,9 +485,9 @@ describe("toEqual", function() { it("reports mismatches between Sets nested in objects", function() { jasmine.getEnv().requireFunctioningSets(); - var actualSet = new Set(); + var actualSet = new Set(); // eslint-disable-line compat/compat actualSet.add(1); - var expectedSet = new Set(); + var expectedSet = new Set(); // eslint-disable-line compat/compat expectedSet.add(2); var actual = { sets: [actualSet] }; @@ -487,10 +500,10 @@ describe("toEqual", function() { it("reports mismatches between Sets of different lengths", function() { jasmine.getEnv().requireFunctioningSets(); - var actual = new Set(); + var actual = new Set(); // eslint-disable-line compat/compat actual.add(1); actual.add(2); - var expected = new Set(); + var expected = new Set(); // eslint-disable-line compat/compat expected.add(2); var message = 'Expected Set( 1, 2 ) to equal Set( 2 ).'; @@ -501,10 +514,10 @@ describe("toEqual", function() { jasmine.getEnv().requireFunctioningSets(); // Use 'duplicate' object in actual so sizes match - var actual = new Set(); + var actual = new Set(); // eslint-disable-line compat/compat actual.add({x: 1}); actual.add({x: 1}); - var expected = new Set(); + var expected = new Set(); // eslint-disable-line compat/compat expected.add({x: 1}); expected.add({x: 2}); var message = 'Expected Set( Object({ x: 1 }), Object({ x: 1 }) ) to equal Set( Object({ x: 1 }), Object({ x: 2 }) ).'; @@ -516,10 +529,10 @@ describe("toEqual", function() { jasmine.getEnv().requireFunctioningSets(); // Use 'duplicate' object in expected so sizes match - var actual = new Set(); + var actual = new Set(); // eslint-disable-line compat/compat actual.add({x: 1}); actual.add({x: 2}); - var expected = new Set(); + var expected = new Set(); // eslint-disable-line compat/compat expected.add({x: 1}); expected.add({x: 1}); var message = 'Expected Set( Object({ x: 1 }), Object({ x: 2 }) ) to equal Set( Object({ x: 1 }), Object({ x: 1 }) ).'; @@ -533,9 +546,9 @@ describe("toEqual", function() { jasmine.getEnv().requireFunctioningMaps(); // values are the same but with different object identity - var actual = new Map(); + var actual = new Map(); // eslint-disable-line compat/compat actual.set('a',{x:1}); - var expected = new Map(); + var expected = new Map(); // eslint-disable-line compat/compat expected.set('a',{x:1}); expect(compareEquals(actual, expected).pass).toBe(true); @@ -544,9 +557,9 @@ describe("toEqual", function() { it("reports deep mismatches within Maps", function() { jasmine.getEnv().requireFunctioningMaps(); - var actual = new Map(); + var actual = new Map(); // eslint-disable-line compat/compat actual.set('a',{x:1}); - var expected = new Map(); + var expected = new Map(); // eslint-disable-line compat/compat expected.set('a',{x:2}); var message = "Expected Map( [ 'a', Object({ x: 1 }) ] ) to equal Map( [ 'a', Object({ x: 2 }) ] )."; @@ -556,9 +569,9 @@ describe("toEqual", function() { it("reports mismatches between Maps nested in objects", function() { jasmine.getEnv().requireFunctioningMaps(); - var actual = {Maps:[new Map()]}; + var actual = {Maps:[new Map()]}; // eslint-disable-line compat/compat actual.Maps[0].set('a',1); - var expected = {Maps:[new Map()]}; + var expected = {Maps:[new Map()]}; // eslint-disable-line compat/compat expected.Maps[0].set('a',2); var message = "Expected $.Maps[0] = Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ] )."; @@ -569,9 +582,9 @@ describe("toEqual", function() { it("reports mismatches between Maps of different lengths", function() { jasmine.getEnv().requireFunctioningMaps(); - var actual = new Map(); + var actual = new Map(); // eslint-disable-line compat/compat actual.set('a',1); - var expected = new Map(); + var expected = new Map(); // eslint-disable-line compat/compat expected.set('a',2); expected.set('b',1); var message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ], [ 'b', 1 ] )."; @@ -582,9 +595,9 @@ describe("toEqual", function() { it("reports mismatches between Maps with equal values but differing keys", function() { jasmine.getEnv().requireFunctioningMaps(); - var actual = new Map(); + var actual = new Map(); // eslint-disable-line compat/compat actual.set('a',1); - var expected = new Map(); + var expected = new Map(); // eslint-disable-line compat/compat expected.set('b',1); var message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'b', 1 ] )."; @@ -594,9 +607,9 @@ describe("toEqual", function() { it("does not report mismatches between Maps with keys with same object identity", function() { jasmine.getEnv().requireFunctioningMaps(); var key = {x: 1}; - var actual = new Map(); + var actual = new Map(); // eslint-disable-line compat/compat actual.set(key,2); - var expected = new Map(); + var expected = new Map(); // eslint-disable-line compat/compat expected.set(key,2); expect(compareEquals(actual, expected).pass).toBe(true); @@ -605,9 +618,9 @@ describe("toEqual", function() { it("reports mismatches between Maps with identical keys with different object identity", function() { jasmine.getEnv().requireFunctioningMaps(); - var actual = new Map(); + var actual = new Map(); // eslint-disable-line compat/compat actual.set({x:1},2); - var expected = new Map(); + var expected = new Map(); // eslint-disable-line compat/compat expected.set({x:1},2); var message = "Expected Map( [ Object({ x: 1 }), 2 ] ) to equal Map( [ Object({ x: 1 }), 2 ] )."; @@ -617,9 +630,9 @@ describe("toEqual", function() { it("does not report mismatches when comparing Map key to jasmine.anything()", function() { jasmine.getEnv().requireFunctioningMaps(); - var actual = new Map(); + var actual = new Map(); // eslint-disable-line compat/compat actual.set('a',1); - var expected = new Map(); + var expected = new Map(); // eslint-disable-line compat/compat expected.set(jasmineUnderTest.anything(),1); expect(compareEquals(actual, expected).pass).toBe(true); @@ -629,10 +642,10 @@ describe("toEqual", function() { jasmine.getEnv().requireFunctioningMaps(); jasmine.getEnv().requireFunctioningSymbols(); - var key = Symbol(); - var actual = new Map(); + var key = Symbol(); // eslint-disable-line compat/compat + var actual = new Map(); // eslint-disable-line compat/compat actual.set(key,1); - var expected = new Map(); + var expected = new Map(); // eslint-disable-line compat/compat expected.set(key,1); expect(compareEquals(actual, expected).pass).toBe(true); @@ -642,10 +655,10 @@ describe("toEqual", function() { jasmine.getEnv().requireFunctioningMaps(); jasmine.getEnv().requireFunctioningSymbols(); - var actual = new Map(); - actual.set(Symbol(),1); - var expected = new Map(); - expected.set(Symbol(),1); + var actual = new Map(); // eslint-disable-line compat/compat + actual.set(Symbol(),1); // eslint-disable-line compat/compat + var expected = new Map(); // eslint-disable-line compat/compat + expected.set(Symbol(),1); // eslint-disable-line compat/compat var message = "Expected Map( [ Symbol(), 1 ] ) to equal Map( [ Symbol(), 1 ] )."; expect(compareEquals(actual, expected).message).toBe(message); @@ -655,9 +668,9 @@ describe("toEqual", function() { jasmine.getEnv().requireFunctioningMaps(); jasmine.getEnv().requireFunctioningSymbols(); - var actual = new Map(); - actual.set(Symbol(),1); - var expected = new Map(); + var actual = new Map(); // eslint-disable-line compat/compat + actual.set(Symbol(),1); // eslint-disable-line compat/compat + var expected = new Map(); // eslint-disable-line compat/compat expected.set(jasmineUnderTest.anything(),1); expect(compareEquals(actual, expected).pass).toBe(true); diff --git a/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js new file mode 100644 index 00000000..d79711a5 --- /dev/null +++ b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js @@ -0,0 +1,95 @@ +describe("toHaveBeenCalledOnceWith", function () { + + it("passes when the actual was called only once and with matching parameters", function () { + var pp = jasmineUnderTest.makePrettyPrinter(), + util = new jasmineUnderTest.MatchersUtil({ pp: pp }), + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'b'); + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(true); + expect(result.message).toEqual("Expected spy called-spy to have been called 0 times, multiple times, or once, but with arguments different from:\n [ 'a', 'b' ]\nBut the actual call was:\n [ 'a', 'b' ].\n\n"); + }); + + it("supports custom equality testers", function () { + var customEqualityTesters = [function() { return true; }], + matchersUtil = new jasmineUnderTest.MatchersUtil({customTesters: customEqualityTesters}), + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(matchersUtil), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'b'); + result = matcher.compare(calledSpy, 'a', 'a'); + + expect(result.pass).toBe(true); + }); + + it("fails when the actual was never called", function () { + var pp = jasmineUnderTest.makePrettyPrinter(), + util = new jasmineUnderTest.MatchersUtil({ pp: pp }), + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(false); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut it was never called.\n\n"); + }); + + it("fails when the actual was called once with different parameters", function () { + var pp = jasmineUnderTest.makePrettyPrinter(), + util = new jasmineUnderTest.MatchersUtil({ pp: pp }), + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'c'); + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(false); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual call was:\n [ 'a', 'c' ].\nExpected $[1] = 'c' to equal 'b'.\n\n"); + }); + + it("fails when the actual was called multiple times with expected parameters", function () { + var pp = jasmineUnderTest.makePrettyPrinter(), + util = new jasmineUnderTest.MatchersUtil({ pp: pp }), + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'b'); + calledSpy('a', 'b'); + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(false); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual calls were:\n [ 'a', 'b' ],\n [ 'a', 'b' ].\n\n"); + }); + + it("fails when the actual was called multiple times (one of them - with expected parameters)", function () { + var pp = jasmineUnderTest.makePrettyPrinter(), + util = new jasmineUnderTest.MatchersUtil({ pp: pp }), + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'b'); + calledSpy('a', 'c'); + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(false); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual calls were:\n [ 'a', 'b' ],\n [ 'a', 'c' ].\n\n"); + }); + + it("throws an exception when the actual is not a spy", function () { + var pp = jasmineUnderTest.makePrettyPrinter(), + util = new jasmineUnderTest.MatchersUtil({ pp: pp }), + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + fn = function () { }; + + expect(function () { matcher.compare(fn) }).toThrowError(/Expected a spy, but got Function./); + }); +}); diff --git a/spec/core/matchers/toHaveSizeSpec.js b/spec/core/matchers/toHaveSizeSpec.js new file mode 100644 index 00000000..d25e39e7 --- /dev/null +++ b/spec/core/matchers/toHaveSizeSpec.js @@ -0,0 +1,138 @@ +/* eslint-disable compat/compat */ +describe('toHaveSize', function() { + 'use strict'; + + it('passes for an array whose length matches', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare([1, 2], 2); + + expect(result.pass).toBe(true); + }); + + it('fails for an array whose length does not match', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare([1, 2, 3], 2); + + expect(result.pass).toBe(false); + }); + + it('passes for an object with the proper number of keys', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare({a: 1, b: 2}, 2); + + expect(result.pass).toBe(true); + }); + + it('fails for an object with a different number of keys', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare({a: 1, b: 2}, 1); + + expect(result.pass).toBe(false); + }); + + it('passes for an object with an explicit `length` property that matches', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare({a: 1, b: 2, length: 5}, 5); + + expect(result.pass).toBe(true); + }); + + it('fails for an object with an explicit `length` property that does not match', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare({a: 1, b: 2, length: 5}, 1); + + expect(result.pass).toBe(false); + }); + + it('passes for a string whose length matches', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare('ab', 2); + + expect(result.pass).toBe(true); + }); + + it('fails for a string whose length does not match', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare('abc', 2); + + expect(result.pass).toBe(false); + }); + + it('passes for a Map whose length matches', function() { + jasmine.getEnv().requireFunctioningMaps(); + + var map = new Map(); + map.set('a',1); + map.set('b',2); + + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare(map, 2); + + expect(result.pass).toBe(true); + }); + + it('fails for a Map whose length does not match', function() { + jasmine.getEnv().requireFunctioningMaps(); + + var map = new Map(); + map.set('a',1); + map.set('b',2); + + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare(map, 1); + + expect(result.pass).toBe(false); + }); + + it('passes for a Set whose length matches', function() { + jasmine.getEnv().requireFunctioningSets(); + + var set = new Set(); + set.add('a'); + set.add('b'); + + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare(set, 2); + + expect(result.pass).toBe(true); + }); + + it('fails for a Set whose length does not match', function() { + jasmine.getEnv().requireFunctioningSets(); + + var set = new Set(); + set.add('a'); + set.add('b'); + + var matcher = jasmineUnderTest.matchers.toHaveSize(), + result = matcher.compare(set, 1); + + expect(result.pass).toBe(false); + }); + + it('throws an error for WeakSet', function() { + jasmine.getEnv().requireWeakSets(); + var matcher = jasmineUnderTest.matchers.toHaveSize(); + + expect(function() { + matcher.compare(new WeakSet(), 2); + }).toThrowError('Cannot get size of [object WeakSet].'); + }); + + it('throws an error for WeakMap', function() { + jasmine.getEnv().requireWeakMaps(); + var matcher = jasmineUnderTest.matchers.toHaveSize(); + + expect(function() { + matcher.compare(new WeakMap(), 2); + }).toThrowError(/Cannot get size of \[object (WeakMap|Object)\]\./); + }); + + it('throws an error for DataView', function() { + var matcher = jasmineUnderTest.matchers.toHaveSize(); + + expect(function() { + matcher.compare(new DataView(new ArrayBuffer(128)), 2); + }).toThrowError(/Cannot get size of \[object (DataView|Object)\]\./); + }); +}); diff --git a/spec/helpers/checkForMap.js b/spec/helpers/checkForMap.js index 46e346a8..b8bdacd3 100644 --- a/spec/helpers/checkForMap.js +++ b/spec/helpers/checkForMap.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ (function(env) { env.hasFunctioningMaps = function() { if (typeof Map === 'undefined') { @@ -43,4 +44,10 @@ env.pending('Browser has incomplete or missing support for Maps'); } }; + + env.requireWeakMaps = function() { + if (typeof WeakMap === 'undefined') { + env.pending('Browser does not have support for WeakMap'); + } + }; })(jasmine.getEnv()); diff --git a/spec/helpers/checkForSet.js b/spec/helpers/checkForSet.js index dacb06aa..cae95db7 100644 --- a/spec/helpers/checkForSet.js +++ b/spec/helpers/checkForSet.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ (function(env) { env.hasFunctioningSets = function() { if (typeof Set === 'undefined') { @@ -47,4 +48,10 @@ env.pending('Browser has incomplete or missing support for Sets'); } }; + + env.requireWeakSets = function() { + if (typeof WeakSet === 'undefined') { + env.pending('Browser does not have support for WeakSet'); + } + }; })(jasmine.getEnv()); diff --git a/spec/helpers/checkForSymbol.js b/spec/helpers/checkForSymbol.js index c1ed5415..8b5e4b1e 100644 --- a/spec/helpers/checkForSymbol.js +++ b/spec/helpers/checkForSymbol.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ (function(env) { function hasFunctioningSymbols() { if (typeof Symbol === 'undefined') { diff --git a/spec/helpers/checkForTypedArrays.js b/spec/helpers/checkForTypedArrays.js index 3b5d6b35..6902120b 100644 --- a/spec/helpers/checkForTypedArrays.js +++ b/spec/helpers/checkForTypedArrays.js @@ -1,3 +1,4 @@ +/* eslint-disable compat/compat */ (function(env) { function hasFunctioningTypedArrays() { if (typeof Uint32Array === 'undefined') { diff --git a/spec/helpers/generator.js b/spec/helpers/generator.js new file mode 100644 index 00000000..b62f5acd --- /dev/null +++ b/spec/helpers/generator.js @@ -0,0 +1,22 @@ +(function(env) { + function getGeneratorFuncCtor() { + try { + eval('var func = function*() {}'); + } catch (e) { + return null; + } + + return Object.getPrototypeOf(func).constructor; + } + + env.makeGeneratorFunction = function(text) { + var GeneratorFunction = getGeneratorFuncCtor(); + return new GeneratorFunction(text || ''); + }; + + env.requireGeneratorFunctions = function() { + if (!getGeneratorFuncCtor()) { + env.pending('Environment does not support generator functions'); + } + }; +})(jasmine.getEnv()); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 3b4f20e5..c41b68bb 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -270,12 +270,14 @@ describe('HtmlReporter', function() { reporter.jasmineStarted({}); reporter.specDone({ status: 'passed', + fullName: 'a spec with a deprecation', deprecationWarnings: [{ message: 'spec deprecation' }], failedExpectations: [], passedExpectations: [] }); reporter.suiteDone({ status: 'passed', + fullName: 'a suite with a deprecation', deprecationWarnings: [{ message: 'suite deprecation' }], failedExpectations: [] }); @@ -287,12 +289,17 @@ describe('HtmlReporter', function() { var alertBars = container.querySelectorAll('.jasmine-alert .jasmine-bar'); expect(alertBars.length).toEqual(4); - expect(alertBars[1].innerHTML).toMatch(/spec deprecation/); + expect(alertBars[1].innerHTML).toMatch( + /spec deprecation.*\(in spec: a spec with a deprecation\)/ + ); expect(alertBars[1].getAttribute('class')).toEqual( 'jasmine-bar jasmine-warning' ); - expect(alertBars[2].innerHTML).toMatch(/suite deprecation/); + expect(alertBars[2].innerHTML).toMatch( + /suite deprecation.*\(in suite: a suite with a deprecation\)/ + ); expect(alertBars[3].innerHTML).toMatch(/global deprecation/); + expect(alertBars[3].innerHTML).not.toMatch(/in /); }); }); diff --git a/spec/html/PrettyPrintHtmlSpec.js b/spec/html/PrettyPrintHtmlSpec.js index fd364d9e..738753ba 100644 --- a/spec/html/PrettyPrintHtmlSpec.js +++ b/spec/html/PrettyPrintHtmlSpec.js @@ -31,7 +31,7 @@ describe('PrettyPrinter (HTML Dependent)', function() { } // Different versions of FF produce different error messages. expect(pp(err)).toMatch( - /Not enough arguments|CustomEvent requires at least 1 argument, but only 0 were passed/ + /Not enough arguments|CustomEvent.*only 0.*passed/ ); } }); diff --git a/spec/support/jasmine-browser.js b/spec/support/jasmine-browser.js index 9ff33cb8..86b7b974 100644 --- a/spec/support/jasmine-browser.js +++ b/spec/support/jasmine-browser.js @@ -18,6 +18,7 @@ module.exports = { specFiles: ['**/*[Ss]pec.js', '!npmPackage/**/*'], helpers: [ 'helpers/asyncAwait.js', + 'helpers/generator.js', 'helpers/BrowserFlags.js', 'helpers/checkForMap.js', 'helpers/checkForSet.js', diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index 5b720636..b4e97736 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -6,6 +6,7 @@ ], "helpers": [ "helpers/asyncAwait.js", + "helpers/generator.js", "helpers/checkForMap.js", "helpers/checkForSet.js", "helpers/checkForSymbol.js", diff --git a/src/core/Env.js b/src/core/Env.js index 39fa276b..41bbea47 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -620,12 +620,22 @@ getJasmineRequireObj().Env = function(j$) { this.deprecated = function(deprecation) { var runnable = currentRunnable() || topSuite; + var context; + + if (runnable === topSuite) { + context = ''; + } else if (runnable === currentSuite()) { + context = ' (in suite: ' + runnable.getFullName() + ')'; + } else { + context = ' (in spec: ' + runnable.getFullName() + ')'; + } + runnable.addDeprecationWarning(deprecation); if ( typeof console !== 'undefined' && typeof console.error === 'function' ) { - console.error('DEPRECATION:', deprecation); + console.error('DEPRECATION: ' + deprecation + context); } }; @@ -767,7 +777,8 @@ getJasmineRequireObj().Env = function(j$) { queueRunnerFactory ); - this.execute = function(runnablesToRun) { + // Both params are optional. + this.execute = function(runnablesToRun, onComplete) { installGlobalErrors(); if (!runnablesToRun) { @@ -875,7 +886,11 @@ getJasmineRequireObj().Env = function(j$) { failedExpectations: topSuite.result.failedExpectations, deprecationWarnings: topSuite.result.deprecationWarnings }, - function() {} + function() { + if (onComplete) { + onComplete(); + } + } ); }); } @@ -1014,6 +1029,7 @@ getJasmineRequireObj().Env = function(j$) { id: getNextSuiteId(), description: description, parentSuite: currentDeclarationSuite, + timer: new j$.Timer(), expectationFactory: expectationFactory, asyncExpectationFactory: suiteAsyncExpectationFactory, expectationResultFactory: expectationResultFactory, @@ -1192,6 +1208,40 @@ getJasmineRequireObj().Env = function(j$) { return spec; }; + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} + * @name Env#setSpecProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + this.setSpecProperty = function(key, value) { + if (!currentRunnable() || currentRunnable() == currentSuite()) { + throw new Error( + "'setSpecProperty' was used when there was no current spec" + ); + } + currentRunnable().setSpecProperty(key, value); + }; + + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} + * @name Env#setSuiteProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + this.setSuiteProperty = function(key, value) { + if (!currentSuite()) { + throw new Error( + "'setSuiteProperty' was used when there was no current suite" + ); + } + currentSuite().setSuiteProperty(key, value); + }; + this.expect = function(actual) { if (!currentRunnable()) { throw new Error( diff --git a/src/core/GlobalErrors.js b/src/core/GlobalErrors.js index 282eb262..b79976d9 100644 --- a/src/core/GlobalErrors.js +++ b/src/core/GlobalErrors.js @@ -96,7 +96,17 @@ getJasmineRequireObj().GlobalErrors = function(j$) { handlers.push(listener); }; - this.popListener = function popListener() { + this.popListener = function popListener(listener) { + if (!listener) { + throw new Error('popListener expects a listener'); + } + + if (listener !== handlers[handlers.length - 1]) { + throw new Error( + 'popListener was passed a different listener than the current one' + ); + } + handlers.pop(); }; } diff --git a/src/core/JsApiReporter.js b/src/core/JsApiReporter.js index 8875a45e..a498f388 100644 --- a/src/core/JsApiReporter.js +++ b/src/core/JsApiReporter.js @@ -6,7 +6,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) { * @hideconstructor */ function JsApiReporter(options) { - var timer = options.timer || j$.noopTimer, + var timer = options.timer || new j$.Timer(), status = 'loaded'; this.started = false; diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js index 28c1153f..be9e7f62 100644 --- a/src/core/QueueRunner.js +++ b/src/core/QueueRunner.js @@ -1,4 +1,6 @@ getJasmineRequireObj().QueueRunner = function(j$) { + var nextid = 1; + function StopExecutionError() {} StopExecutionError.prototype = new Error(); j$.StopExecutionError = StopExecutionError; @@ -18,6 +20,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { function emptyFn() {} function QueueRunner(attrs) { + this.id_ = nextid++; var queueableFns = attrs.queueableFns || []; this.queueableFns = queueableFns.concat(attrs.cleanupFns || []); this.firstCleanupIx = queueableFns.length; @@ -120,7 +123,8 @@ getJasmineRequireObj().QueueRunner = function(j$) { }), errored = false, queueableFn = self.queueableFns[iterativeIndex], - timeoutId; + timeoutId, + maybeThenable; next.fail = function nextFail() { self.fail.apply(null, arguments); @@ -148,7 +152,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { try { if (queueableFn.fn.length === 0) { - var maybeThenable = queueableFn.fn.call(self.userContext); + maybeThenable = queueableFn.fn.call(self.userContext); if (maybeThenable && j$.isFunction_(maybeThenable.then)) { maybeThenable.then(next, onPromiseRejection); @@ -156,7 +160,8 @@ getJasmineRequireObj().QueueRunner = function(j$) { return { completedSynchronously: false }; } } else { - queueableFn.fn.call(self.userContext, next); + maybeThenable = queueableFn.fn.call(self.userContext, next); + this.diagnoseConflictingAsync_(queueableFn.fn, maybeThenable); completedSynchronously = false; return { completedSynchronously: false }; } @@ -209,5 +214,28 @@ getJasmineRequireObj().QueueRunner = function(j$) { }); }; + QueueRunner.prototype.diagnoseConflictingAsync_ = function(fn, retval) { + if (retval && j$.isFunction_(retval.then)) { + // Issue a warning that matches the user's code + if (j$.isAsyncFunction_(fn)) { + this.deprecated( + 'An asynchronous before/it/after ' + + 'function was defined with the async keyword but also took a ' + + 'done callback. This is not supported and will stop working in' + + ' the future. Either remove the done callback (recommended) or ' + + 'remove the async keyword.' + ); + } else { + this.deprecated( + 'An asynchronous before/it/after ' + + 'function took a done callback but also returned a promise. ' + + 'This is not supported and will stop working in the future. ' + + 'Either remove the done callback (recommended) or change the ' + + 'function to not return a promise.' + ); + } + } + }; + return QueueRunner; }; diff --git a/src/core/Spec.js b/src/core/Spec.js index b8791890..83d389d4 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -31,7 +31,7 @@ getJasmineRequireObj().Spec = function(j$) { return true; }; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; - this.timer = attrs.timer || j$.noopTimer; + this.timer = attrs.timer || new j$.Timer(); if (!this.queueableFn.fn) { this.pend(); @@ -48,6 +48,7 @@ getJasmineRequireObj().Spec = function(j$) { * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. + * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} */ this.result = { id: this.id, @@ -57,7 +58,8 @@ getJasmineRequireObj().Spec = function(j$) { passedExpectations: [], deprecationWarnings: [], pendingReason: '', - duration: null + duration: null, + properties: null }; } @@ -74,6 +76,11 @@ getJasmineRequireObj().Spec = function(j$) { } }; + Spec.prototype.setSpecProperty = function(key, value) { + this.result.properties = this.result.properties || {}; + this.result.properties[key] = value; + }; + Spec.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; @@ -96,6 +103,7 @@ getJasmineRequireObj().Spec = function(j$) { fn: function(done) { self.queueableFn.fn = null; self.result.status = self.status(excluded, failSpecWithNoExp); + self.result.duration = self.timer.elapsed(); self.resultCallback(self.result, done); } }; @@ -111,7 +119,6 @@ getJasmineRequireObj().Spec = function(j$) { self.onException.apply(self, arguments); }, onComplete: function() { - self.result.duration = self.timer.elapsed(); onComplete( self.result.status === 'failed' && new j$.StopExecutionError('spec failed') diff --git a/src/core/SpyStrategy.js b/src/core/SpyStrategy.js index 7e86abd6..382164b6 100644 --- a/src/core/SpyStrategy.js +++ b/src/core/SpyStrategy.js @@ -150,10 +150,10 @@ getJasmineRequireObj().SpyStrategy = function(j$) { * @name SpyStrategy#throwError * @since 2.0.0 * @function - * @param {Error|String} something Thing to throw + * @param {Error|Object|String} something Thing to throw */ SpyStrategy.prototype.throwError = function(something) { - var error = something instanceof Error ? something : new Error(something); + var error = j$.isString_(something) ? new Error(something) : something; this.plan = function() { throw error; }; @@ -168,7 +168,13 @@ getJasmineRequireObj().SpyStrategy = function(j$) { * @param {Function} fn The function to invoke with the passed parameters. */ SpyStrategy.prototype.callFake = function(fn) { - if (!(j$.isFunction_(fn) || j$.isAsyncFunction_(fn))) { + if ( + !( + j$.isFunction_(fn) || + j$.isAsyncFunction_(fn) || + j$.isGeneratorFunction_(fn) + ) + ) { throw new Error( 'Argument passed to callFake should be a function, got ' + fn ); diff --git a/src/core/Suite.js b/src/core/Suite.js index 75104068..e660d1f1 100644 --- a/src/core/Suite.js +++ b/src/core/Suite.js @@ -14,7 +14,7 @@ getJasmineRequireObj().Suite = function(j$) { this.beforeAllFns = []; this.afterAllFns = []; - this.timer = attrs.timer || j$.noopTimer; + this.timer = attrs.timer || new j$.Timer(); this.children = []; @@ -27,6 +27,7 @@ getJasmineRequireObj().Suite = function(j$) { * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. + * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} */ this.result = { id: this.id, @@ -34,10 +35,16 @@ getJasmineRequireObj().Suite = function(j$) { fullName: this.getFullName(), failedExpectations: [], deprecationWarnings: [], - duration: null + duration: null, + properties: null }; } + Suite.prototype.setSuiteProperty = function(key, value) { + this.result.properties = this.result.properties || {}; + this.result.properties[key] = value; + }; + Suite.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; diff --git a/src/core/Timer.js b/src/core/Timer.js index 35ef9f32..ebe6983d 100644 --- a/src/core/Timer.js +++ b/src/core/Timer.js @@ -22,12 +22,3 @@ getJasmineRequireObj().Timer = function() { return Timer; }; - -getJasmineRequireObj().noopTimer = function() { - return { - start: function() {}, - elapsed: function() { - return 0; - } - }; -}; diff --git a/src/core/asymmetricEqualityTesterArgCompatShim.js b/src/core/asymmetricEqualityTesterArgCompatShim.js index 1876abe7..7589e021 100644 --- a/src/core/asymmetricEqualityTesterArgCompatShim.js +++ b/src/core/asymmetricEqualityTesterArgCompatShim.js @@ -66,7 +66,9 @@ getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) { for (i = 0; i < props.length; i++) { k = props[i]; - if (k !== 'length') { + // Skip length (dealt with above), and anything that collides with + // MatchesUtil e.g. an Array.prototype.contains method added by user code + if (k !== 'length' && !self[k]) { copyAndDeprecate(self, Array.prototype, k); } } @@ -97,7 +99,7 @@ getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) { }); } - props = Object.getOwnPropertyDescriptors(Array.prototype); + props = Object.getOwnPropertyDescriptors(Array.prototype); // eslint-disable-line compat/compat a = []; for (k in props) { diff --git a/src/core/base.js b/src/core/base.js index e86a12c8..d02606d5 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -76,6 +76,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { return j$.isA_('AsyncFunction', value); }; + j$.isGeneratorFunction_ = function(value) { + return j$.isA_('GeneratorFunction', value); + }; + j$.isTypedArray_ = function(value) { return ( j$.isA_('Float32Array', value) || @@ -148,6 +152,24 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { ); }; + j$.isWeakMap = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + typeof jasmineGlobal.WeakMap !== 'undefined' && + obj.constructor === jasmineGlobal.WeakMap + ); + }; + + j$.isDataView = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + typeof jasmineGlobal.DataView !== 'undefined' && + obj.constructor === jasmineGlobal.DataView + ); + }; + j$.isPromise = function(obj) { return ( typeof jasmineGlobal.Promise !== 'undefined' && diff --git a/src/core/matchers/DiffBuilder.js b/src/core/matchers/DiffBuilder.js index 4e43aeaa..70b52ea7 100644 --- a/src/core/matchers/DiffBuilder.js +++ b/src/core/matchers/DiffBuilder.js @@ -72,12 +72,7 @@ getJasmineRequireObj().DiffBuilder = function (j$) { }; function dereferencePath(objectPath, actual, expected, pp) { - var i, asymmetricResult - - for (i = 0; i < objectPath.components.length; i++) { - actual = actual[objectPath.components[i]]; - expected = expected[objectPath.components[i]]; - + function handleAsymmetricExpected() { if (j$.isAsymmetricEqualityTester_(expected) && j$.isFunction_(expected.valuesForDiff_)) { var asymmetricResult = expected.valuesForDiff_(actual, pp); expected = asymmetricResult.self; @@ -85,6 +80,15 @@ getJasmineRequireObj().DiffBuilder = function (j$) { } } + var i; + handleAsymmetricExpected(); + + for (i = 0; i < objectPath.components.length; i++) { + actual = actual[objectPath.components[i]]; + expected = expected[objectPath.components[i]]; + handleAsymmetricExpected(); + } + return {actual: actual, expected: expected}; } diff --git a/src/core/matchers/async/toBePending.js b/src/core/matchers/async/toBePending.js new file mode 100644 index 00000000..c73aa3d3 --- /dev/null +++ b/src/core/matchers/async/toBePending.js @@ -0,0 +1,26 @@ +/* eslint-disable compat/compat */ +getJasmineRequireObj().toBePending = function(j$) { + /** + * Expect a promise to be pending, i.e. the promise is neither resolved nor rejected. + * @function + * @async + * @name async-matchers#toBePending + * @since 3.6 + * @example + * await expectAsync(aPromise).toBePending(); + */ + return function toBePending() { + return { + compare: function(actual) { + if (!j$.isPromiseLike(actual)) { + throw new Error('Expected toBePending to be called on a promise.'); + } + var want = {}; + return Promise.race([actual, Promise.resolve(want)]).then( + function(got) { return {pass: want === got}; }, + function() { return {pass: false}; } + ); + } + }; + }; +}; diff --git a/src/core/matchers/matchersUtil.js b/src/core/matchers/matchersUtil.js index d20b990a..3bc912c3 100644 --- a/src/core/matchers/matchersUtil.js +++ b/src/core/matchers/matchersUtil.js @@ -3,7 +3,6 @@ getJasmineRequireObj().MatchersUtil = function(j$) { * _Note:_ Do not construct this directly. Jasmine will construct one and * pass it to matchers and asymmetric equality testers. * @name MatchersUtil - * @since 2.0.0 * @classdesc Utilities for use in implementing matchers * @constructor */ @@ -15,6 +14,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) { * taking into account the current set of custom value formatters. * @function * @name MatchersUtil#pp + * @since 3.6.0 * @param {*} value The value to pretty-print * @return {string} The pretty-printed value */ @@ -26,6 +26,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) { * logic as {@link MatchersUtil#equals}. * @function * @name MatchersUtil#contains + * @since 2.0.0 * @param {*} haystack The collection to search * @param {*} needle The value to search for * @param [customTesters] An array of custom equality testers. Deprecated. @@ -125,6 +126,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) { * Determines whether two values are deeply equal to each other. * @function * @name MatchersUtil#equals + * @since 2.0.0 * @param {*} a The first value to compare * @param {*} b The second value to compare * @param [customTesters] An array of custom equality testers. Deprecated. diff --git a/src/core/matchers/requireAsyncMatchers.js b/src/core/matchers/requireAsyncMatchers.js index faa91c02..abe9b8ef 100644 --- a/src/core/matchers/requireAsyncMatchers.js +++ b/src/core/matchers/requireAsyncMatchers.js @@ -1,5 +1,6 @@ getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { var availableMatchers = [ + 'toBePending', 'toBeResolved', 'toBeRejected', 'toBeResolvedTo', diff --git a/src/core/matchers/requireMatchers.js b/src/core/matchers/requireMatchers.js index e8b1fd58..c14ceac9 100644 --- a/src/core/matchers/requireMatchers.js +++ b/src/core/matchers/requireMatchers.js @@ -20,8 +20,10 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) { 'toBeUndefined', 'toContain', 'toEqual', + 'toHaveSize', 'toHaveBeenCalled', 'toHaveBeenCalledBefore', + 'toHaveBeenCalledOnceWith', 'toHaveBeenCalledTimes', 'toHaveBeenCalledWith', 'toHaveClass', diff --git a/src/core/matchers/toHaveBeenCalledOnceWith.js b/src/core/matchers/toHaveBeenCalledOnceWith.js new file mode 100644 index 00000000..2cd607f8 --- /dev/null +++ b/src/core/matchers/toHaveBeenCalledOnceWith.js @@ -0,0 +1,69 @@ +getJasmineRequireObj().toHaveBeenCalledOnceWith = function (j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledOnceWith(...arguments)'); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments. + * @function + * @name matchers#toHaveBeenCalledOnceWith + * @since 3.6.0 + * @param {...Object} - The arguments to look for + * @example + * expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2); + */ + function toHaveBeenCalledOnceWith(util) { + return { + compare: function () { + var args = Array.prototype.slice.call(arguments, 0), + actual = args[0], + expectedArgs = args.slice(1); + + if (!j$.isSpy(actual)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + util.pp(actual) + '.')); + } + + var prettyPrintedCalls = actual.calls.allArgs().map(function (argsForCall) { + return ' ' + util.pp(argsForCall); + }); + + if (actual.calls.count() === 1 && util.contains(actual.calls.allArgs(), expectedArgs)) { + return { + pass: true, + message: 'Expected spy ' + actual.and.identity + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n' + + ' ' + util.pp(expectedArgs) + '\n' + + 'But the actual call was:\n' + + prettyPrintedCalls.join(',\n') + '.\n\n' + }; + } + + function getDiffs() { + return actual.calls.allArgs().map(function (argsForCall, callIx) { + var diffBuilder = new j$.DiffBuilder(); + util.equals(argsForCall, expectedArgs, diffBuilder); + return diffBuilder.getMessage(); + }); + } + + function butString() { + switch (actual.calls.count()) { + case 0: + return 'But it was never called.\n\n'; + case 1: + return 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n' + getDiffs().join('\n') + '\n\n'; + default: + return 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n'; + } + } + + return { + pass: false, + message: 'Expected spy ' + actual.and.identity + ' to have been called only once, and with given args:\n' + + ' ' + util.pp(expectedArgs) + '\n' + + butString() + }; + } + }; + } + + return toHaveBeenCalledOnceWith; +}; diff --git a/src/core/matchers/toHaveSize.js b/src/core/matchers/toHaveSize.js new file mode 100644 index 00000000..03c8d431 --- /dev/null +++ b/src/core/matchers/toHaveSize.js @@ -0,0 +1,42 @@ +getJasmineRequireObj().toHaveSize = function(j$) { + /** + * {@link expect} the actual size to be equal to the expected, using array-like length or object keys size. + * @function + * @name matchers#toHaveSize + * @since 3.6.0 + * @param {Object} expected - Expected size + * @example + * array = [1,2]; + * expect(array).toHaveSize(2); + */ + function toHaveSize() { + return { + compare: function(actual, expected) { + var result = { + pass: false + }; + + if (j$.isA_('WeakSet', actual) || j$.isWeakMap(actual) || j$.isDataView(actual)) { + throw new Error('Cannot get size of ' + actual + '.'); + } + + if (j$.isSet(actual) || j$.isMap(actual)) { + result.pass = actual.size === expected; + } else if (isLength(actual.length)) { + result.pass = actual.length === expected; + } else { + result.pass = Object.keys(actual).length === expected; + } + + return result; + } + }; + } + + var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // eslint-disable-line compat/compat + function isLength(value) { + return (typeof value == 'number') && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER; + } + + return toHaveSize; +}; diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 1bace753..d5ea06a6 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -49,7 +49,6 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.Expector = jRequire.Expector(j$); j$.Expectation = jRequire.Expectation(j$); j$.buildExpectationResult = jRequire.buildExpectationResult(j$); - j$.noopTimer = jRequire.noopTimer(); j$.JsApiReporter = jRequire.JsApiReporter(j$); j$.asymmetricEqualityTesterArgCompatShim = jRequire.asymmetricEqualityTesterArgCompatShim( j$ diff --git a/src/core/requireInterface.js b/src/core/requireInterface.js index 15a13cd1..6473ffaa 100644 --- a/src/core/requireInterface.js +++ b/src/core/requireInterface.js @@ -168,6 +168,30 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.afterAll.apply(env, arguments); }, + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} + * @name setSpecProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + setSpecProperty: function(key, value) { + return env.setSpecProperty(key, value); + }, + + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} + * @name setSuiteProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + setSuiteProperty: function(key, value) { + return env.setSuiteProperty(key, value); + }, + /** * Create an expectation for a spec. * @name expect @@ -327,7 +351,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 3.6.0 * @function * @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise. - * @see custom_object_formatter + * @see custom_object_formatters */ jasmine.addCustomObjectFormatter = function(formatter) { return env.addCustomObjectFormatter(formatter); diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 1c6478cf..e591ccac 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -101,7 +101,7 @@ jasmineRequire.HtmlReporter = function(j$) { if (result.status === 'failed') { failures.push(failureDom(result)); } - addDeprecationWarnings(result); + addDeprecationWarnings(result, 'suite'); }; this.specStarted = function(result) { @@ -137,7 +137,7 @@ jasmineRequire.HtmlReporter = function(j$) { failures.push(failureDom(result)); } - addDeprecationWarnings(result); + addDeprecationWarnings(result, 'spec'); }; this.displaySpecInCorrectFormat = function(result) { @@ -276,14 +276,27 @@ jasmineRequire.HtmlReporter = function(j$) { addDeprecationWarnings(doneResult); - var warningBarClassName = 'jasmine-bar jasmine-warning'; for (i = 0; i < deprecationWarnings.length; i++) { - var warning = deprecationWarnings[i]; + var context; + + switch (deprecationWarnings[i].runnableType) { + case 'spec': + context = '(in spec: ' + deprecationWarnings[i].runnableName + ')'; + break; + case 'suite': + context = '(in suite: ' + deprecationWarnings[i].runnableName + ')'; + break; + default: + context = ''; + } + alert.appendChild( createDom( 'span', - { className: warningBarClassName }, - 'DEPRECATION: ' + warning + { className: 'jasmine-bar jasmine-warning' }, + 'DEPRECATION: ' + deprecationWarnings[i].message, + createDom('br'), + context ) ); } @@ -321,9 +334,11 @@ jasmineRequire.HtmlReporter = function(j$) { find('.jasmine-failures-menu').onclick = function() { setMenuModeTo('jasmine-failure-list'); + return false; }; find('.jasmine-spec-list-menu').onclick = function() { setMenuModeTo('jasmine-spec-list'); + return false; }; setMenuModeTo('jasmine-failure-list'); @@ -592,12 +607,16 @@ jasmineRequire.HtmlReporter = function(j$) { return addToExistingQueryString('spec', els.join(' ')); } - function addDeprecationWarnings(result) { + function addDeprecationWarnings(result, runnableType) { if (result && result.deprecationWarnings) { for (var i = 0; i < result.deprecationWarnings.length; i++) { var warning = result.deprecationWarnings[i].message; if (!j$.util.arrayContains(warning)) { - deprecationWarnings.push(warning); + deprecationWarnings.push({ + message: warning, + runnableName: result.fullName, + runnableType: runnableType + }); } } } diff --git a/src/html/requireHtml.js b/src/html/requireHtml.js index b60c3ba5..a8fe2a1e 100644 --- a/src/html/requireHtml.js +++ b/src/html/requireHtml.js @@ -1,3 +1,5 @@ +var jasmineRequire = window.jasmineRequire || require('./jasmine.js'); + jasmineRequire.html = function(j$) { j$.ResultsNode = jasmineRequire.ResultsNode(); j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);