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 8309416cb2.
This commit is contained in:
Steve Gravrock
2025-10-05 09:59:36 -07:00
parent 489b83c61b
commit dbc1f9244e
10 changed files with 52 additions and 279 deletions

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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;
}
};
}
});

View File

@@ -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/
);
});

View File

@@ -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);
});
});

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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)
);
}

View File

@@ -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);
/**

View File

@@ -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();
};