Also require setSpecProperty/setSuiteProperty args to be JSON serializable

This commit is contained in:
Steve Gravrock
2025-09-27 15:40:19 -07:00
parent c2ce55580c
commit e11f320df3
5 changed files with 42 additions and 12 deletions

View File

@@ -761,9 +761,12 @@ getJasmineRequireObj().util = function(j$) {
} }
}; };
util.assertStructuredCloneable = function(v, msgPrefix) { util.assertReporterCloneable = function(v, msgPrefix) {
try { try {
structuredClone(v); // Reporter events are cloned internally via structuredClone, and it's
// common for reporters (including jasmine-browser-runner's) to JSON
// serialize them.
JSON.stringify(structuredClone(v));
} catch (e) { } catch (e) {
throw new Error(`${msgPrefix} can't be cloned`, { cause: e }); throw new Error(`${msgPrefix} can't be cloned`, { cause: e });
} }
@@ -848,8 +851,8 @@ getJasmineRequireObj().Spec = function(j$) {
// Key and value will eventually be cloned during reporting. The error // Key and value will eventually be cloned during reporting. The error
// thrown at that point if they aren't cloneable isn't very helpful. // thrown at that point if they aren't cloneable isn't very helpful.
// Throw a better one now. // Throw a better one now.
j$.private.util.assertStructuredCloneable(key, 'Key'); j$.private.util.assertReporterCloneable(key, 'Key');
j$.private.util.assertStructuredCloneable(value, 'Value'); j$.private.util.assertReporterCloneable(value, 'Value');
this.result.properties = this.result.properties || {}; this.result.properties = this.result.properties || {};
this.result.properties[key] = value; this.result.properties[key] = value;
} }
@@ -10631,8 +10634,8 @@ getJasmineRequireObj().Suite = function(j$) {
// Key and value will eventually be cloned during reporting. The error // Key and value will eventually be cloned during reporting. The error
// thrown at that point if they aren't cloneable isn't very helpful. // thrown at that point if they aren't cloneable isn't very helpful.
// Throw a better one now. // Throw a better one now.
j$.private.util.assertStructuredCloneable(key, 'Key'); j$.private.util.assertReporterCloneable(key, 'Key');
j$.private.util.assertStructuredCloneable(value, 'Value'); j$.private.util.assertReporterCloneable(value, 'Value');
this.result.properties = this.result.properties || {}; this.result.properties = this.result.properties || {};
this.result.properties[key] = value; this.result.properties[key] = value;
} }

View File

@@ -113,6 +113,18 @@ describe('Spec', function() {
}).toThrowError("Key can't be cloned"); }).toThrowError("Key can't be cloned");
}); });
it('throws if the key is not JSON-serializable', function() {
const spec = new privateUnderTest.Spec({
queueableFn: { fn: () => {} }
});
expect(function() {
const k = {};
k.self = k;
spec.setSpecProperty(k, '');
}).toThrowError("Key can't be cloned");
});
it('throws if the value is not structured-cloneable', function() { it('throws if the value is not structured-cloneable', function() {
const spec = new privateUnderTest.Spec({ const spec = new privateUnderTest.Spec({
queueableFn: { fn: () => {} } queueableFn: { fn: () => {} }
@@ -122,6 +134,18 @@ describe('Spec', function() {
spec.setSpecProperty('k', new Promise(() => {})); spec.setSpecProperty('k', new Promise(() => {}));
}).toThrowError("Value can't be cloned"); }).toThrowError("Value can't be cloned");
}); });
it('throws if the value is not JSON-serializable', function() {
const spec = new privateUnderTest.Spec({
queueableFn: { fn: () => {} }
});
expect(function() {
const v = {};
v.self = v;
spec.setSpecProperty('k', v);
}).toThrowError("Value can't be cloned");
});
}); });
describe('status', function() { describe('status', function() {

View File

@@ -74,8 +74,8 @@ getJasmineRequireObj().Spec = function(j$) {
// Key and value will eventually be cloned during reporting. The error // Key and value will eventually be cloned during reporting. The error
// thrown at that point if they aren't cloneable isn't very helpful. // thrown at that point if they aren't cloneable isn't very helpful.
// Throw a better one now. // Throw a better one now.
j$.private.util.assertStructuredCloneable(key, 'Key'); j$.private.util.assertReporterCloneable(key, 'Key');
j$.private.util.assertStructuredCloneable(value, 'Value'); j$.private.util.assertReporterCloneable(value, 'Value');
this.result.properties = this.result.properties || {}; this.result.properties = this.result.properties || {};
this.result.properties[key] = value; this.result.properties[key] = value;
} }

View File

@@ -34,8 +34,8 @@ getJasmineRequireObj().Suite = function(j$) {
// Key and value will eventually be cloned during reporting. The error // Key and value will eventually be cloned during reporting. The error
// thrown at that point if they aren't cloneable isn't very helpful. // thrown at that point if they aren't cloneable isn't very helpful.
// Throw a better one now. // Throw a better one now.
j$.private.util.assertStructuredCloneable(key, 'Key'); j$.private.util.assertReporterCloneable(key, 'Key');
j$.private.util.assertStructuredCloneable(value, 'Value'); j$.private.util.assertReporterCloneable(value, 'Value');
this.result.properties = this.result.properties || {}; this.result.properties = this.result.properties || {};
this.result.properties[key] = value; this.result.properties[key] = value;
} }

View File

@@ -80,9 +80,12 @@ getJasmineRequireObj().util = function(j$) {
} }
}; };
util.assertStructuredCloneable = function(v, msgPrefix) { util.assertReporterCloneable = function(v, msgPrefix) {
try { try {
structuredClone(v); // Reporter events are cloned internally via structuredClone, and it's
// common for reporters (including jasmine-browser-runner's) to JSON
// serialize them.
JSON.stringify(structuredClone(v));
} catch (e) { } catch (e) {
throw new Error(`${msgPrefix} can't be cloned`, { cause: e }); throw new Error(`${msgPrefix} can't be cloned`, { cause: e });
} }