diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index e55f611f..3c046ce9 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -769,6 +769,14 @@ getJasmineRequireObj().util = function(j$) { } }; + util.assertStructuredCloneable = function(v, msgPrefix) { + try { + structuredClone(v); + } catch (e) { + throw new Error(`${msgPrefix} can't be cloned`, { cause: e }); + } + }; + return util; }; @@ -844,8 +852,12 @@ getJasmineRequireObj().Spec = function(j$) { return this.result.properties[key]; } - // TODO: throw if the key or value is not structred cloneable setSpecProperty(key, value) { + // Key and value will eventually be cloned during reporting. The error + // thrown at that point if they aren't cloneable isn't very helpful. + // Throw a better one now. + j$.util.assertStructuredCloneable(key, 'Key'); + j$.util.assertStructuredCloneable(value, 'Value'); this.result.properties = this.result.properties || {}; this.result.properties[key] = value; } @@ -10604,6 +10616,11 @@ getJasmineRequireObj().Suite = function(j$) { } setSuiteProperty(key, value) { + // Key and value will eventually be cloned during reporting. The error + // thrown at that point if they aren't cloneable isn't very helpful. + // Throw a better one now. + j$.util.assertStructuredCloneable(key, 'Key'); + j$.util.assertStructuredCloneable(value, 'Value'); this.result.properties = this.result.properties || {}; this.result.properties[key] = value; } diff --git a/spec/core/SpecSpec.js b/spec/core/SpecSpec.js index 5d727729..18769efa 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -102,6 +102,26 @@ describe('Spec', function() { b: 'original-value' }); }); + + it('throws if the key is not structured-cloneable', function() { + const spec = new jasmineUnderTest.Spec({ + queueableFn: { fn: () => {} } + }); + + expect(function() { + spec.setSpecProperty(new Promise(() => {}), ''); + }).toThrowError("Key can't be cloned"); + }); + + it('throws if the value is not structured-cloneable', function() { + const spec = new jasmineUnderTest.Spec({ + queueableFn: { fn: () => {} } + }); + + expect(function() { + spec.setSpecProperty('k', new Promise(() => {})); + }).toThrowError("Value can't be cloned"); + }); }); describe('status', function() { diff --git a/spec/core/SuiteSpec.js b/spec/core/SuiteSpec.js index af2e4d64..e3961193 100644 --- a/spec/core/SuiteSpec.js +++ b/spec/core/SuiteSpec.js @@ -405,4 +405,22 @@ describe('Suite', function() { expect(subject.hasChildWithDescription('a spec')).toBeFalse(); }); }); + + describe('#setSuiteProperty', function() { + it('throws if the key is not structured-cloneable', function() { + const suite = new jasmineUnderTest.Suite({}); + + expect(function() { + suite.setSuiteProperty(new Promise(() => {}), ''); + }).toThrowError("Key can't be cloned"); + }); + + it('throws if the value is not structured-cloneable', function() { + const suite = new jasmineUnderTest.Suite({}); + + expect(function() { + suite.setSuiteProperty('k', new Promise(() => {})); + }).toThrowError("Value can't be cloned"); + }); + }); }); diff --git a/src/core/Spec.js b/src/core/Spec.js index 05383d93..56d572bf 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -70,8 +70,12 @@ getJasmineRequireObj().Spec = function(j$) { return this.result.properties[key]; } - // TODO: throw if the key or value is not structred cloneable setSpecProperty(key, value) { + // Key and value will eventually be cloned during reporting. The error + // thrown at that point if they aren't cloneable isn't very helpful. + // Throw a better one now. + j$.util.assertStructuredCloneable(key, 'Key'); + j$.util.assertStructuredCloneable(value, 'Value'); this.result.properties = this.result.properties || {}; this.result.properties[key] = value; } diff --git a/src/core/Suite.js b/src/core/Suite.js index 58e226f4..4a75565f 100644 --- a/src/core/Suite.js +++ b/src/core/Suite.js @@ -31,6 +31,11 @@ getJasmineRequireObj().Suite = function(j$) { } setSuiteProperty(key, value) { + // Key and value will eventually be cloned during reporting. The error + // thrown at that point if they aren't cloneable isn't very helpful. + // Throw a better one now. + j$.util.assertStructuredCloneable(key, 'Key'); + j$.util.assertStructuredCloneable(value, 'Value'); this.result.properties = this.result.properties || {}; this.result.properties[key] = value; } diff --git a/src/core/util.js b/src/core/util.js index e40d6a0e..f824df1d 100644 --- a/src/core/util.js +++ b/src/core/util.js @@ -80,5 +80,13 @@ getJasmineRequireObj().util = function(j$) { } }; + util.assertStructuredCloneable = function(v, msgPrefix) { + try { + structuredClone(v); + } catch (e) { + throw new Error(`${msgPrefix} can't be cloned`, { cause: e }); + } + }; + return util; };