Deprecate access to non-public Spec properties in spec filters

This commit is contained in:
Steve Gravrock
2025-09-14 11:34:06 -07:00
parent 6d77f3e7f0
commit 3cbf4dc27b
6 changed files with 125 additions and 10 deletions

View File

@@ -66,6 +66,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.getClearStack = jRequire.clearStack(j$);
j$.Clock = jRequire.Clock();
j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$);
j$.deprecatingSpecProxy = jRequire.deprecatingSpecProxy(j$);
j$.Deprecator = jRequire.Deprecator(j$);
j$.Configuration = jRequire.Configuration(j$);
j$.Env = jRequire.Env(j$);
@@ -1498,7 +1499,8 @@ getJasmineRequireObj().Env = function(j$) {
runQueue,
TreeProcessor: j$.TreeProcessor,
globalErrors,
getConfig: () => config
getConfig: () => config,
deprecated: this.deprecated
});
this.setParallelLoadingState = function(state) {
@@ -3711,6 +3713,43 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
return DelayedFunctionScheduler;
};
// TODO: Remove this in the next major release.
getJasmineRequireObj().deprecatingSpecProxy = function(j$) {
const allowedMembers = ['id', 'description', 'getFullName', 'getPath'];
function isMember(target, prop) {
return (
Object.keys(target).indexOf(prop) !== -1 ||
Object.keys(j$.Spec.prototype).indexOf(prop) !== -1
);
}
function msg(member) {
const memberName = member.toString().replace(/^Symbol\((.+)\)$/, '$1');
return (
'Access to private Spec members (in this case `' +
memberName +
'`) via spec filters is not supported and will break in ' +
'a future release. See <https://jasmine.github.io/api/edge/Spec.html> ' +
'for correct usage.'
);
}
function deprecatingSpecProxy(spec, deprecated) {
return new Proxy(spec, {
get(target, prop, receiver) {
if (isMember(target, prop) && !allowedMembers.includes(prop)) {
deprecated(msg(prop));
}
return target[prop];
}
});
}
return deprecatingSpecProxy;
};
getJasmineRequireObj().Deprecator = function(j$) {
function Deprecator(topSuite) {
this.topSuite_ = topSuite;
@@ -9440,6 +9479,7 @@ getJasmineRequireObj().Runner = function(j$) {
#globalErrors;
#reportDispatcher;
#getConfig;
#deprecated;
#executedBefore;
#currentRunableTracker;
@@ -9453,6 +9493,7 @@ getJasmineRequireObj().Runner = function(j$) {
this.#globalErrors = options.globalErrors;
this.#reportDispatcher = options.reportDispatcher;
this.#getConfig = options.getConfig;
this.#deprecated = options.deprecated;
this.#executedBefore = false;
this.#currentRunableTracker = new j$.CurrentRunableTracker();
}
@@ -9505,8 +9546,9 @@ getJasmineRequireObj().Runner = function(j$) {
orderChildren: function(node) {
return order.sort(node.children);
},
excludeNode: function(spec) {
return !config.specFilter(spec);
excludeNode: spec => {
const proxy = j$.deprecatingSpecProxy(spec, this.#deprecated);
return !config.specFilter(proxy);
}
});
this.#executionTree = treeProcessor.processTree();

View File

@@ -913,6 +913,7 @@ describe('Env integration', function() {
});
env.configure({
random: false,
specFilter: function(spec) {
return /^first suite/.test(spec.getFullName());
}
@@ -920,13 +921,44 @@ describe('Env integration', function() {
await env.execute();
expect(calls.length).toEqual(2);
expect(calls).toEqual(
jasmine.arrayContaining(['first spec', 'second spec'])
);
expect(calls).toEqual(['first spec', 'second spec']);
expect(suiteCallback).toHaveBeenCalled();
});
it('reports a deprecation warning when a spec filter accesses private properties', async function() {
env.it('a spec', function() {});
const reporter = jasmine.createSpyObj('reporter', ['jasmineDone']);
env.addReporter(reporter);
env.configure({
random: false,
specFilter: function(spec) {
spec.result; // deprecated
spec.id; // not deprecated
spec.description; // not deprecated
spec.getPath(); // not deprecated
spec.getFullName(); // not deprecated
return true;
}
});
spyOn(console, 'error');
await env.execute();
expect(reporter.jasmineDone).toHaveBeenCalledWith(
jasmine.objectContaining({
deprecationWarnings: [
jasmine.objectContaining({
message: jasmine.stringContaining(
'Access to private Spec members (in this case `result`)'
)
})
]
})
);
});
it('Functions can be spied on and have their calls tracked', async function() {
let originalFunctionWasCalled = false;
const subject = {

View File

@@ -371,7 +371,8 @@ getJasmineRequireObj().Env = function(j$) {
runQueue,
TreeProcessor: j$.TreeProcessor,
globalErrors,
getConfig: () => config
getConfig: () => config,
deprecated: this.deprecated
});
this.setParallelLoadingState = function(state) {

View File

@@ -10,6 +10,7 @@ getJasmineRequireObj().Runner = function(j$) {
#globalErrors;
#reportDispatcher;
#getConfig;
#deprecated;
#executedBefore;
#currentRunableTracker;
@@ -23,6 +24,7 @@ getJasmineRequireObj().Runner = function(j$) {
this.#globalErrors = options.globalErrors;
this.#reportDispatcher = options.reportDispatcher;
this.#getConfig = options.getConfig;
this.#deprecated = options.deprecated;
this.#executedBefore = false;
this.#currentRunableTracker = new j$.CurrentRunableTracker();
}
@@ -75,8 +77,9 @@ getJasmineRequireObj().Runner = function(j$) {
orderChildren: function(node) {
return order.sort(node.children);
},
excludeNode: function(spec) {
return !config.specFilter(spec);
excludeNode: spec => {
const proxy = j$.deprecatingSpecProxy(spec, this.#deprecated);
return !config.specFilter(proxy);
}
});
this.#executionTree = treeProcessor.processTree();

View File

@@ -0,0 +1,36 @@
// TODO: Remove this in the next major release.
getJasmineRequireObj().deprecatingSpecProxy = function(j$) {
const allowedMembers = ['id', 'description', 'getFullName', 'getPath'];
function isMember(target, prop) {
return (
Object.keys(target).indexOf(prop) !== -1 ||
Object.keys(j$.Spec.prototype).indexOf(prop) !== -1
);
}
function msg(member) {
const memberName = member.toString().replace(/^Symbol\((.+)\)$/, '$1');
return (
'Access to private Spec members (in this case `' +
memberName +
'`) via spec filters is not supported and will break in ' +
'a future release. See <https://jasmine.github.io/api/edge/Spec.html> ' +
'for correct usage.'
);
}
function deprecatingSpecProxy(spec, deprecated) {
return new Proxy(spec, {
get(target, prop, receiver) {
if (isMember(target, prop) && !allowedMembers.includes(prop)) {
deprecated(msg(prop));
}
return target[prop];
}
});
}
return deprecatingSpecProxy;
};

View File

@@ -42,6 +42,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.getClearStack = jRequire.clearStack(j$);
j$.Clock = jRequire.Clock();
j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$);
j$.deprecatingSpecProxy = jRequire.deprecatingSpecProxy(j$);
j$.Deprecator = jRequire.Deprecator(j$);
j$.Configuration = jRequire.Configuration(j$);
j$.Env = jRequire.Env(j$);