diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 9a614a5d..d890cc0b 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -761,9 +761,12 @@ getJasmineRequireObj().util = function(j$) { } }; - util.assertStructuredCloneable = function(v, msgPrefix) { + util.assertReporterCloneable = function(v, msgPrefix) { 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) { 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 // thrown at that point if they aren't cloneable isn't very helpful. // Throw a better one now. - j$.private.util.assertStructuredCloneable(key, 'Key'); - j$.private.util.assertStructuredCloneable(value, 'Value'); + j$.private.util.assertReporterCloneable(key, 'Key'); + j$.private.util.assertReporterCloneable(value, 'Value'); this.result.properties = this.result.properties || {}; this.result.properties[key] = value; } @@ -10631,8 +10634,8 @@ getJasmineRequireObj().Suite = function(j$) { // 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$.private.util.assertStructuredCloneable(key, 'Key'); - j$.private.util.assertStructuredCloneable(value, 'Value'); + j$.private.util.assertReporterCloneable(key, 'Key'); + j$.private.util.assertReporterCloneable(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 13bcddf7..12370d6d 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -113,6 +113,18 @@ describe('Spec', function() { }).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() { const spec = new privateUnderTest.Spec({ queueableFn: { fn: () => {} } @@ -122,6 +134,18 @@ describe('Spec', function() { spec.setSpecProperty('k', new Promise(() => {})); }).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() { diff --git a/src/core/Spec.js b/src/core/Spec.js index 2f3202d8..1a990cc9 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -74,8 +74,8 @@ getJasmineRequireObj().Spec = function(j$) { // 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$.private.util.assertStructuredCloneable(key, 'Key'); - j$.private.util.assertStructuredCloneable(value, 'Value'); + j$.private.util.assertReporterCloneable(key, 'Key'); + j$.private.util.assertReporterCloneable(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 62a91216..96eabe2c 100644 --- a/src/core/Suite.js +++ b/src/core/Suite.js @@ -34,8 +34,8 @@ getJasmineRequireObj().Suite = function(j$) { // 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$.private.util.assertStructuredCloneable(key, 'Key'); - j$.private.util.assertStructuredCloneable(value, 'Value'); + j$.private.util.assertReporterCloneable(key, 'Key'); + j$.private.util.assertReporterCloneable(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 e0e9edbd..52ef4a74 100644 --- a/src/core/util.js +++ b/src/core/util.js @@ -80,9 +80,12 @@ getJasmineRequireObj().util = function(j$) { } }; - util.assertStructuredCloneable = function(v, msgPrefix) { + util.assertReporterCloneable = function(v, msgPrefix) { 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) { throw new Error(`${msgPrefix} can't be cloned`, { cause: e }); }