blob: 5262d1f4d4ab196c91dc5ca59dcd30d0a3b97f14 [file] [log] [blame]
<!DOCTYPE HTML>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
function promise_test(func, name, properties) {
properties = properties || {};
var test = async_test(name, properties);
Promise.resolve(test.step(func, test, test))
.then(function() { test.done(); })
.catch(test.step_func(function(value) { throw value; }));
}
function structuredClone(o)
{
return new Promise(function(resolve, reject) {
var mc = new MessageChannel();
mc.port2.onmessage = function(e) { resolve(e.data); };
mc.port1.postMessage(o);
});
}
promise_test(function() {
var inner = {};
var orig = { inner: inner };
inner.outer = orig;
return structuredClone(orig).then(function(clone) {
assert_equals(clone.inner.outer, clone, 'Cycles should be preserved');
});
}, 'Verify: "This algorithm preserves cycles..."');
promise_test(function() {
var gen = {name: 'AES-CBC', length: 128};
return crypto.subtle.generateKey(gen, false, ['encrypt']).then(function(key) {
var simple = {};
var blob = new Blob(['content']);
var orig = {
s1: simple, s2: simple,
b1: blob, b2: blob,
k1: key, k2: key
};
return structuredClone(orig).then(function(clone) {
assert_equals(clone.s1, clone.s2, 'JS object identity should be preserved');
assert_equals(clone.b1, clone.b2, 'Core object identity should be preserved');
assert_equals(clone.k1, clone.k2, 'Module object identity should be preserved');
});
});
}, 'Verify: "This algorithm preserves... the identity of duplicate objects in graphs."');
promise_test(function() {
var name = 'this is a setter';
var orig = {};
orig[name] = 'value';
var setter_called = false;
Object.defineProperty(Object.prototype, name, {
set: function(a) {
setter_called = true;
}
});
assert_true(orig.hasOwnProperty(name));
return structuredClone(orig).then(function(clone) {
assert_equals(typeof clone, 'object', 'Clone should be an object');
assert_false(setter_called, 'Setter should not be called by cloning algorithm.');
assert_true(clone.hasOwnProperty(name), 'Cloning algorithm should add an own property.')
assert_equals(clone[name], orig[name], 'Property value should match');
});
}, 'Verify: "Add a new property..." (objects)');
promise_test(function() {
var name = '123456';
var orig = [];
orig[name] = 'value';
var setter_called = false;
Object.defineProperty(Object.prototype, name, {
set: function(a) {
setter_called = true;
}
});
assert_true(orig.hasOwnProperty(name));
return structuredClone(orig).then(function(clone) {
assert_true(Array.isArray(clone), 'Clone should be an Array');
assert_false(setter_called, 'Setter should not be called by cloning algorithm.');
assert_true(clone.hasOwnProperty(name), 'Cloning algorithm should add an own property.')
assert_equals(clone[name], orig[name], 'Property value should match');
});
}, 'Verify: "Add a new property..." (arrays)');
promise_test(function() {
var name = '256';
var orig = [];
for (var i = 0; i < 256; ++i) {
orig[i] = i;
}
orig[name] = 'value';
var setter_called = false;
Object.defineProperty(Object.prototype, name, {
set: function(a) {
setter_called = true;
}
});
assert_true(orig.hasOwnProperty(name));
return structuredClone(orig).then(function(clone) {
assert_true(Array.isArray(clone), 'Clone should be an Array');
assert_false(setter_called, 'Setter should not be called by cloning algorithm.');
assert_true(clone.hasOwnProperty(name), 'Cloning algorithm should add an own property.')
assert_equals(clone[name], orig[name], 'Property value should match');
});
}, 'Verify: "Add a new property..." (dense arrays)');
promise_test(function() {
var orig = {
emptySet: new Set,
set: new Set([1, 2, 3]),
emptyMap: new Map,
map: new Map([[1, 2], [3, 4]]),
};
return structuredClone(orig).then(function(clone) {
assert_true(clone.emptySet instanceof Set, 'Clone should be a Set');
assert_true(clone.emptyMap instanceof Map, 'Clone should be a Map');
assert_true(clone.set instanceof Set, 'Clone should be a Set');
assert_true(clone.map instanceof Map, 'Clone should be a Map');
assert_equals(clone.emptySet.size, 0, 'Clone should be the same size');
assert_equals(clone.emptyMap.size, 0, 'Clone should be the same size');
assert_equals(clone.set.size, orig.set.size, 'Clone should be the same size');
assert_equals(clone.map.size, orig.map.size, 'Clone should be the same size');
assert_true(clone.set.has(1) && clone.set.has(2) && clone.set.has(3), 'Cloned set should have the same keys');
assert_true(clone.map.get(1) == 2 && clone.map.get(3) == 4, 'Cloned map should have the same keys and values');
});
}, 'Maps and Sets are cloned');
promise_test(function() {
var set = new Set;
set.add(set);
var map = new Map;
map.set(map, map);
var orig = { map: map, set: set };
return structuredClone(orig).then(function(clone) {
assert_true(clone.set instanceof Set, 'Clone should be a Set');
assert_true(clone.map instanceof Map, 'Clone should be a Map');
assert_equals(clone.set, Array.from(clone.set)[0], 'Recursive sets should preserve identity');
assert_equals(clone.map, Array.from(clone.map)[0][0], 'Recursive maps should preserve identity');
assert_equals(clone.map, Array.from(clone.map)[0][1], 'Recursive maps should preserve identity');
});
}, 'Cloning Maps and Sets preserve cycles');
promise_test(function() {
var set = new Set;
var map = new Map;
var setMutator = {
get val() {
set.add('mutated');
return 'setMutator';
}
}
var mapMutator = {
get val() {
map.set('mutated', true);
return 'mapMutator';
}
}
set.add(setMutator);
map.set('mapMutator', mapMutator);
var orig = { map: map, set: set };
return structuredClone(orig).then(function(clone) {
assert_true(clone.set instanceof Set, 'Clone should be a Set');
assert_true(clone.map instanceof Map, 'Clone should be a Map');
assert_equals(orig.set.size, 2, 'Original set should have been mutated');
assert_equals(orig.map.size, 2, 'Original map should have been mutated');
assert_equals(clone.set.size, 1, 'Cloned set should not reflect mutation');
assert_equals(clone.map.size, 1, 'Cloned map should not reflect mutation');
assert_equals(Array.from(clone.set)[0].val, 'setMutator', 'Cloned set should contain getter return value');
assert_equals(clone.map.get('mapMutator').val, 'mapMutator', 'Cloned map should contain getter return value');
});
}, 'Cloned Maps and Sets do not reflect mutations that occur during cloning');
promise_test(function() {
var map = new Map([['key', function(){}]]);
return structuredClone(map).then(function(clone) {
assert_unreached('Should have thrown an exception');
}, function(ex) {
assert_true(ex instanceof DOMException, 'Should throw a DOMException');
assert_equals(ex.code, DOMException.DATA_CLONE_ERR, 'Should be a DataCloneError');
});
}, 'Cloning Maps should fail if they contain non-cloneable things');
promise_test(function() {
var set = new Set([function(){}]);
return structuredClone(set).then(function(clone) {
assert_unreached('Should have thrown an exception');
}, function(ex) {
assert_true(ex instanceof DOMException, 'Should throw a DOMException');
assert_equals(ex.code, DOMException.DATA_CLONE_ERR, 'Should be a DataCloneError');
});
}, 'Cloning Sets should fail if they contain non-cloneable things');
promise_test(function() {
return structuredClone(Symbol('foo')).then(function(clone) {
assert_unreached('Should have thrown an exception');
}, function(ex) {
assert_true(ex instanceof DOMException, 'Should throw a DOMException');
assert_equals(ex.code, DOMException.DATA_CLONE_ERR, 'Should be a DataCloneError');
});
}, 'Cloning Symbols should fail');
</script>