Re-add support for partial spec name filtering

No UI for this but users can construct URLs manually.
Fixes #2085.
This commit is contained in:
Steve Gravrock
2025-10-24 17:26:49 -07:00
parent 6667a42301
commit 85322d1877
8 changed files with 114 additions and 69 deletions

View File

@@ -58,6 +58,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
env.addReporter(jsApiReporter); env.addReporter(jsApiReporter);
env.addReporter(htmlReporter); env.addReporter(htmlReporter);
/**
* Configures Jasmine based on the current set of query parameters. This
* supports all parameters set by the HTML reporter as well as
* spec=partialPath, which filters out specs whose paths don't contain the
* parameter.
*/
env.configure(urls.configFromCurrentUrl()); env.configure(urls.configFromCurrentUrl());
/** /**

View File

@@ -1233,7 +1233,10 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
} }
/** /**
* Creates a {@link Configuration} from the current page's URL. * Creates a {@link Configuration} from the current page's URL. Supported
* query string parameters include all those set by {@link HtmlReporterV2}
* as well as spec=partialPath, which filters out specs whose paths don't
* contain partialPath.
* @returns {Configuration} * @returns {Configuration}
* @example * @example
* const urls = new jasmine.HtmlReporterV2Urls(); * const urls = new jasmine.HtmlReporterV2Urls();
@@ -1259,9 +1262,10 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
} }
const specFilter = new j$.private.HtmlSpecFilterV2({ const specFilter = new j$.private.HtmlSpecFilterV2({
filterString: () => { filterParams: () => ({
return this.queryString.getParam('path'); path: this.queryString.getParam('path'),
} spec: this.queryString.getParam('spec')
})
}); });
config.specFilter = function(spec) { config.specFilter = function(spec) {
@@ -1281,35 +1285,35 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
jasmineRequire.HtmlSpecFilterV2 = function() { jasmineRequire.HtmlSpecFilterV2 = function() {
class HtmlSpecFilterV2 { class HtmlSpecFilterV2 {
#getFilterString; #getFilterParams;
constructor(options) { constructor(options) {
this.#getFilterString = options.filterString; this.#getFilterParams = options.filterParams;
} }
/**
* Determines whether the spec with the specified name should be executed.
* @name HtmlSpecFilterV2#matches
* @function
* @param {Spec} spec
* @returns {boolean}
*/
matches(spec) { matches(spec) {
const filterString = this.#getFilterString(); const params = this.#getFilterParams();
if (!filterString) { if (params.path) {
return true; return this.#matchesPath(spec, JSON.parse(params.path));
} else if (params.spec) {
// Like legacy HtmlSpecFilter, retained because it's convenient for
// hand-constructing filter URLs
return spec.getFullName().includes(params.spec);
} }
const filterPath = JSON.parse(this.#getFilterString()); return true;
}
#matchesPath(spec, path) {
const specPath = spec.getPath(); const specPath = spec.getPath();
if (filterPath.length > specPath.length) { if (path.length > specPath.length) {
return false; return false;
} }
for (let i = 0; i < filterPath.length; i++) { for (let i = 0; i < path.length; i++) {
if (specPath[i] !== filterPath[i]) { if (specPath[i] !== path[i]) {
return false; return false;
} }
} }

View File

@@ -8905,6 +8905,7 @@ getJasmineRequireObj().reporterEvents = function(j$) {
* {@link ReporterCapabilities} will apply. * {@link ReporterCapabilities} will apply.
* @name Reporter#reporterCapabilities * @name Reporter#reporterCapabilities
* @type ReporterCapabilities | undefined * @type ReporterCapabilities | undefined
* @optional
* @since 5.0 * @since 5.0
*/ */
/** /**
@@ -8971,6 +8972,7 @@ getJasmineRequireObj().reporterEvents = function(j$) {
/** /**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function * @function
* @optional
* @name Reporter#specStarted * @name Reporter#specStarted
* @param {SpecStartedEvent} result Information about the individual {@link it} being run * @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. * @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.

View File

@@ -1,48 +1,69 @@
describe('HtmlSpecFilterV2', function() { describe('HtmlSpecFilterV2', function() {
it('matches everything when no string is provided', function() { describe('When both query parameters are falsy', function() {
const specFilter = new privateUnderTest.HtmlSpecFilterV2({ it('matches everything', function() {
filterString() { const specFilter = new privateUnderTest.HtmlSpecFilterV2({
return ''; filterParams() {
} return { path: '', spec: '' };
}); }
});
expect(specFilter.matches({})).toBeTrue(); expect(specFilter.matches({})).toBeTrue();
});
}); });
it('matches a spec with the exact same path', function() { describe('When the path parameter is truthy', function() {
const specFilter = new privateUnderTest.HtmlSpecFilterV2({ it('matches a spec with the exact same path', function() {
filterString() { const specFilter = new privateUnderTest.HtmlSpecFilterV2({
return '["a","b","c"]'; filterParams() {
} return { path: '["a","b","c"]', spec: '' };
}
});
expect(specFilter.matches(stubSpec(['a', 'b', 'c']))).toBeTrue();
}); });
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 privateUnderTest.HtmlSpecFilterV2({
filterParams() {
return { path: '["a","b"]', spec: '' };
}
});
it('matches a spec whose path has the filter path as a prefix', function() { expect(specFilter.matches(stubSpec(['a', 'b', 'c']))).toBeTrue();
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
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 privateUnderTest.HtmlSpecFilterV2({
filterParams() {
return { path: '["a","b","c"]', spec: '' };
}
});
it('does not match a spec with a different path', function() { expect(specFilter.matches(stubSpec(['a', 'd', 'c']))).toBeFalse();
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
filterString() {
return '["a","b","c"]';
}
}); });
expect(specFilter.matches(stubSpec(['a', 'd', 'c']))).toBeFalse();
}); });
function stubSpec(path) { describe('When the path parameter is falsy and the spec parameter is truthy', function() {
it('matches specs with full names containing the parameter value', function() {
const specFilter = new privateUnderTest.HtmlSpecFilterV2({
filterParams() {
return { path: '', spec: 'bar' };
}
});
expect(specFilter.matches(stubSpec('', 'foo bar baz'))).toBeTrue();
expect(specFilter.matches(stubSpec('', 'foo baz'))).toBeFalse();
expect(specFilter.matches(stubSpec('', 'sandbars'))).toBeTrue();
});
});
function stubSpec(path, fullName) {
return { return {
getPath() { getPath() {
return path; return path;
},
getFullName() {
return fullName;
} }
}; };
} }

View File

@@ -34,6 +34,12 @@
*/ */
env.addReporter(jsApiReporter); env.addReporter(jsApiReporter);
env.addReporter(htmlReporter); env.addReporter(htmlReporter);
/**
* Configures Jasmine based on the current set of query parameters. This
* supports all parameters set by the HTML reporter as well as
* spec=partialPath, which filters out specs whose paths don't contain the
* parameter.
*/
env.configure(urls.configFromCurrentUrl()); env.configure(urls.configFromCurrentUrl());
/** /**

View File

@@ -7,6 +7,7 @@ getJasmineRequireObj().reporterEvents = function(j$) {
* {@link ReporterCapabilities} will apply. * {@link ReporterCapabilities} will apply.
* @name Reporter#reporterCapabilities * @name Reporter#reporterCapabilities
* @type ReporterCapabilities | undefined * @type ReporterCapabilities | undefined
* @optional
* @since 5.0 * @since 5.0
*/ */
/** /**
@@ -73,6 +74,7 @@ getJasmineRequireObj().reporterEvents = function(j$) {
/** /**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function * @function
* @optional
* @name Reporter#specStarted * @name Reporter#specStarted
* @param {SpecStartedEvent} result Information about the individual {@link it} being run * @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. * @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.

View File

@@ -21,7 +21,10 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
} }
/** /**
* Creates a {@link Configuration} from the current page's URL. * Creates a {@link Configuration} from the current page's URL. Supported
* query string parameters include all those set by {@link HtmlReporterV2}
* as well as spec=partialPath, which filters out specs whose paths don't
* contain partialPath.
* @returns {Configuration} * @returns {Configuration}
* @example * @example
* const urls = new jasmine.HtmlReporterV2Urls(); * const urls = new jasmine.HtmlReporterV2Urls();
@@ -47,9 +50,10 @@ jasmineRequire.HtmlReporterV2Urls = function(j$) {
} }
const specFilter = new j$.private.HtmlSpecFilterV2({ const specFilter = new j$.private.HtmlSpecFilterV2({
filterString: () => { filterParams: () => ({
return this.queryString.getParam('path'); path: this.queryString.getParam('path'),
} spec: this.queryString.getParam('spec')
})
}); });
config.specFilter = function(spec) { config.specFilter = function(spec) {

View File

@@ -1,34 +1,34 @@
jasmineRequire.HtmlSpecFilterV2 = function() { jasmineRequire.HtmlSpecFilterV2 = function() {
class HtmlSpecFilterV2 { class HtmlSpecFilterV2 {
#getFilterString; #getFilterParams;
constructor(options) { constructor(options) {
this.#getFilterString = options.filterString; this.#getFilterParams = options.filterParams;
} }
/**
* Determines whether the spec with the specified name should be executed.
* @name HtmlSpecFilterV2#matches
* @function
* @param {Spec} spec
* @returns {boolean}
*/
matches(spec) { matches(spec) {
const filterString = this.#getFilterString(); const params = this.#getFilterParams();
if (!filterString) { if (params.path) {
return true; return this.#matchesPath(spec, JSON.parse(params.path));
} else if (params.spec) {
// Like legacy HtmlSpecFilter, retained because it's convenient for
// hand-constructing filter URLs
return spec.getFullName().includes(params.spec);
} }
const filterPath = JSON.parse(this.#getFilterString()); return true;
}
#matchesPath(spec, path) {
const specPath = spec.getPath(); const specPath = spec.getPath();
if (filterPath.length > specPath.length) { if (path.length > specPath.length) {
return false; return false;
} }
for (let i = 0; i < filterPath.length; i++) { for (let i = 0; i < path.length; i++) {
if (specPath[i] !== filterPath[i]) { if (specPath[i] !== path[i]) {
return false; return false;
} }
} }