From c59009566204cddc6afd591660f857a0d7e5f7df Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 29 Sep 2025 19:53:25 -0700 Subject: [PATCH 1/3] Copy 6.0.0-alpha.0 release notes from branch --- release_notes/6.0.0-alpha.0.md | 100 +++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 release_notes/6.0.0-alpha.0.md diff --git a/release_notes/6.0.0-alpha.0.md b/release_notes/6.0.0-alpha.0.md new file mode 100644 index 00000000..93d954dd --- /dev/null +++ b/release_notes/6.0.0-alpha.0.md @@ -0,0 +1,100 @@ +# Jasmine Core 6.0.0-alpha.0 Release Notes + +This is a pre-release, intended to offer a preview of breaking 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-alpha.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-alpha.0`. + +## 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 that's 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. + +* Arguments to `setSpecProperty`/`setSuiteProperty` must be both + structured-cloneable and JSON-serializable. +* Mock clock timing functions cannot be spied on. Previously this "worked" but + prevented the mock clock from uninstalling itself. +* The default value of the `forbidDuplicateNames` config option has been + changed to true. +* The mock clock no longer supports the eval forms of `setTimeout` and + `setInterval`. +* 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.0-alpha.0/Spec.html) + instance, not the internal spec object. + +### 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. Please [open an issue](https://github.com/jasmine/jasmine/issues/new?template=bug_report.yml) +if you find a published reporter package that works with jasmine-core 5.x but +not with this release. + +* Irrelevant properties such as `status` and `failedExpectations` are omitted + from [the event passed to specStarted](https://jasmine.github.io/api/6.0.0-alpha.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.0-alpha.0/global.html#ExpectationResult) + have been removed. +* The [order](https://jasmine.github.io/api/6.0.0-alpha.0/global.html#Order) + property of the`jasmineStarted` and `jasmineDone` reporter events no longer + includes undocumented properties. + +### Changes to Node boot functions + +* [boot](https://jasmine.github.io/api/6.0.0-alpha.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.0-alpha.0/module-jasmine-core.html#.noGlobals) + no longer takes a parameter. It always returns the same object when called + repeatedly. + +## Supported environments + +This version has been tested in the following environments. + +| Environment | Supported versions | +|-------------------|--------------------------------| +| Node | 20, 22, 24 | +| Safari | 16**, 17** | +| Chrome | 140* | +| Firefox | 102**, 115**, 128**, 140, 143* | +| Edge | 140* | + +\* 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)_ From 489b83c61b558cc20c5b1be6a9779ecfa213ca41 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sun, 5 Oct 2025 09:54:25 -0700 Subject: [PATCH 2/3] Revert "Move knowledge of query parameters out of boot1.js" This reverts commit 6715f24fd0ccbe8a53392dcd62a494db127f6271. --- lib/jasmine-core/boot1.js | 17 ++++++++++++++-- lib/jasmine-core/jasmine-html.js | 19 +++++++++--------- spec/html/HtmlExactSpecFilterSpec.js | 30 ++++++++-------------------- spec/html/HtmlReporterSpec.js | 19 ++---------------- src/boot/boot1.js | 17 ++++++++++++++-- src/html/HtmlExactSpecFilter.js | 13 +++++++----- src/html/HtmlReporter.js | 6 +----- 7 files changed, 58 insertions(+), 63 deletions(-) diff --git a/lib/jasmine-core/boot1.js b/lib/jasmine-core/boot1.js index c1bf086b..302fceec 100644 --- a/lib/jasmine-core/boot1.js +++ b/lib/jasmine-core/boot1.js @@ -38,12 +38,20 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (function() { const env = jasmine.getEnv(); + /** + * ## Runner Parameters + * + * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. + */ + const queryString = new jasmine.QueryString({ getWindowLocation: function() { return window.location; } }); + const filterSpecs = !!queryString.getParam('spec'); + const config = { stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'), stopSpecOnExpectationFailure: queryString.getParam( @@ -85,7 +93,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. return document.createTextNode.apply(document, arguments); }, timer: new jasmine.Timer(), - queryString + filterSpecs: filterSpecs }); /** @@ -97,7 +105,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Filter which specs will be run by matching the start of the full name against the `spec` query param. */ - const specFilter = new jasmine.HtmlExactSpecFilter({ queryString }); + const specFilter = new jasmine.HtmlExactSpecFilter({ + filterString: function() { + return queryString.getParam('spec'); + } + }); + config.specFilter = function(spec) { return specFilter.matches(spec); }; diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 61f389d1..c9aa3c59 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -99,14 +99,10 @@ jasmineRequire.HtmlReporter = function(j$) { const getContainer = options.getContainer; const createElement = options.createElement; const createTextNode = options.createTextNode; - // TODO: in the next major release, replace navigateWithNewParam and - // addToExistingQueryString with direct usage of options.queryString const navigateWithNewParam = options.navigateWithNewParam || function() {}; const addToExistingQueryString = options.addToExistingQueryString || defaultQueryString; - const filterSpecs = options.queryString - ? !!options.queryString.getParam('spec') - : options.filterSpecs; // For compatibility with pre-5.11 boot files + const filterSpecs = options.filterSpecs; let htmlReporterMain; let symbols; const deprecationWarnings = []; @@ -1065,13 +1061,16 @@ jasmineRequire.HtmlExactSpecFilter = function() { /** * Create a filter instance. - * @param options Object with a queryString property, which should be an - * instance of {@link QueryString}. + * @param options Object with a filterString method, which should + * return the value of the "spec" query string parameter set by + * {@link HtmlReporter}. */ constructor(options) { - this.#getFilterString = function() { - return options.queryString.getParam('spec'); - }; + if (typeof options?.filterString !== 'function') { + throw new Error('options.filterString must be a function'); + } + + this.#getFilterString = options.filterString; } /** diff --git a/spec/html/HtmlExactSpecFilterSpec.js b/spec/html/HtmlExactSpecFilterSpec.js index ada0c49e..9fcef7d3 100644 --- a/spec/html/HtmlExactSpecFilterSpec.js +++ b/spec/html/HtmlExactSpecFilterSpec.js @@ -1,10 +1,8 @@ describe('HtmlExactSpecFilter', function() { it('matches everything when no string is provided', function() { const specFilter = new jasmineUnderTest.HtmlExactSpecFilter({ - queryString: { - getParam(name) { - return ''; - } + filterString() { + return ''; } }); @@ -13,12 +11,8 @@ describe('HtmlExactSpecFilter', function() { it('matches a spec with the exact same path', function() { const specFilter = new jasmineUnderTest.HtmlExactSpecFilter({ - queryString: { - getParam(name) { - if (name === 'spec') { - return '["a","b","c"]'; - } - } + filterString() { + return '["a","b","c"]'; } }); @@ -27,12 +21,8 @@ describe('HtmlExactSpecFilter', function() { it('matches a spec whose path has the filter path as a prefix', function() { const specFilter = new jasmineUnderTest.HtmlExactSpecFilter({ - queryString: { - getParam(name) { - if (name === 'spec') { - return '["a","b"]'; - } - } + filterString() { + return '["a","b"]'; } }); @@ -41,12 +31,8 @@ describe('HtmlExactSpecFilter', function() { it('does not match a spec with a different path', function() { const specFilter = new jasmineUnderTest.HtmlExactSpecFilter({ - queryString: { - getParam(name) { - if (name === 'spec') { - return '["a","b","c"]'; - } - } + filterString() { + return '["a","b","c"]'; } }); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index a62fd187..fc06c560 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -1368,11 +1368,6 @@ describe('HtmlReporter', function() { }, createTextNode: function() { return document.createTextNode.apply(document, arguments); - }, - queryString: { - getParam(name) { - return ''; - } } }; specStatus = { @@ -1387,12 +1382,7 @@ describe('HtmlReporter', function() { describe('when the specs are not filtered', function() { beforeEach(function() { - reporterConfig.queryString.getParam = function(name) { - if (name !== 'spec') { - throw new Error('Unexpected query param ' + name); - } - return ''; - }; + reporterConfig.filterSpecs = false; reporter = new jasmineUnderTest.HtmlReporter(reporterConfig); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 1 }); @@ -1410,12 +1400,7 @@ describe('HtmlReporter', function() { describe('when the specs are filtered', function() { beforeEach(function() { - reporterConfig.queryString.getParam = function(name) { - if (name !== 'spec') { - throw new Error('Unexpected query param ' + name); - } - return 'not the empty string'; - }; + reporterConfig.filterSpecs = true; reporter = new jasmineUnderTest.HtmlReporter(reporterConfig); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 1 }); diff --git a/src/boot/boot1.js b/src/boot/boot1.js index 45547069..39327ca7 100644 --- a/src/boot/boot1.js +++ b/src/boot/boot1.js @@ -14,12 +14,20 @@ (function() { const env = jasmine.getEnv(); + /** + * ## Runner Parameters + * + * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. + */ + const queryString = new jasmine.QueryString({ getWindowLocation: function() { return window.location; } }); + const filterSpecs = !!queryString.getParam('spec'); + const config = { stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'), stopSpecOnExpectationFailure: queryString.getParam( @@ -61,7 +69,7 @@ return document.createTextNode.apply(document, arguments); }, timer: new jasmine.Timer(), - queryString + filterSpecs: filterSpecs }); /** @@ -73,7 +81,12 @@ /** * Filter which specs will be run by matching the start of the full name against the `spec` query param. */ - const specFilter = new jasmine.HtmlExactSpecFilter({ queryString }); + const specFilter = new jasmine.HtmlExactSpecFilter({ + filterString: function() { + return queryString.getParam('spec'); + } + }); + config.specFilter = function(spec) { return specFilter.matches(spec); }; diff --git a/src/html/HtmlExactSpecFilter.js b/src/html/HtmlExactSpecFilter.js index a027648c..f4209c7c 100644 --- a/src/html/HtmlExactSpecFilter.js +++ b/src/html/HtmlExactSpecFilter.js @@ -10,13 +10,16 @@ jasmineRequire.HtmlExactSpecFilter = function() { /** * Create a filter instance. - * @param options Object with a queryString property, which should be an - * instance of {@link QueryString}. + * @param options Object with a filterString method, which should + * return the value of the "spec" query string parameter set by + * {@link HtmlReporter}. */ constructor(options) { - this.#getFilterString = function() { - return options.queryString.getParam('spec'); - }; + if (typeof options?.filterString !== 'function') { + throw new Error('options.filterString must be a function'); + } + + this.#getFilterString = options.filterString; } /** diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 88cafae1..ed011091 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -64,14 +64,10 @@ jasmineRequire.HtmlReporter = function(j$) { const getContainer = options.getContainer; const createElement = options.createElement; const createTextNode = options.createTextNode; - // TODO: in the next major release, replace navigateWithNewParam and - // addToExistingQueryString with direct usage of options.queryString const navigateWithNewParam = options.navigateWithNewParam || function() {}; const addToExistingQueryString = options.addToExistingQueryString || defaultQueryString; - const filterSpecs = options.queryString - ? !!options.queryString.getParam('spec') - : options.filterSpecs; // For compatibility with pre-5.11 boot files + const filterSpecs = options.filterSpecs; let htmlReporterMain; let symbols; const deprecationWarnings = []; From dbc1f9244e839a5328707460a8a46d1c530aa57b Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sun, 5 Oct 2025 09:59:36 -0700 Subject: [PATCH 3/3] Revert "Clicking a link in the HTML reporter does exact filtering" This change broke spec filtering in Karma by changing the format of the `spec` query parameter. Although karma-jasmine-html-reporter uses jasmine-core's HtmlSpecFilter, karm-jasmine provides its own spec filter that interprets the query parameters itself. This feature may be reintroduced in 6.0 as a breaking change. This reverts commit 8309416cb2ab2d343f2ce83a8f7323913ce60062. --- lib/jasmine-core/boot1.js | 4 +- lib/jasmine-core/jasmine-html.js | 120 +++++---------------------- spec/html/HtmlExactSpecFilterSpec.js | 49 ----------- spec/html/HtmlReporterSpec.js | 20 ++--- spec/html/HtmlSpecFilterSpec.js | 15 +--- src/boot/boot1.js | 4 +- src/html/HtmlExactSpecFilter.js | 55 ------------ src/html/HtmlReporter.js | 41 ++++----- src/html/HtmlSpecFilter.js | 22 +---- src/html/requireHtml.js | 1 - 10 files changed, 52 insertions(+), 279 deletions(-) delete mode 100644 spec/html/HtmlExactSpecFilterSpec.js delete mode 100644 src/html/HtmlExactSpecFilter.js diff --git a/lib/jasmine-core/boot1.js b/lib/jasmine-core/boot1.js index 302fceec..8c12f8c7 100644 --- a/lib/jasmine-core/boot1.js +++ b/lib/jasmine-core/boot1.js @@ -105,14 +105,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Filter which specs will be run by matching the start of the full name against the `spec` query param. */ - const specFilter = new jasmine.HtmlExactSpecFilter({ + const specFilter = new jasmine.HtmlSpecFilter({ filterString: function() { return queryString.getParam('spec'); } }); config.specFilter = function(spec) { - return specFilter.matches(spec); + return specFilter.matches(spec.getFullName()); }; env.configure(config); diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index c9aa3c59..705a8c8f 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -30,7 +30,6 @@ jasmineRequire.html = function(j$) { j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); j$.QueryString = jasmineRequire.QueryString(); j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); - j$.HtmlExactSpecFilter = jasmineRequire.HtmlExactSpecFilter(); }; jasmineRequire.HtmlReporter = function(j$) { @@ -40,13 +39,11 @@ jasmineRequire.HtmlReporter = function(j$) { this.specsExecuted = 0; this.failureCount = 0; this.pendingSpecCount = 0; - this.suitesById = []; } ResultsStateBuilder.prototype.suiteStarted = function(result) { this.currentParent.addChild(result, 'suite'); this.currentParent = this.currentParent.last(); - this.suitesById[result.id] = this.currentParent; }; ResultsStateBuilder.prototype.suiteDone = function(result) { @@ -731,6 +728,21 @@ jasmineRequire.HtmlReporter = function(j$) { return wrapper; } + function suiteHref(suite) { + const els = []; + + while (suite && suite.parent) { + els.unshift(suite.result.description); + suite = suite.parent; + } + + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (window.location.pathname || '') + + addToExistingQueryString('spec', els.join(' ')) + ); + } + function addDeprecationWarnings(result, runnableType) { if (result && result.deprecationWarnings) { for (let i = 0; i < result.deprecationWarnings.length; i++) { @@ -828,33 +840,11 @@ jasmineRequire.HtmlReporter = function(j$) { return '' + count + ' ' + word; } - function suitePath(suite) { - const els = []; - - while (suite && suite.parent) { - els.unshift(suite.result.description); - suite = suite.parent; - } - - return els; - } - - function suiteHref(suite) { - return pathHref(suitePath(suite)); - } - function specHref(result) { - const suite = stateBuilder.suitesById[result.parentSuiteId]; - const path = suitePath(suite); - path.push(result.description); - return pathHref(path); - } - - function pathHref(path) { // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + - addToExistingQueryString('spec', JSON.stringify(path)) + addToExistingQueryString('spec', result.fullName) ); } @@ -903,27 +893,13 @@ jasmineRequire.HtmlReporter = function(j$) { }; jasmineRequire.HtmlSpecFilter = function() { - /** - * @name HtmlSpecFilter - * @classdesc Legacy HTML spec filter, for backward compatibility - * with boot files that predate {@link HtmlExactSpecFilter}. - * @param options Object with a filterString method - * @constructor - * @deprecated - * @since 1.2.0 - */ // Legacy HTML spec filter, preserved for backward compatibility with // boot files that predate HtmlExactSpecFilterV2 function HtmlSpecFilter(options) { - let filterString = (options && options.filterString()) || ''; - - if (filterString.startsWith('[')) { - // Convert an HtmlExactSpecFilterV2 string into something we can use - filterString = JSON.parse(filterString).join(' '); - } - - filterString = filterString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); - + const filterString = + options && + options.filterString() && + options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); const filterPattern = new RegExp(filterString); /** @@ -1048,59 +1024,3 @@ jasmineRequire.QueryString = function() { return QueryString; }; - -jasmineRequire.HtmlExactSpecFilter = function() { - /** - * Spec filter for use with {@link HtmlReporter} - * - * See lib/jasmine-core/boot1.js for usage. - * @since 5.11.0 - */ - class HtmlExactSpecFilter { - #getFilterString; - - /** - * Create a filter instance. - * @param options Object with a filterString method, which should - * return the value of the "spec" query string parameter set by - * {@link HtmlReporter}. - */ - constructor(options) { - if (typeof options?.filterString !== 'function') { - throw new Error('options.filterString must be a function'); - } - - this.#getFilterString = options.filterString; - } - - /** - * Determines whether the specified spec should be executed. - * @param {Spec} spec - * @returns {boolean} - */ - matches(spec) { - const filterString = this.#getFilterString(); - - if (!filterString) { - return true; - } - - const filterPath = JSON.parse(this.#getFilterString()); - const specPath = spec.getPath(); - - if (filterPath.length > specPath.length) { - return false; - } - - for (let i = 0; i < filterPath.length; i++) { - if (specPath[i] !== filterPath[i]) { - return false; - } - } - - return true; - } - } - - return HtmlExactSpecFilter; -}; diff --git a/spec/html/HtmlExactSpecFilterSpec.js b/spec/html/HtmlExactSpecFilterSpec.js deleted file mode 100644 index 9fcef7d3..00000000 --- a/spec/html/HtmlExactSpecFilterSpec.js +++ /dev/null @@ -1,49 +0,0 @@ -describe('HtmlExactSpecFilter', function() { - it('matches everything when no string is provided', function() { - const specFilter = new jasmineUnderTest.HtmlExactSpecFilter({ - filterString() { - return ''; - } - }); - - expect(specFilter.matches({})).toBeTrue(); - }); - - it('matches a spec with the exact same path', function() { - const specFilter = new jasmineUnderTest.HtmlExactSpecFilter({ - filterString() { - return '["a","b","c"]'; - } - }); - - expect(specFilter.matches(stubSpec(['a', 'b', 'c']))).toBeTrue(); - }); - - it('matches a spec whose path has the filter path as a prefix', function() { - const specFilter = new jasmineUnderTest.HtmlExactSpecFilter({ - filterString() { - return '["a","b"]'; - } - }); - - expect(specFilter.matches(stubSpec(['a', 'b', 'c']))).toBeTrue(); - }); - - it('does not match a spec with a different path', function() { - const specFilter = new jasmineUnderTest.HtmlExactSpecFilter({ - filterString() { - return '["a","b","c"]'; - } - }); - - expect(specFilter.matches(stubSpec(['a', 'd', 'c']))).toBeFalse(); - }); - - function stubSpec(path) { - return { - getPath() { - return path; - } - }; - } -}); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index fc06c560..9e552f8d 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -528,7 +528,6 @@ describe('HtmlReporter', function() { let specResult = { id: 123, - parentSuiteId: 1, description: 'with a spec', fullName: 'A Suite with a spec', status: 'passed', @@ -606,9 +605,7 @@ describe('HtmlReporter', function() { const suiteDetail = outerSuite.childNodes[0]; const suiteLink = suiteDetail.childNodes[0]; expect(suiteLink.innerHTML).toEqual('A Suite'); - expect(suiteLink.getAttribute('href')).toEqual( - '/?foo=bar&spec=["A Suite"]' - ); + expect(suiteLink.getAttribute('href')).toEqual('/?foo=bar&spec=A Suite'); const specs = outerSuite.childNodes[1]; const spec = specs.childNodes[0]; @@ -618,7 +615,7 @@ describe('HtmlReporter', function() { const specLink = spec.childNodes[0]; expect(specLink.innerHTML).toEqual('with a spec'); expect(specLink.getAttribute('href')).toEqual( - '/?foo=bar&spec=["A Suite","with a spec"]' + '/?foo=bar&spec=A Suite with a spec' ); const specDuration = spec.childNodes[1]; @@ -1544,7 +1541,6 @@ describe('HtmlReporter', function() { const failingSpecResult = { id: 124, - parentSuiteId: 2, status: 'failed', description: 'a failing spec', fullName: 'a suite inner suite a failing spec', @@ -1668,18 +1664,16 @@ describe('HtmlReporter', function() { expect(links.length).toEqual(3); expect(links[0].textContent).toEqual('A suite'); - expect(links[0].getAttribute('href')).toEqual( - '/?foo=bar&spec=["A suite"]' - ); + expect(links[0].getAttribute('href')).toMatch(/\?foo=bar&spec=A suite/); expect(links[1].textContent).toEqual('inner suite'); - expect(links[1].getAttribute('href')).toEqual( - '/?foo=bar&spec=["A suite","inner suite"]' + expect(links[1].getAttribute('href')).toMatch( + /\?foo=bar&spec=A suite inner suite/ ); expect(links[2].textContent).toEqual('a failing spec'); - expect(links[2].getAttribute('href')).toEqual( - '/?foo=bar&spec=["A suite","inner suite","a failing spec"]' + expect(links[2].getAttribute('href')).toMatch( + /\?foo=bar&spec=a suite inner suite a failing spec/ ); }); diff --git a/spec/html/HtmlSpecFilterSpec.js b/spec/html/HtmlSpecFilterSpec.js index ccfdc11d..8fa9a05d 100644 --- a/spec/html/HtmlSpecFilterSpec.js +++ b/spec/html/HtmlSpecFilterSpec.js @@ -1,4 +1,4 @@ -describe('HtmlSpecFilter', function() { +describe('jasmineUnderTest.HtmlSpecFilter', function() { it('should match when no string is provided', function() { const specFilter = new jasmineUnderTest.HtmlSpecFilter(); @@ -16,17 +16,4 @@ describe('HtmlSpecFilter', function() { expect(specFilter.matches('foo')).toBe(true); expect(specFilter.matches('bar')).toBe(false); }); - - it('copes with HtmlExactSpecFilterV2 filter strings', function() { - const specFilter = new jasmineUnderTest.HtmlSpecFilter({ - filterString: function() { - return '["foo","bar"]'; - } - }); - - expect(specFilter.matches('foo bar')).toBe(true); - expect(specFilter.matches('baz foo bar qux')).toBe(true); - expect(specFilter.matches('foo')).toBe(false); - expect(specFilter.matches('bar')).toBe(false); - }); }); diff --git a/src/boot/boot1.js b/src/boot/boot1.js index 39327ca7..55d7e800 100644 --- a/src/boot/boot1.js +++ b/src/boot/boot1.js @@ -81,14 +81,14 @@ /** * Filter which specs will be run by matching the start of the full name against the `spec` query param. */ - const specFilter = new jasmine.HtmlExactSpecFilter({ + const specFilter = new jasmine.HtmlSpecFilter({ filterString: function() { return queryString.getParam('spec'); } }); config.specFilter = function(spec) { - return specFilter.matches(spec); + return specFilter.matches(spec.getFullName()); }; env.configure(config); diff --git a/src/html/HtmlExactSpecFilter.js b/src/html/HtmlExactSpecFilter.js deleted file mode 100644 index f4209c7c..00000000 --- a/src/html/HtmlExactSpecFilter.js +++ /dev/null @@ -1,55 +0,0 @@ -jasmineRequire.HtmlExactSpecFilter = function() { - /** - * Spec filter for use with {@link HtmlReporter} - * - * See lib/jasmine-core/boot1.js for usage. - * @since 5.11.0 - */ - class HtmlExactSpecFilter { - #getFilterString; - - /** - * Create a filter instance. - * @param options Object with a filterString method, which should - * return the value of the "spec" query string parameter set by - * {@link HtmlReporter}. - */ - constructor(options) { - if (typeof options?.filterString !== 'function') { - throw new Error('options.filterString must be a function'); - } - - this.#getFilterString = options.filterString; - } - - /** - * Determines whether the specified spec should be executed. - * @param {Spec} spec - * @returns {boolean} - */ - matches(spec) { - const filterString = this.#getFilterString(); - - if (!filterString) { - return true; - } - - const filterPath = JSON.parse(this.#getFilterString()); - const specPath = spec.getPath(); - - if (filterPath.length > specPath.length) { - return false; - } - - for (let i = 0; i < filterPath.length; i++) { - if (specPath[i] !== filterPath[i]) { - return false; - } - } - - return true; - } - } - - return HtmlExactSpecFilter; -}; diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index ed011091..6f846b7c 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -5,13 +5,11 @@ jasmineRequire.HtmlReporter = function(j$) { this.specsExecuted = 0; this.failureCount = 0; this.pendingSpecCount = 0; - this.suitesById = []; } ResultsStateBuilder.prototype.suiteStarted = function(result) { this.currentParent.addChild(result, 'suite'); this.currentParent = this.currentParent.last(); - this.suitesById[result.id] = this.currentParent; }; ResultsStateBuilder.prototype.suiteDone = function(result) { @@ -696,6 +694,21 @@ jasmineRequire.HtmlReporter = function(j$) { return wrapper; } + function suiteHref(suite) { + const els = []; + + while (suite && suite.parent) { + els.unshift(suite.result.description); + suite = suite.parent; + } + + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (window.location.pathname || '') + + addToExistingQueryString('spec', els.join(' ')) + ); + } + function addDeprecationWarnings(result, runnableType) { if (result && result.deprecationWarnings) { for (let i = 0; i < result.deprecationWarnings.length; i++) { @@ -793,33 +806,11 @@ jasmineRequire.HtmlReporter = function(j$) { return '' + count + ' ' + word; } - function suitePath(suite) { - const els = []; - - while (suite && suite.parent) { - els.unshift(suite.result.description); - suite = suite.parent; - } - - return els; - } - - function suiteHref(suite) { - return pathHref(suitePath(suite)); - } - function specHref(result) { - const suite = stateBuilder.suitesById[result.parentSuiteId]; - const path = suitePath(suite); - path.push(result.description); - return pathHref(path); - } - - function pathHref(path) { // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + - addToExistingQueryString('spec', JSON.stringify(path)) + addToExistingQueryString('spec', result.fullName) ); } diff --git a/src/html/HtmlSpecFilter.js b/src/html/HtmlSpecFilter.js index 187b20f2..20e00bd0 100644 --- a/src/html/HtmlSpecFilter.js +++ b/src/html/HtmlSpecFilter.js @@ -1,25 +1,11 @@ jasmineRequire.HtmlSpecFilter = function() { - /** - * @name HtmlSpecFilter - * @classdesc Legacy HTML spec filter, for backward compatibility - * with boot files that predate {@link HtmlExactSpecFilter}. - * @param options Object with a filterString method - * @constructor - * @deprecated - * @since 1.2.0 - */ // Legacy HTML spec filter, preserved for backward compatibility with // boot files that predate HtmlExactSpecFilterV2 function HtmlSpecFilter(options) { - let filterString = (options && options.filterString()) || ''; - - if (filterString.startsWith('[')) { - // Convert an HtmlExactSpecFilterV2 string into something we can use - filterString = JSON.parse(filterString).join(' '); - } - - filterString = filterString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); - + const filterString = + options && + options.filterString() && + options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); const filterPattern = new RegExp(filterString); /** diff --git a/src/html/requireHtml.js b/src/html/requireHtml.js index 522db07f..9df50b08 100644 --- a/src/html/requireHtml.js +++ b/src/html/requireHtml.js @@ -6,5 +6,4 @@ jasmineRequire.html = function(j$) { j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); j$.QueryString = jasmineRequire.QueryString(); j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); - j$.HtmlExactSpecFilter = jasmineRequire.HtmlExactSpecFilter(); };