diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..9f3680f5 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,163 @@ +# Run tests against supported Node versions, and (except for pull requests) +# against supported browsers. + +version: 2.1 + +orbs: + node: circleci/node@3.0.0 + +executors: + node14: + docker: + - image: circleci/node:14 + working_directory: ~/workspace + node12: + docker: + - image: circleci/node:12 + working_directory: ~/workspace + node10: + docker: + - image: circleci/node:10 + working_directory: ~/workspace + +jobs: + build: + parameters: + executor: + type: executor + executor: << parameters.executor >> + steps: + - checkout + - run: + name: Report Node and NPM versions + command: echo "Using Node $(node --version) and NPM $(npm --version)" + - run: + name: Install dependencies + command: npm install + - run: + name: Build + command: npm run build + - persist_to_workspace: + root: . + paths: + - . + + test_node: &test_node + parameters: + executor: + type: executor + executor: << parameters.executor >> + steps: + - attach_workspace: + at: . + - run: + name: Run tests + command: npm test + + # Warning: Sometimes takes a very long time (>25 minutes) on Circle. + # Probably not a good idea to run it from anything but a nightly. + test_node_with_long_property_tests: + <<: *test_node + environment: + JASMINE_LONG_PROPERTY_TESTS: y + + test_browsers: + executor: node14 + steps: + - attach_workspace: + at: . + - run: + name: Install Sauce Connect + command: | + cd /tmp + curl https://saucelabs.com/downloads/sc-4.6.4-linux.tar.gz | tar zxf - + chmod +x sc-4.6.4-linux/bin/sc + mkdir ~/workspace/bin + cp sc-4.6.4-linux/bin/sc ~/workspace/bin + ~/workspace/bin/sc --version + - run: + name: Run tests + command: | + # Do everything in one step because Sauce Connect won't exit + # cleanly if we kill it from a different step than it started in. + + export PATH=$PATH:$HOME/workspace/bin + export SAUCE_TUNNEL_IDENTIFIER=$CIRCLE_BUILD_NUM + scripts/start-sauce-connect sauce-pidfile + set +o errexit + scripts/run-all-browsers + exitcode=$? + set -o errexit + scripts/stop-sauce-connect $(cat sauce-pidfile) + exit $exitcode + +workflows: + version: 2 + cron: + triggers: + - schedule: + # The choice of hour is somewhat load-bearing. test_browser currently + # tends to fail if there are other Sauce tunnels open. So we only + # run it at a time when that's unlikely. + # Times are UTC. + cron: "0 10 * * *" + filters: + branches: + only: + - main + jobs: + - build: + executor: node14 + name: build_node_14 + - build: + executor: node12 + name: build_node_12 + - build: + executor: node10 + name: build_node_10 + - test_node_with_long_property_tests: + executor: node14 + requires: + - build_node_14 + - test_node: + executor: node12 + name: test_node_12 + requires: + - build_node_12 + - test_node: + executor: node10 + name: test_node_10 + requires: + - build_node_10 + - test_browsers: + requires: + - build_node_14 + filters: + branches: + only: main + push: + jobs: + - build: + executor: node14 + name: build_node_14 + - build: + executor: node12 + name: build_node_12 + - build: + executor: node10 + name: build_node_10 + - test_node: + executor: node14 + name: test_node_14 + requires: + - build_node_14 + - test_node: + executor: node12 + name: test_node_12 + requires: + - build_node_12 + - test_node: + executor: node10 + name: test_node_10 + requires: + - build_node_10 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8f592ea0..6c961b54 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -56,15 +56,15 @@ Jasmine supports the following environments: * Browsers * IE10+ - * Edge Latest - * Firefox Latest - * Chrome Latest + * Edge latest + * Firefox latest, 78, and 68 + * Chrome latest * Safari 8+ * Node.js - * 8 * 10 * 12 + * 14 ## Development @@ -99,11 +99,18 @@ Follow these tips and your pull request, patch, or suggestion is much more likel ### Running Specs -Jasmine uses some internal tooling to test itself in browser on Travis. This tooling _should_ work locally as well. +Be sure to run the tests in at least one supported Node version and at least a +couple of supported browsers. It's also a good idea to run the tests in Internet +Explorer if you've touched code in `src/html`, if your change involves newer +JavaScript language/runtime features, or if you're unfamiliar with writing code +for older browsers. To run the tests in Node, simply use `npm test` as described +above. To run the tests in a browser, run `npm run serve` and then visit +`http://localhost:8888`. - $ node spec/support/ci.js +If you have the necessary Selenium drivers installed, you can also use Jasmine's +CI tooling: -You can also set the `JASMINE_BROWSER` environment variable to specify which browser should be used. + $ JASMINE_BROWSER= node spec/support/ci.js The easiest way to run the tests in **Internet Explorer** is to run a VM that has IE installed. It's easy to do this with VirtualBox. @@ -112,7 +119,7 @@ The easiest way to run the tests in **Internet Explorer** is to run a VM that ha 1. Unzip the downloaded archive. There should be an OVA file inside. 1. In VirtualBox, choose `File > Import Appliance` and select the OVA file. Accept the default settings in the dialog that appears. Now you have a Windows VM! 1. Run the VM and start IE. -1. With `npm run serve` running on your host machine, navigate to `http://10.0.2.2:8888` in IE. +1. With `npm run serve` running on your host machine, navigate to `http://:8888` in IE. ## Before Committing or Submitting a Pull Request @@ -123,5 +130,5 @@ The easiest way to run the tests in **Internet Explorer** is to run a VM that ha * 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 main -Note that we use Travis for Continuous Integration. We only accept green pull requests. +Note that we use Circle CI for Continuous Integration. We only accept green pull requests. diff --git a/.npmignore b/.npmignore index 16e59f5c..2f53304a 100644 --- a/.npmignore +++ b/.npmignore @@ -16,7 +16,8 @@ jasmine-core.gemspec .jshintrc .rspec .sass-cache/ -.travis.yml +.circleci +scripts/ *.sh *.py Gruntfile.js diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4015d631..00000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -language: node_js -node_js: 14 - -script: $TEST_COMMAND - -env: - global: - - USE_SAUCE=true - - TEST_COMMAND="bash travis-core-script.sh" - - secure: WSPWhlnC4mWSnSPquX+m1/BCu5ch5NygkaHuM2Nea7lD8oS3XLX8QncZZAsQ4lnNfqoDDuBOizG0AESiqNvE4y6x5qvLLTS6q+ce255ZEMZ71TBdZgDEEvGMEjOPPsVXiXyTQOP1lwOPlrbZvaPgWV7e11KIBab6DfFcQpnvDgo= - - secure: SW7CJhZnwaNT749Gdnhvqb5rbXlAOsygUAzh9qhtyvbqXKkmJdBIEsO01YF6pbju1X2twE9JvWCOxeZju43NgQChJlPsGbjY2j3k/TdQeTAJesQe2K7ytwghunI30gjEovtRH0T3w1EmcKPH8yj5eBIcB2OYoJHx8KEC7e68q1g= - -matrix: - include: - - node_js: "14" - env: JASMINE_LONG_PROPERTY_TESTS="y" TEST_COMMAND="npm test" - - node_js: "12" - env: TEST_COMMAND="npm test" - - node_js: "10" - env: TEST_COMMAND="npm test" - - env: JASMINE_BROWSER="internet explorer" SAUCE_BROWSER_VERSION=11 SAUCE_OS="Windows 8.1" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="internet explorer" SAUCE_BROWSER_VERSION=10 SAUCE_OS="Windows 8" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="firefox" SAUCE_BROWSER_VERSION='' SAUCE_OS="Windows 10" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="firefox" SAUCE_BROWSER_VERSION='78' SAUCE_OS="Windows 10" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="firefox" SAUCE_BROWSER_VERSION='68' SAUCE_OS="Windows 10" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="chrome" SAUCE_BROWSER_VERSION='' SAUCE_OS="Windows 10" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="safari" SAUCE_BROWSER_VERSION="14" SAUCE_OS="OS X 11.0" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="safari" SAUCE_BROWSER_VERSION="13" SAUCE_OS="OS X 10.13" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="safari" SAUCE_BROWSER_VERSION="8" SAUCE_OS="OS X 10.10" - if: type != pull_request - addons: - sauce_connect: true - - env: JASMINE_BROWSER="MicrosoftEdge" SAUCE_BROWSER_VERSION="" SAUCE_OS="Windows 10" - if: type != pull_request - addons: - sauce_connect: true diff --git a/README.md b/README.md index d10056ca..177face0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [](http://jasmine.github.io) -[![Build Status](https://travis-ci.com/jasmine/jasmine.svg?branch=main)](https://travis-ci.com/jasmine/jasmine) +[![Build Status](https://circleci.com/gh/jasmine/jasmine.svg?style=shield)](https://circleci.com/gh/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) @@ -11,8 +11,6 @@ Jasmine is a Behavior Driven Development testing framework for JavaScript. It do 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). -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/main/.github/CONTRIBUTING.md). diff --git a/RELEASE.md b/RELEASE.md index 8d0689b1..a1f886fb 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -35,9 +35,10 @@ When ready to release - specs are all green and the stories are done: ### Commit and push core changes +1. Run the browser tests using `scripts/run-all-browsers`. 1. Commit release notes and version changes (jasmine.js, version.rb, package.json) 1. Push -1. Wait for Travis to go green +1. Wait for Circle CI to go green ### Build standalone distribution @@ -77,7 +78,7 @@ Probably only need to do this when releasing a minor version, and not a patch ve 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. Wait for Circle CI to go green again. 1. `grunt release `. (Note: This will publish the package by running `npm publish`.) #### Gem @@ -86,7 +87,7 @@ Probably only need to do this when releasing a minor version, and not a patch ve 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. Wait for Circle CI to go green again. 1. `rake release` ### Finally diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 69e792f8..fa0ae527 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -1710,6 +1710,13 @@ getJasmineRequireObj().Env = function(j$) { defaultResourcesForRunnable(topSuite.id); currentDeclarationSuite = topSuite; + /** + * Provides the root suite, through which all suites and specs can be + * accessed. + * @function + * @name Env#topSuite + * @return {Suite} the root suite + */ this.topSuite = function() { return topSuite; }; @@ -3103,6 +3110,18 @@ getJasmineRequireObj().CallTracker = function(j$) { return call ? call.args : []; }; + /** + * Get the "this" object that was passed to a specific invocation of this spy. + * @name Spy#calls#thisFor + * @function + * @param {Integer} index The 0-based invocation index. + * @return {Object?} + */ + this.thisFor = function(index) { + var call = calls[index]; + return call ? call.object : undefined; + }; + /** * Get the raw calls array for this spy. * @name Spy#calls#all @@ -4243,7 +4262,22 @@ getJasmineRequireObj().GlobalErrors = function(j$) { this.jasmineHandlers = {}; this.installOne_ = function installOne_(errorType, jasmineMessage) { function taggedOnError(error) { - error.jasmineMessage = jasmineMessage + ': ' + error; + var substituteMsg; + + if (error) { + error.jasmineMessage = jasmineMessage + ': ' + error; + } else { + substituteMsg = jasmineMessage + ' with no error or message'; + + if (errorType === 'unhandledRejection') { + substituteMsg += + '\n' + + '(Tip: to get a useful stack trace, use ' + + 'Promise.reject(new Error(...)) instead of Promise.reject().)'; + } + + error = new Error(substituteMsg); + } var handler = handlers[handlers.length - 1]; @@ -9173,10 +9207,26 @@ getJasmineRequireObj().StackTrace = function(j$) { }; getJasmineRequireObj().Suite = function(j$) { + /** + * @interface Suite + * @see Env#topSuite + */ function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; + /** + * The parent of this suite, or null if this is the top suite. + * @name Suite#parentSuite + * @readonly + * @type {Suite} + */ this.parentSuite = attrs.parentSuite; + /** + * The description passed to the {@link describe} that created this suite. + * @name Suite#description + * @readonly + * @type {string} + */ this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; @@ -9190,6 +9240,11 @@ getJasmineRequireObj().Suite = function(j$) { this.timer = attrs.timer || new j$.Timer(); + /** + * The suite's children. + * @name Suite#children + * @type {Array.<(Spec|Suite)>} + */ this.children = []; /** @@ -9227,6 +9282,12 @@ getJasmineRequireObj().Suite = function(j$) { return this.asyncExpectationFactory(actual, this); }; + /** + * The full description including all ancestors of this suite. + * @name Suite#getFullName + * @function + * @returns {string} + */ Suite.prototype.getFullName = function() { var fullName = []; for ( diff --git a/package.json b/package.json index e65e990f..b90ced73 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "grunt-css-url-embed": "^1.11.1", "grunt-sass": "^3.0.2", "jasmine": "^3.4.0", - "jasmine-browser-runner": "^0.4.0", + "jasmine-browser-runner": "github:jasmine/jasmine-browser#main", "jsdom": "^15.0.0", "load-grunt-tasks": "^4.0.0", "node-sass": "^4.11.0", diff --git a/scripts/run-all-browsers b/scripts/run-all-browsers new file mode 100755 index 00000000..331109cc --- /dev/null +++ b/scripts/run-all-browsers @@ -0,0 +1,45 @@ +#!/bin/sh + +run_browser() { + browser=$1 + version=$2 + description="$browser $version" + if [ $version = "latest" ]; then + version="" + fi + + echo + echo + echo "Running $description" + echo + USE_SAUCE=true JASMINE_BROWSER=$browser SAUCE_BROWSER_VERSION=$version npm run ci + + if [ $? -eq 0 ]; then + echo "PASS: $description" >> "$passfile" + else + echo "FAIL: $description" >> "$failfile" + fi +} + +passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1 +failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1 +run_browser "internet explorer" 11 +run_browser "internet explorer" 10 +run_browser firefox latest +run_browser firefox 78 +run_browser firefox 68 +run_browser safari 14 +run_browser safari 13 +run_browser safari 12 +run_browser safari 11 +run_browser safari 10 +run_browser safari 9 +run_browser safari 8 +run_browser MicrosoftEdge latest + +echo +cat "$passfile" "$failfile" + +if [ -s "$failfile" ]; then + exit 1 +fi diff --git a/scripts/start-sauce-connect b/scripts/start-sauce-connect new file mode 100755 index 00000000..cfe551b2 --- /dev/null +++ b/scripts/start-sauce-connect @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -o errexit +set -o pipefail + +if [ $# -gt 1 -o "$1" = "--help" ]; then + echo "Usage: $0 [pidfile]" 1>&2 + exit +fi + +if [ -z "$1" ]; then + pidfile=`mktemp` +else + pidfile="$1" +fi + +outfile=`mktemp` +echo "Starting Sauce Connect" +if [ -z "$SAUCE_TUNNEL_IDENTIFIER" ]; then + sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" -X 4445 --pidfile "$pidfile" 2>&1 | tee "$outfile" & +else + sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" -X 4445 --pidfile "$pidfile" -i "$SAUCE_TUNNEL_IDENTIFIER" 2>&1 | tee "$outfile" & +fi + +while ! fgrep "Sauce Connect is up, you may start your tests." "$outfile" > /dev/null; do + sleep 1 + + if ! ps -p $(cat "$pidfile") > /dev/null; then + echo "Sauce Connect exited" + exit 1 + fi +done + +if ! nc -z localhost 4445; then + echo "Can't connect to Sauce tunnel" + killall sc + exit 1 +fi diff --git a/scripts/stop-sauce-connect b/scripts/stop-sauce-connect new file mode 100755 index 00000000..1b82081a --- /dev/null +++ b/scripts/stop-sauce-connect @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -o errexit +set -o pipefail + +if [ -z "$1" ]; then + echo "Usage: $0 sauce-connect-pid" 1>&2 + exit +fi + +pid="$1" +echo "PID: $pid" + +echo "Stopping Sauce Connect" +# Sauce Connect docs say that we can just kill -9 it if we don't care about +# failing any ongoing sessions. In practice, that sometimes works but usually +# leaks a tunnel so badly that you can't even stop it from the web UI. +# Instead of doing that, we give Sauce Connect some time to shut down +# gracefully and then give up. +kill -INT $pid + +# Wait up to 2 minutes, then give up if it's still running +n=0 +while [ $n -lt 120 ] && ps -p $pid > /dev/null; do + sleep 1 + kill -INT $pid 2> /dev/null || true + n=$(($n + 1)) +done + +if ps -p $pid > /dev/null; then + echo "Could not shut down Sauce Connect" +fi + +exit $exitcode diff --git a/spec/core/CallTrackerSpec.js b/spec/core/CallTrackerSpec.js index 474f674f..f997de44 100644 --- a/spec/core/CallTrackerSpec.js +++ b/spec/core/CallTrackerSpec.js @@ -30,6 +30,20 @@ describe('CallTracker', function() { expect(callTracker.argsFor(1)).toEqual([0, 'foo']); }); + it("tracks the 'this' object from each execution", function() { + var callTracker = new jasmineUnderTest.CallTracker(); + + var this0 = {}, + this1 = {}; + callTracker.track({ object: this0, args: [] }); + callTracker.track({ object: this1, args: [] }); + callTracker.track({ args: [] }); + + expect(callTracker.thisFor(0)).toBe(this0); + expect(callTracker.thisFor(1)).toBe(this1); + expect(callTracker.thisFor(2)).toBe(undefined); + }); + it('returns any empty array when there was no call', function() { var callTracker = new jasmineUnderTest.CallTracker(); diff --git a/spec/core/GlobalErrorsSpec.js b/spec/core/GlobalErrorsSpec.js index 4fb41c21..8e3ffbc5 100644 --- a/spec/core/GlobalErrorsSpec.js +++ b/spec/core/GlobalErrorsSpec.js @@ -122,7 +122,7 @@ describe('GlobalErrors', function() { errors.uninstall(); }); - it('reports uncaughtException in node.js', function() { + it('reports uncaught exceptions in node.js', function() { var fakeGlobal = { process: { on: jasmine.createSpy('process.on'), @@ -170,7 +170,7 @@ describe('GlobalErrors', function() { ); }); - it('reports unhandledRejection in node.js', function() { + it('reports unhandled promise rejections in node.js', function() { var fakeGlobal = { process: { on: jasmine.createSpy('process.on'), @@ -218,6 +218,38 @@ describe('GlobalErrors', function() { ); }); + it('reports unhandled promise rejections in node.js when no error is provided', function() { + var fakeGlobal = { + process: { + on: jasmine.createSpy('process.on'), + removeListener: function() {}, + listeners: function() { + return []; + }, + removeAllListeners: function() {} + } + }, + handler = jasmine.createSpy('errorHandler'), + errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); + + errors.install(); + errors.pushListener(handler); + + expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual( + 'unhandledRejection' + ); + var addedListener = fakeGlobal.process.on.calls.argsFor(1)[1]; + addedListener(undefined); + + expect(handler).toHaveBeenCalledWith( + new Error( + 'Unhandled promise rejection with no error or message\n' + + '(Tip: to get a useful stack trace, use ' + + 'Promise.reject(new Error(...)) instead of Promise.reject().)' + ) + ); + }); + describe('Reporting unhandled promise rejections in the browser', function() { it('subscribes and unsubscribes from the unhandledrejection event', function() { var fakeGlobal = jasmine.createSpyObj('globalErrors', [ diff --git a/spec/core/SpyStrategySpec.js b/spec/core/SpyStrategySpec.js index 85624083..b4beabc1 100644 --- a/spec/core/SpyStrategySpec.js +++ b/spec/core/SpyStrategySpec.js @@ -90,7 +90,7 @@ describe('SpyStrategy', function() { expect(function() { spyStrategy.exec(); - }).toThrow({ code: 'ESRCH' }); + }).toThrow(jasmine.objectContaining({ code: 'ESRCH' })); expect(originalFn).not.toHaveBeenCalled(); }); diff --git a/spec/core/asymmetricEqualityTesterArgCompatShimSpec.js b/spec/core/asymmetricEqualityTesterArgCompatShimSpec.js index b9f5ad25..26bb90fd 100644 --- a/spec/core/asymmetricEqualityTesterArgCompatShimSpec.js +++ b/spec/core/asymmetricEqualityTesterArgCompatShimSpec.js @@ -93,7 +93,6 @@ describe('asymmetricEqualityTesterArgCompatShim', function() { 'flatMap', 'includes', 'keys', - 'toSource', 'values' ], shim = jasmineUnderTest.asymmetricEqualityTesterArgCompatShim({}, []), diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index e09acd16..facbb62c 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -683,8 +683,8 @@ describe('Env integration', function() { }); env.execute(null, function() { - // Expect >= 9 rather than >= 10 to compensate for clock imprecision - expect(duration).toBeGreaterThanOrEqual(9); + // Expect > 0 to compensate for clock imprecision + expect(duration).toBeGreaterThan(0); done(); }); }); @@ -2886,6 +2886,8 @@ describe('Env integration', function() { resolve = res; }); + env.configure({ random: false }); + env.describe('a suite', function() { env.it('does not wait', function() { // Note: we intentionally don't return the result of each expectAsync. @@ -2895,6 +2897,12 @@ describe('Env integration', function() { }); }); + env.it('another spec', function(done) { + // This is here to make sure that the async expectation evaluates + // before the Jasmine under test finishes, especially on Safari 8 and 9. + setTimeout(done, 10); + }); + env.addReporter({ specDone: function() { resolve(); @@ -2941,6 +2949,8 @@ describe('Env integration', function() { resolve = res; }); + env.configure({ random: false }); + env.describe('a suite', function() { env.afterAll(function() { // Note: we intentionally don't return the result of expectAsync. @@ -2951,6 +2961,12 @@ describe('Env integration', function() { env.it('is a spec', function() {}); }); + env.it('another spec', function(done) { + // This is here to make sure that the async expectation evaluates + // before the Jasmine under test finishes, especially on Safari 8 and 9. + setTimeout(done, 10); + }); + env.addReporter({ suiteDone: function() { resolve(); diff --git a/spec/npmPackage/npmPackageSpec.js b/spec/npmPackage/npmPackageSpec.js index ea292bc2..70af391b 100644 --- a/spec/npmPackage/npmPackageSpec.js +++ b/spec/npmPackage/npmPackageSpec.js @@ -99,4 +99,13 @@ describe('npm package', function() { expect(images).toContain('jasmine-horizontal.svg'); expect(images).toContain('jasmine_favicon.png'); }); + + it('does not have CI config files and scripts', function() { + expect(fs.existsSync(path.resolve(this.tmpDir, 'package/.circleci'))).toBe( + false + ); + expect(fs.existsSync(path.resolve(this.tmpDir, 'package/scripts'))).toBe( + false + ); + }); }); diff --git a/spec/support/jasmine-browser.js b/spec/support/jasmine-browser.js index e1bd519d..486d4df6 100644 --- a/spec/support/jasmine-browser.js +++ b/spec/support/jasmine-browser.js @@ -43,9 +43,7 @@ module.exports = { browserVersion: process.env.SAUCE_BROWSER_VERSION, build: `Core ${process.env.TRAVIS_BUILD_NUMBER || 'Ran locally'}`, tags: ['Jasmine-Core'], - tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER - ? process.env.TRAVIS_JOB_NUMBER.toString() - : null, + tunnelIdentifier: process.env.SAUCE_TUNNEL_IDENTIFIER, username: process.env.SAUCE_USERNAME, accessKey: process.env.SAUCE_ACCESS_KEY } diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index 91a178c7..68878471 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -17,6 +17,7 @@ "helpers/integrationMatchers.js", "helpers/promises.js", "helpers/requireFastCheck.js", + "helpers/overrideConsoleLogForCircleCi.js", "helpers/nodeDefineJasmineUnderTest.js", "helpers/resetEnv.js" ], diff --git a/src/core/CallTracker.js b/src/core/CallTracker.js index 8613a250..128790fe 100644 --- a/src/core/CallTracker.js +++ b/src/core/CallTracker.js @@ -49,6 +49,18 @@ getJasmineRequireObj().CallTracker = function(j$) { return call ? call.args : []; }; + /** + * Get the "this" object that was passed to a specific invocation of this spy. + * @name Spy#calls#thisFor + * @function + * @param {Integer} index The 0-based invocation index. + * @return {Object?} + */ + this.thisFor = function(index) { + var call = calls[index]; + return call ? call.object : undefined; + }; + /** * Get the raw calls array for this spy. * @name Spy#calls#all diff --git a/src/core/Env.js b/src/core/Env.js index b76cde84..1d950402 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -708,6 +708,13 @@ getJasmineRequireObj().Env = function(j$) { defaultResourcesForRunnable(topSuite.id); currentDeclarationSuite = topSuite; + /** + * Provides the root suite, through which all suites and specs can be + * accessed. + * @function + * @name Env#topSuite + * @return {Suite} the root suite + */ this.topSuite = function() { return topSuite; }; diff --git a/src/core/GlobalErrors.js b/src/core/GlobalErrors.js index f0253b2d..b0c6e20e 100644 --- a/src/core/GlobalErrors.js +++ b/src/core/GlobalErrors.js @@ -17,7 +17,22 @@ getJasmineRequireObj().GlobalErrors = function(j$) { this.jasmineHandlers = {}; this.installOne_ = function installOne_(errorType, jasmineMessage) { function taggedOnError(error) { - error.jasmineMessage = jasmineMessage + ': ' + error; + var substituteMsg; + + if (error) { + error.jasmineMessage = jasmineMessage + ': ' + error; + } else { + substituteMsg = jasmineMessage + ' with no error or message'; + + if (errorType === 'unhandledRejection') { + substituteMsg += + '\n' + + '(Tip: to get a useful stack trace, use ' + + 'Promise.reject(new Error(...)) instead of Promise.reject().)'; + } + + error = new Error(substituteMsg); + } var handler = handlers[handlers.length - 1]; diff --git a/src/core/Suite.js b/src/core/Suite.js index e660d1f1..5043017b 100644 --- a/src/core/Suite.js +++ b/src/core/Suite.js @@ -1,8 +1,24 @@ getJasmineRequireObj().Suite = function(j$) { + /** + * @interface Suite + * @see Env#topSuite + */ function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; + /** + * The parent of this suite, or null if this is the top suite. + * @name Suite#parentSuite + * @readonly + * @type {Suite} + */ this.parentSuite = attrs.parentSuite; + /** + * The description passed to the {@link describe} that created this suite. + * @name Suite#description + * @readonly + * @type {string} + */ this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; @@ -16,6 +32,11 @@ getJasmineRequireObj().Suite = function(j$) { this.timer = attrs.timer || new j$.Timer(); + /** + * The suite's children. + * @name Suite#children + * @type {Array.<(Spec|Suite)>} + */ this.children = []; /** @@ -53,6 +74,12 @@ getJasmineRequireObj().Suite = function(j$) { return this.asyncExpectationFactory(actual, this); }; + /** + * The full description including all ancestors of this suite. + * @name Suite#getFullName + * @function + * @returns {string} + */ Suite.prototype.getFullName = function() { var fullName = []; for ( diff --git a/travis-core-script.sh b/travis-core-script.sh deleted file mode 100644 index 1e5733b1..00000000 --- a/travis-core-script.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -e - -if [ $USE_SAUCE == true ] -then - if [ $TRAVIS_SECURE_ENV_VARS != true ] - then - echo "skipping tests since we can't use sauce" - exit 0 - fi -fi - -npm run ci