diff --git a/LICENSE b/LICENSE index 73a62df0..3bbb5119 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ Copyright (c) 2008-2019 Pivotal Labs -Copyright (c) 2008-2025 The Jasmine developers +Copyright (c) 2008-2026 The Jasmine developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 6d6d69a7..7963fc75 100644 --- a/README.md +++ b/README.md @@ -60,5 +60,5 @@ To find out what environments work with a particular Jasmine release, see the [r * Sheel Choksi Copyright (c) 2008-2019 Pivotal Labs
-Copyright (c) 2008-2025 The Jasmine developers
+Copyright (c) 2008-2026 The Jasmine developers
This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/LICENSE). diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index a8ef9188..14f07851 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -44,6 +44,9 @@ const getJasmineRequireObj = (function() { return jasmineRequire; } + const loadedAsBrowserEsm = + globalThis.document && !globalThis.document.currentScript; + getJasmineRequire().core = function(jRequire) { const private$ = {}; const j$ = {}; @@ -136,8 +139,7 @@ const getJasmineRequireObj = (function() { private$ ); - private$.loadedAsBrowserEsm = - globalThis.document && !globalThis.document.currentScript; + private$.loadedAsBrowserEsm = loadedAsBrowserEsm; return { jasmine: j$, private: private$ }; }; @@ -218,7 +220,7 @@ getJasmineRequireObj().base = function(j$, private$, jasmineGlobal) { * Maximum number of characters to display when pretty printing objects. * Characters past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_CHARS - * @default 100 + * @default 1000 * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; @@ -3836,7 +3838,8 @@ getJasmineRequireObj().ExceptionFormatter = function(j$, private$) { 'lineNumber', 'column', 'description', - 'jasmineMessage' + 'jasmineMessage', + 'errors' ]; function ExceptionFormatter(options) { @@ -3902,6 +3905,19 @@ getJasmineRequireObj().ExceptionFormatter = function(j$, private$) { lines = lines.concat(substack); } + if (Array.isArray(error.errors)) { + for (let i = 0; i < error.errors.length; i++) { + if (error.errors[i] instanceof Error) { + lines.push(''); + const substack = this.stack_(error.errors[i], { + messageHandling: 'require' + }); + substack[0] = 'Error ' + (i + 1) + ': ' + substack[0]; + lines = lines.concat(substack.map(x => ' ' + x)); + } + } + } + return lines; }; @@ -8792,7 +8808,6 @@ getJasmineRequireObj().reporterEvents = function(j$, private$) { * {@link ReporterCapabilities} will apply. * @name Reporter#reporterCapabilities * @type ReporterCapabilities | undefined - * @optional * @since 5.0 */ /** @@ -8859,7 +8874,6 @@ getJasmineRequireObj().reporterEvents = function(j$, private$) { /** * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) * @function - * @optional * @name Reporter#specStarted * @param {SpecStartedEvent} result Information about the individual {@link it} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. @@ -8906,6 +8920,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ @@ -8921,6 +8936,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ @@ -8937,6 +8953,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ @@ -8955,6 +8972,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {String} description Textual description of what this spec is checking * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. @@ -8972,6 +8990,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. */ @@ -8987,6 +9006,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} testFunction Function that contains the code of your test. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. @@ -9002,6 +9022,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. * @see async @@ -9016,6 +9037,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. * @see async @@ -9032,6 +9054,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. * @see async @@ -9048,6 +9071,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. * @see async @@ -9061,6 +9085,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name getSpecProperty * @since 5.10.0 * @function + * @global + * @overwritable * @param {String} key The name of the property * @returns {*} The value of the property */ @@ -9073,6 +9099,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name setSpecProperty * @since 3.6.0 * @function + * @global + * @overwritable * @param {String} key The name of the property * @param {*} value The value of the property */ @@ -9085,6 +9113,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name setSuiteProperty * @since 3.6.0 * @function + * @global + * @overwritable * @param {String} key The name of the property * @param {*} value The value of the property */ @@ -9098,6 +9128,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ @@ -9114,6 +9145,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 3.3.0 * @function * @global + * @overwritable * @param {Object} actual - Actual computed value to test expectations against. * @return {async-matchers} * @example @@ -9140,8 +9172,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name throwUnlessAsync * @since 5.1.0 * @function - * @param actual * @global + * @overwritable * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ @@ -9164,8 +9196,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name throwUnless * @since 5.1.0 * @function - * @param actual * @global + * @overwritable * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ @@ -9179,6 +9211,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.0.0 * @function * @global + * @overwritable * @param {String} [message] - Reason the spec is pending. */ pending: function() { @@ -9191,6 +9224,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {String|Error} [error] - Reason for the failure. */ fail: function() { @@ -9203,6 +9237,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {Object} obj - The object upon which to install the {@link Spy}. * @param {String} methodName - The name of the method to replace with a {@link Spy}. * @returns {Spy} @@ -9217,6 +9252,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.6.0 * @function * @global + * @overwritable * @param {Object} obj - The object upon which to install the {@link Spy} * @param {String} propertyName - The name of the property to replace with a {@link Spy}. * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. @@ -9232,6 +9268,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 3.2.1 * @function * @global + * @overwritable * @param {Object} obj - The object upon which to install the {@link Spy}s * @param {boolean} includeNonEnumerable - Whether or not to add spies to non-enumerable properties * @returns {Object} the spied object @@ -12206,7 +12243,7 @@ getJasmineRequireObj().UserContext = function(j$, private$) { }; getJasmineRequireObj().version = function() { - return '6.0.0-alpha.2'; + return '6.0.1'; }; (function() { diff --git a/package.json b/package.json index 69a35110..9955b384 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jasmine-core", "license": "MIT", - "version": "6.0.0-alpha.2", + "version": "6.0.1", "repository": { "type": "git", "url": "https://github.com/jasmine/jasmine.git" @@ -46,10 +46,10 @@ "ejs": "^3.1.10", "eslint": "^9.24.0", "eslint-plugin-compat": "^6.0.2", - "glob": "^10.2.3", + "glob": "^13.0.0", "globals": "^16.0.0", "jasmine": "file:../jasmine-npm", - "jasmine-browser-runner": "github:jasmine/jasmine-browser-runner#4.0", + "jasmine-browser-runner": "github:jasmine/jasmine-browser-runner", "jsdom": "^26.0.0", "prettier": "1.17.1", "sass": "^1.58.3" diff --git a/release_notes/5.13.0.md b/release_notes/5.13.0.md new file mode 100644 index 00000000..64874b7f --- /dev/null +++ b/release_notes/5.13.0.md @@ -0,0 +1,44 @@ +# Jasmine Core 5.13.0 Release Notes + +## Changes to supported environments + +Safari 26 is now supported on a best-effort basis. + +Due to the limited availability of Safari 18 and later on free CI services, +Safari support in future jasmine-core versions will be limited to: + +* Best-effort support for the latest Safari version available on GitHub Actions, + which may change at any time. +* Best-effort support for Safari 16 and 17 for as long as it remains practical. + +## New Features + +* New `extraItStackFrames` and `extraDescribeStackFrames` config options to fix + the filename properties of reporter events in configurations that wrap + `it`/`describe`, such as zone.js. The `filename` properties of reporter events + are no longer deprecated. +* `jasmine.allOf` asymmetric equality tester + * Merges [#2087](https://github.com/jasmine/jasmine/issues/2083) from @jonahd-g + * Fixes [#2083](https://github.com/jasmine/jasmine/pull/2087) + +## Supported environments + +This version has been tested in the following environments. + +| Environment | Supported versions | +|-------------|--------------------------------| +| Node | 18.20.5**, 20, 22, 24 | +| Safari** | 16, 17, 26.1 | +| Chrome | 142* | +| Firefox | 102**, 115**, 128**, 140, 145* | +| Edge | 142* | + +\* Evergreen browser. Each version of Jasmine is tested against the latest +version available at release time.
+\** Supported on a best-effort basis. Support for these versions may be dropped +if it becomes impractical, and bugs affecting only these versions may not be +treated as release blockers. + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ diff --git a/release_notes/6.0.0-beta.0.md b/release_notes/6.0.0-beta.0.md new file mode 100644 index 00000000..3ea586ee --- /dev/null +++ b/release_notes/6.0.0-beta.0.md @@ -0,0 +1,74 @@ +# Jasmine Core 6.0.0-beta.0 Release Notes + +This is a pre-release, intended to offer a preview of upcoming changes and to +solicit feedback. + +## A Note About Pre-Release Compatibility + +There may be additional breaking changes in future 6.0 pre-releases or in the +final 6.0 release. That's allowed by the semver specification, but users are +sometimes unpleasantly surprised by it. + +NPM's implementation of carat version ranges assumes that subsequent +pre-releases and final releases are fully compatible with earlier pre-releases. +If your package.json contains `"jasmine-core": "^6.0.0-beta.0`, +NPM might install any later 6.x version even though there is no guarantee of +compatibility. If that isn't ok, you should specify an exact pre-release version: +`"jasmine-core": "6.0.0-beta.0`. + + +## Breaking changes + +* boot1.js no longer adds jsApiReporter to the env. +* HtmlReporterV2 initialization and boot1.js have been simplified. If you + maintain your own boot file, update it to match the boot1.js included in this + package. + + +## New features + +* Statically exposed pretty printer as jasmine.pp(). + +## Bug fixes + +* Fixed HtmlReporterV2 progress bar when running a subset of specs. + + +## Deprecations + +* jsApiReporter is deprecated. +* Detect monkey patching and emit a deprecation warning. + +## Documentation improvements + +* Documented the set of possible spec statuses. + + +## Internal improvements + +* Replaced isArray helper with native Array.isArray + + +## Supported environments + +This version has been tested in the following environments. + +| Environment | Supported versions | +|-------------------|--------------------------------| +| Node | 20, 22, 24 | +| Safari | 16**, 17**, 26.1** | +| Chrome | 142* | +| Firefox | 102**, 115**, 128**, 140, 145* | +| Edge | 142* | + +\* Evergreen browser. Each version of Jasmine is tested against the latest +version available at release time.
+\** Supported on a best-effort basis. Support for these versions may be dropped +if it becomes impractical, and bugs affecting only these versions may not be +treated as release blockers. + + + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ diff --git a/release_notes/6.0.0-beta.1.md b/release_notes/6.0.0-beta.1.md new file mode 100644 index 00000000..85a3977d --- /dev/null +++ b/release_notes/6.0.0-beta.1.md @@ -0,0 +1,47 @@ +# Jasmine Core 6.0.0-beta.1 Release Notes + +This is a pre-release, intended to offer a preview of upcoming changes and to +solicit feedback. + +A corresponding release of the `jasmine` package is not planned because the +change in this release only affects browser users. + +## A Note About Pre-Release Compatibility + +There may be additional breaking changes in future 6.0 pre-releases or in the +final 6.0 release. That's allowed by the semver specification, but users are +sometimes unpleasantly surprised by it. + +NPM's implementation of carat version ranges assumes that subsequent +pre-releases and final releases are fully compatible with earlier pre-releases. +If your package.json contains `"jasmine-core": "^6.0.0-beta.1`, +NPM might install any later 6.x version even though there is no guarantee of +compatibility. If that isn't ok, you should specify an exact pre-release version: +`"jasmine-core": "6.0.0-beta.1`. + +## Bug Fixes + +* Revert to using window.onload in boot1.js. This fixes top level await in + jasmine-browser-runner. + +## Supported environments + +This version has been tested in the following environments. + +| Environment | Supported versions | +|-------------------|--------------------------------| +| Node | 20, 22, 24 | +| Safari** | 16, 17, 26.1 | +| Chrome | 143* | +| Firefox | 102**, 115**, 128**, 140, 145* | +| Edge | 142* | + +\* Evergreen browser. Each version of Jasmine is tested against the latest +version available at release time.
+\** Supported on a best-effort basis. Support for these versions may be dropped +if it becomes impractical, and bugs affecting only these versions may not be +treated as release blockers. + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ diff --git a/release_notes/6.0.0.md b/release_notes/6.0.0.md new file mode 100644 index 00000000..7051f2a3 --- /dev/null +++ b/release_notes/6.0.0.md @@ -0,0 +1,190 @@ +# Jasmine Core 6.0.0 Release Notes + +## Summary + +This is a major release that includes breaking changes as well as significant +new features. Many of the breaking changes and deprecations in this release are +intended to improve the stability of the Jasmine ecosystem by making the +distinction between public and private APIs more obvious, reducing exposure of +jasmine-core's internal state, removing ambiguities from the reporter API, and +warning about monkey patching. + +6.x is intended to ba a relatively short-lived, transitional series. It is +compatible with the current versions of karma-jasmine and other legacy Angular +tools but emits deprecation warnings when used with them. 7.0 will drop +compatibility with those tools. If you use Karma in a non-Angular context, +consider migrating to a maintained alternative such as jasmine-browser-runner or +web-test-runner. If you use Angular, please direct any questions about support +for future versions of Jasmine to the Angular team. + +Please see the [migration guide](https://jasmine.github.io/upgrade-guides/6.0) +for more information. If you use Jasmine via the `jasmine` package, please see +[its release notes](https://github.com/jasmine/jasmine-npm/blob/main/release_notes/6.0.0.md) +as well. + + +## Changes to supported environments + +* Node 18 is no longer supported. + + +## Breaking changes + +### General + +* Private APIs have been removed from the `jasmine` namespace. + + The purpose of this change is to reduce the risk of users inadvertently + depending on private APIs. Anything not covered by + [the documentation](https://jasmine.github.io/pages/docs_home.html) remains + private regardless of namespacing. Private APIs may be changed or removed in + any release. This change is being made in a major release as a courtesy to + users of libraries that depend on private APIs. + +### Changes that affect spec writing + +* Global error spies always receive a single argument. Previously, the browser + error event was passed as the second argument. +* If a spy is invoked via `.call(null, ...)` or `.apply(null, ...)`, the spy's + `this` argument will be `null`. Previously it was `globalThis`. +* Mock clock timing functions cannot be spied on. Previously this "worked" but + prevented the mock clock from uninstalling itself. +* The mock clock no longer supports the eval forms of `setTimeout` and + `setInterval`. +* Keys passed to `setSpecProperty`/`setSuiteProperty` must be strings. Values + must be both structured-cloneable and JSON-serializable. + +### Changes that affect configuration + +* HTML reporters cache configuration throughout each run. Configuration changes + made while specs are running will not affect reporter behavior. +* If an execution order is passed to `Env#execute`, it must not enter any suite + more than once. +* The argument passed to spec filters is a + [spec metadata](https://jasmine.github.io/api/6.0/Spec.html) + instance, not the internal spec object. +* The default value of the `forbidDuplicateNames` config option has been + changed to true. + +### Changes that affect reporters + +This release includes changes that are intended to streamline and clarify the +reporter interface, prevent sharing of mutable state, and prevent bugs involving +non-serializable objects. These changes should be compatible with most existing +reporters but could break reporters that manage their internal state in unusual +ways. + +* Meaningless properties such as `status` and `failedExpectations` are omitted + from the events passed to [suiteStarted](https://jasmine.github.io/api/6.0/global.html#SuiteStartedEvent) + and [specStarted](https://jasmine.github.io/api/6.0/global.html#SpecStartedEvent). +* Reporter events are deep-cloned before being passed to each reporter. This + protects reporters against later mutation by jasmine-core or other reporters. +* The `expected` and `actual` properties of + [passed and failed expectations](https://jasmine.github.io/api/6.0/global.html#ExpectationResult) + have been removed. +* The [order](https://jasmine.github.io/api/6.0/global.html#Order) + property of the`jasmineStarted` and `jasmineDone` reporter events no longer + includes undocumented properties. +* boot1.js no longer adds `jsApiReporter` to the env. + +### Changes that affect browser boot files + +* The `createElement` and `createTextNode` options of the legacy `HtmlReporter` + are ignored. `HtmlReporter` now unconditionally uses `document.createElement` + and `document.createTextNode`. + +### Changes to Node boot functions + +* [boot](https://jasmine.github.io/api/6.0/module-jasmine-core.html#.boot) + defaults to creating a new core instance each time it's called. This restores + the pre-5.0 default behavior. +* [noGlobals](https://jasmine.github.io/api/6.0/module-jasmine-core.html#.noGlobals) + no longer takes a parameter. It always returns the same object when called + repeatedly. + + +## New features + +* A new `HtmlReporterV2` with several improvements over the old `HtmlReporter`: + * Clicking a spec/suite link does exact filtering rather than a substring + match. + * The old dots are replaced with a progress bar. This improves usability with + large suites and fixes an accessibility problem. + * Details of failed specs are displayed as soon as each spec finishes. + * A Performance tab shows metrics and a list of the slowest specs. + * Initialization and wire-up in boot files are much simpler. + + If you're using jasmine-browser-runner or copying boot1.js from the standalone + distribution, you'll automatically get the new reporter. If you maintain your + own boot files, you'll get the old reporter unless you update your boot files + to match the one that's in this package. + + The new reporter produces `spec` query string parameters that are different + from those created by the old reporter. If you use other software that + interprets the `spec` parameter, such as karma-jasmine, you won't be able to + adopt `HtmlReporterV2` unlesss that other software is updated. + +* Larger body font size in HTML reporters +* `globalThis` is used to determine the global object during initialization + This makes jasmine-core more tolerant of buggy bundlers or loaders that + cause `this` to be undefined in the global context. +* Experimental [`safariYieldStrategy: "time"`](https://jasmine.github.io/api/6.0/Configuration.html#safariYieldStrategy) + config option, which may make Jasmine run significantly faster in Safari and + similar browsers. So far, this option has not been tested on a wide variety of + workloads. Feedback is appreciated. +* Statically exposed pretty printer as `jasmine.pp()`. + + +## Deprecations + +* Common monkey patching patterns are detected and result in a deprecation + warning. Code that overwrites anything provided by jasmine-core (other than + globals like `it`/`expect`/etc or configuration properties like + `jasmine.DEFAULT_TIMEOUT_INTERVAL`) may be broken by any jasmine-core release. +* Warn if jasmine-core is loaded as an ES module in a browser. + This is an untested and unsupported configuration that has been known to cause + problems. +* `HtmlReporter` and `HtmlSpecFilter` are deprecated in favor of `HtmlReporterV2`. +* `jsApiReporter` is deprecated. + + +## Documentation improvements + +* Improved API reference documentation for APIs that are used from browser boot + files. +* Documented the set of possible spec statuses. +* Documented that globals are overwriteable. + + +## Internal improvements + +* Encapsulated suite and spec result and status management. +* Adopted strict mode throughout the codebase. +* Decomposed `HtmlReporter` into components and converted to ES6 classes. +* Made global error handling more uniform between browsers and Node. +* Removed code to support browsers that don't have `MessageChannel`. Jasmine + hasn't run in any such browsers since 2.x. +* Replaced `isArray` helper with native `Array.isArray`. + +## Supported environments + +This version has been tested in the following environments. + +| Environment | Supported versions | +|-------------------|--------------------------------| +| Node | 20, 22, 24 | +| Safari** | 16, 17, 26.2 | +| Chrome | 143* | +| Firefox | 102**, 115**, 128**, 140, 147* | +| Edge | 143* | + +\* Evergreen browser. Each version of Jasmine is tested against the latest +version available at release time.
+\** Supported on a best-effort basis. Support for these versions may be dropped +if it becomes impractical, and bugs affecting only these versions may not be +treated as release blockers. + + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ diff --git a/release_notes/6.0.1.md b/release_notes/6.0.1.md new file mode 100644 index 00000000..f5ec6a0a --- /dev/null +++ b/release_notes/6.0.1.md @@ -0,0 +1,29 @@ +# Jasmine Core 6.0.1 Release Notes + +## Bug fixes + +* Don't emit a deprecation warning when `jasmineRequire.core` is called from an + ES module + +## Supported environments + +This version has been tested in the following environments. + +| Environment | Supported versions | +|-------------------|--------------------------------| +| Node | 20, 22, 24 | +| Safari** | 16, 17, 26.2 | +| Chrome | 143* | +| Firefox | 102**, 115**, 128**, 140, 147* | +| Edge | 143* | + +\* Evergreen browser. Each version of Jasmine is tested against the latest +version available at release time.
+\** Supported on a best-effort basis. Support for these versions may be dropped +if it becomes impractical, and bugs affecting only these versions may not be +treated as release blockers. + + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ diff --git a/spec/core/ExceptionFormatterSpec.js b/spec/core/ExceptionFormatterSpec.js index 636589d7..f5eba069 100644 --- a/spec/core/ExceptionFormatterSpec.js +++ b/spec/core/ExceptionFormatterSpec.js @@ -352,5 +352,184 @@ describe('ExceptionFormatter', function() { }).not.toThrowError(); }); }); + + describe('when the error has an errors array (AggregateError)', function() { + it('includes all aggregated errors in the stack trace', function() { + const subject = new privateUnderTest.ExceptionFormatter(); + const error1 = (function fn1() { + return new Error('first error'); + })(); + const error2 = (function fn2() { + return new Error('second error'); + })(); + const aggregateError = (function fn3() { + return new Error('Multiple errors occurred'); + })(); + aggregateError.errors = [error1, error2]; + + const lines = subject.stack(aggregateError).split('\n'); + + // TODO: be consistent across environments about whether the message is + // included in the stack trace + if (lines[0] === 'Error: Multiple errors occurred') { + lines.shift(); + } + + // Exclude lines that vary from environment to environment + const filteredLines = lines.filter( + x => + !x.includes('/jasmine.js:') && + // Some Node 20 and 22 minors when running in parallel + !x.includes('process.processTicksAndRejections') + ); + + for (let i = 0; i < filteredLines.length; i++) { + jasmine.debugLog(`Line ${i} after filtering: ${filteredLines[i]}`); + } + + // Inexact matching because stack frame formatting varies from runtime + // to runtime + const expectedPatterns = [ + // Overall error + /fn3.*ExceptionFormatterSpec\.js/, + /ExceptionFormatterSpec\.js/, + /^$/, + + // First nested error + /^ Error 1: Error: first error$/, + /^ .*fn1.*ExceptionFormatterSpec\.js/, + /^ .*ExceptionFormatterSpec\.js/, + /^$/, + + // Second nested error + /^ .*Error 2: Error: second error$/, + /^ .*fn2.*ExceptionFormatterSpec\.js/, + /^ .*ExceptionFormatterSpec\.js/ + ]; + + expect(filteredLines).toEqual( + expectedPatterns.map(p => jasmine.stringMatching(p)) + ); + }); + + it('handles empty errors array', function() { + const subject = new privateUnderTest.ExceptionFormatter(); + const aggregateError = new Error('No errors'); + aggregateError.errors = []; + + expect(function() { + subject.stack(aggregateError); + }).not.toThrowError(); + }); + + it('handles nested AggregateError', function() { + const subject = new privateUnderTest.ExceptionFormatter(); + const innerError1 = new Error('inner error 1'); + const innerError2 = new Error('inner error 2'); + const innerAggregate = new Error('Inner aggregate'); + innerAggregate.errors = [innerError1, innerError2]; + + const outerError = new Error('outer error'); + const outerAggregate = new Error('Outer aggregate'); + outerAggregate.errors = [innerAggregate, outerError]; + + const lines = subject.stack(outerAggregate).split('\n'); + + const innerAggMsgIx = lines.findIndex(line => + line.includes('Error 1: Error: Inner aggregate') + ); + expect(innerAggMsgIx).toBeGreaterThan(-1); + + const innerError1MsgIx = lines.findIndex(line => + line.includes('Error 1: Error: inner error 1') + ); + expect(innerError1MsgIx).toBeGreaterThan(innerAggMsgIx); + + const innerError2MsgIx = lines.findIndex(line => + line.includes('Error 2: Error: inner error 2') + ); + expect(innerError2MsgIx).toBeGreaterThan(innerError1MsgIx); + + const outerErrorMsgIx = lines.findIndex(line => + line.includes('Error 2: Error: outer error') + ); + expect(outerErrorMsgIx).toBeGreaterThan(innerError2MsgIx); + }); + + it('handles AggregateError containing error with cause', function() { + const subject = new privateUnderTest.ExceptionFormatter(); + const rootCause = new Error('root cause'); + const errorWithCause = new Error('error with cause', { + cause: rootCause + }); + const aggregateError = new Error('Aggregate with cause chain'); + aggregateError.errors = [errorWithCause]; + + const lines = subject.stack(aggregateError).split('\n'); + + const error1MsgIx = lines.findIndex(line => + line.includes('Error 1: Error: error with cause') + ); + expect(error1MsgIx).toBeGreaterThan(-1); + + const causeMsgIx = lines.findIndex(line => + line.includes('Caused by: Error: root cause') + ); + expect(causeMsgIx).toBeGreaterThan(error1MsgIx); + }); + + it('skips non-Error items in errors array', function() { + const subject = new privateUnderTest.ExceptionFormatter(); + const error1 = new Error('real error'); + const aggregateError = new Error('Mixed array'); + aggregateError.errors = [ + error1, + 'string error', + { message: 'object error' }, + null, + undefined, + 42 + ]; + + const lines = subject.stack(aggregateError).split('\n'); + + const error1MsgIx = lines.findIndex(line => + line.includes('Error 1: Error: real error') + ); + expect(error1MsgIx).toBeGreaterThan(-1); + + const hasStringError = lines.some(line => + line.includes('string error') + ); + expect(hasStringError).toBe(false); + + const hasObjectError = lines.some(line => + line.includes('object error') + ); + expect(hasObjectError).toBe(false); + }); + + it('works with native AggregateError constructor', function() { + const subject = new privateUnderTest.ExceptionFormatter(); + const error1 = new Error('first error'); + const error2 = new Error('second error'); + const aggregateError = new AggregateError( + [error1, error2], + 'Multiple errors' + ); + + const lines = subject.stack(aggregateError).split('\n'); + + const error1MsgIx = lines.findIndex(line => + line.includes('Error 1: Error: first error') + ); + expect(error1MsgIx).toBeGreaterThan(-1); + + const error2MsgIx = lines.findIndex(line => + line.includes('Error 2: Error: second error') + ); + expect(error2MsgIx).toBeGreaterThan(error1MsgIx); + }); + }); }); }); diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index 98656b37..8558a38a 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -768,7 +768,16 @@ describe('matchersUtil', function() { a2[0] = 1; const diffBuilder = new privateUnderTest.DiffBuilder(); expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false); - jasmine.debugLog('Diff: ' + diffBuilder.getMessage()); + jasmine.debugLog( + 'a1 keys: ' + jasmine.pp(privateUnderTest.MatchersUtil.keys(a1)) + ); + jasmine.debugLog( + 'a2 keys: ' + jasmine.pp(privateUnderTest.MatchersUtil.keys(a2)) + ); + jasmine.debugLog('a1 length:' + a1.length); + jasmine.debugLog('a2 length:' + a2.length); + jasmine.debugLog('a1[0]: ' + a1[0]); + jasmine.debugLog('a2[0]: ' + a2[0]); } ); diff --git a/src/core/ExceptionFormatter.js b/src/core/ExceptionFormatter.js index 7c6b8f01..74cc7928 100644 --- a/src/core/ExceptionFormatter.js +++ b/src/core/ExceptionFormatter.js @@ -11,7 +11,8 @@ getJasmineRequireObj().ExceptionFormatter = function(j$, private$) { 'lineNumber', 'column', 'description', - 'jasmineMessage' + 'jasmineMessage', + 'errors' ]; function ExceptionFormatter(options) { @@ -77,6 +78,19 @@ getJasmineRequireObj().ExceptionFormatter = function(j$, private$) { lines = lines.concat(substack); } + if (Array.isArray(error.errors)) { + for (let i = 0; i < error.errors.length; i++) { + if (error.errors[i] instanceof Error) { + lines.push(''); + const substack = this.stack_(error.errors[i], { + messageHandling: 'require' + }); + substack[0] = 'Error ' + (i + 1) + ': ' + substack[0]; + lines = lines.concat(substack.map(x => ' ' + x)); + } + } + } + return lines; }; diff --git a/src/core/base.js b/src/core/base.js index 2e7dad0b..9f1fa540 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -22,7 +22,7 @@ getJasmineRequireObj().base = function(j$, private$, jasmineGlobal) { * Maximum number of characters to display when pretty printing objects. * Characters past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_CHARS - * @default 100 + * @default 1000 * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; diff --git a/src/core/reporterEvents.js b/src/core/reporterEvents.js index 638c6c49..b537f1ef 100644 --- a/src/core/reporterEvents.js +++ b/src/core/reporterEvents.js @@ -7,7 +7,6 @@ getJasmineRequireObj().reporterEvents = function(j$, private$) { * {@link ReporterCapabilities} will apply. * @name Reporter#reporterCapabilities * @type ReporterCapabilities | undefined - * @optional * @since 5.0 */ /** @@ -74,7 +73,6 @@ getJasmineRequireObj().reporterEvents = function(j$, private$) { /** * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) * @function - * @optional * @name Reporter#specStarted * @param {SpecStartedEvent} result Information about the individual {@link it} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 48e91cc6..1d153dc8 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -19,6 +19,9 @@ const getJasmineRequireObj = (function() { return jasmineRequire; } + const loadedAsBrowserEsm = + globalThis.document && !globalThis.document.currentScript; + getJasmineRequire().core = function(jRequire) { const private$ = {}; const j$ = {}; @@ -111,8 +114,7 @@ const getJasmineRequireObj = (function() { private$ ); - private$.loadedAsBrowserEsm = - globalThis.document && !globalThis.document.currentScript; + private$.loadedAsBrowserEsm = loadedAsBrowserEsm; return { jasmine: j$, private: private$ }; }; diff --git a/src/core/requireInterface.js b/src/core/requireInterface.js index a6e8af59..6f7e74b3 100644 --- a/src/core/requireInterface.js +++ b/src/core/requireInterface.js @@ -20,6 +20,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ @@ -35,6 +36,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ @@ -51,6 +53,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ @@ -69,6 +72,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {String} description Textual description of what this spec is checking * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. @@ -86,6 +90,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. */ @@ -101,6 +106,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} testFunction Function that contains the code of your test. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. @@ -116,6 +122,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. * @see async @@ -130,6 +137,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. * @see async @@ -146,6 +154,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. * @see async @@ -162,6 +171,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. * @see async @@ -175,6 +185,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name getSpecProperty * @since 5.10.0 * @function + * @global + * @overwritable * @param {String} key The name of the property * @returns {*} The value of the property */ @@ -187,6 +199,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name setSpecProperty * @since 3.6.0 * @function + * @global + * @overwritable * @param {String} key The name of the property * @param {*} value The value of the property */ @@ -199,6 +213,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name setSuiteProperty * @since 3.6.0 * @function + * @global + * @overwritable * @param {String} key The name of the property * @param {*} value The value of the property */ @@ -212,6 +228,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ @@ -228,6 +245,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 3.3.0 * @function * @global + * @overwritable * @param {Object} actual - Actual computed value to test expectations against. * @return {async-matchers} * @example @@ -254,8 +272,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name throwUnlessAsync * @since 5.1.0 * @function - * @param actual * @global + * @overwritable * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ @@ -278,8 +296,8 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @name throwUnless * @since 5.1.0 * @function - * @param actual * @global + * @overwritable * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ @@ -293,6 +311,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.0.0 * @function * @global + * @overwritable * @param {String} [message] - Reason the spec is pending. */ pending: function() { @@ -305,6 +324,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.1.0 * @function * @global + * @overwritable * @param {String|Error} [error] - Reason for the failure. */ fail: function() { @@ -317,6 +337,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 1.3.0 * @function * @global + * @overwritable * @param {Object} obj - The object upon which to install the {@link Spy}. * @param {String} methodName - The name of the method to replace with a {@link Spy}. * @returns {Spy} @@ -331,6 +352,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 2.6.0 * @function * @global + * @overwritable * @param {Object} obj - The object upon which to install the {@link Spy} * @param {String} propertyName - The name of the property to replace with a {@link Spy}. * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. @@ -346,6 +368,7 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @since 3.2.1 * @function * @global + * @overwritable * @param {Object} obj - The object upon which to install the {@link Spy}s * @param {boolean} includeNonEnumerable - Whether or not to add spies to non-enumerable properties * @returns {Object} the spied object