|
|
@@ -5,33 +5,34 @@
|
|
|
|
|
|
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
|
|
|
|
|
|
-function areEqual(a,b)
|
|
|
+function areEqual(a, b, msg)
|
|
|
{
|
|
|
- assert.areEqual(a.length,b.length);
|
|
|
+ assert.areEqual(a.length, b.length, msg);
|
|
|
for(var i = 0;i < a.length; i++)
|
|
|
{
|
|
|
- assert.areEqual(a[i],b[i]);
|
|
|
+ assert.areEqual(a[i], b[i], msg);
|
|
|
}
|
|
|
}
|
|
|
-function compareConcats(a,b)
|
|
|
+function compareConcats(a, b)
|
|
|
{
|
|
|
var c = a.concat(b);
|
|
|
b[Symbol.isConcatSpreadable] = false;
|
|
|
var d = a.concat(b);
|
|
|
- assert.areEqual(b,d[a.length],"Indexing d at a.length should return b");
|
|
|
+ assert.areEqual(b, d[a.length], "Indexing d at a.length should return b");
|
|
|
for(var i = 0;i < a.length; i++)
|
|
|
{
|
|
|
- assert.areEqual(a[i],c[i],"confirm array a makes up the first half of array c");
|
|
|
- assert.areEqual(a[i],d[i],"confirm array a makes up the first half of array d");
|
|
|
+ assert.areEqual(a[i], c[i], "confirm array a makes up the first half of array c");
|
|
|
+ assert.areEqual(a[i], d[i], "confirm array a makes up the first half of array d");
|
|
|
}
|
|
|
for(var i = 0;i < b.length; i++)
|
|
|
{
|
|
|
- assert.areEqual(b[i],c[a.length+i],"confirm array b makes up the second half of array c");
|
|
|
- assert.areEqual(b[i],d[a.length][i],"confirm array b makes up a sub array at d[a.length]");
|
|
|
+ assert.areEqual(b[i], c[a.length+i], "confirm array b makes up the second half of array c");
|
|
|
+ assert.areEqual(b[i], d[a.length][i], "confirm array b makes up a sub array at d[a.length]");
|
|
|
|
|
|
}
|
|
|
- assert.areEqual(a.length+1,d.length,"since we are not flattening the top level array grows only by 1");
|
|
|
- excludeLengthCheck = false;
|
|
|
+
|
|
|
+ assert.areEqual(a.length+1, d.length, "since we are not flattening the top level array grows only by 1");
|
|
|
+ excludeLengthCheck = false;
|
|
|
|
|
|
delete b[Symbol.isConcatSpreadable];
|
|
|
}
|
|
|
@@ -40,80 +41,80 @@ var tests = [
|
|
|
name: "nativeInt Arrays",
|
|
|
body: function ()
|
|
|
{
|
|
|
- var a = [1,2,3];
|
|
|
- var b = [4,5,6];
|
|
|
- compareConcats(a,b);
|
|
|
+ var a = [1, 2, 3];
|
|
|
+ var b = [4, 5, 6];
|
|
|
+ compareConcats(a, b);
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
name: "nativefloat Arrays",
|
|
|
body: function ()
|
|
|
{
|
|
|
- var a = [1.1,2.2,3.3];
|
|
|
- var b = [4.4,5.5,6.6];
|
|
|
- compareConcats(a,b);
|
|
|
+ var a = [1.1, 2.2, 3.3];
|
|
|
+ var b = [4.4, 5.5, 6.6];
|
|
|
+ compareConcats(a, b);
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
name: "Var Arrays",
|
|
|
body: function ()
|
|
|
{
|
|
|
- var a = ["a","b","c"];
|
|
|
- var b = ["d","e","f"];
|
|
|
- compareConcats(a,b);
|
|
|
+ var a = ["a", "b", "c"];
|
|
|
+ var b = ["d", "e", "f"];
|
|
|
+ compareConcats(a, b);
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
name: "intermixed Arrays",
|
|
|
body: function ()
|
|
|
{
|
|
|
- var a = [1.1,"b",3];
|
|
|
- var b = [4,5.5,"f"];
|
|
|
- compareConcats(a,b);
|
|
|
+ var a = [1.1, "b", 3];
|
|
|
+ var b = [4, 5.5, "f"];
|
|
|
+ compareConcats(a, b);
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
name: "concating spreadable objects",
|
|
|
body: function ()
|
|
|
{
|
|
|
- var a = [1,2,3,4,5,6,7,8,9,10];
|
|
|
- var b = [1,2,3].concat(4, 5, { [Symbol.isConcatSpreadable]: true, 0: 6, 1: 7, 2: 8, "length" : 3 }, 9, 10);
|
|
|
- areEqual(a,b);
|
|
|
+ var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
|
+ var b = [1, 2, 3].concat(4, 5, { [Symbol.isConcatSpreadable]: true, 0: 6, 1: 7, 2: 8, "length" : 3 }, 9, 10);
|
|
|
+ areEqual(a, b);
|
|
|
// Test to confirm we Spread to nothing when length is not set
|
|
|
- var a = [1,2,3,4,5,9,10];
|
|
|
- var b = [1,2,3].concat(4, 5, { [Symbol.isConcatSpreadable]: true, 0: 6, 1: 7, 2: 8}, 9, 10);
|
|
|
- areEqual(a,b);
|
|
|
+ var a = [1, 2, 3, 4, 5, 9, 10];
|
|
|
+ var b = [1, 2, 3].concat(4, 5, { [Symbol.isConcatSpreadable]: true, 0: 6, 1: 7, 2: 8}, 9, 10);
|
|
|
+ areEqual(a, b);
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
name: " concat with non-arrays",
|
|
|
body: function ()
|
|
|
{
|
|
|
- var a = [1.1,2.1,3.1];
|
|
|
+ var a = [1.1, 2.1, 3.1];
|
|
|
var b = 4.1;
|
|
|
- compareConcats(a,b);
|
|
|
- var a = [1,2,3];
|
|
|
+ compareConcats(a, b);
|
|
|
+ var a = [1, 2, 3];
|
|
|
var b = 4;
|
|
|
- compareConcats(a,b);
|
|
|
- var a = [1,2,3];
|
|
|
+ compareConcats(a, b);
|
|
|
+ var a = [1, 2, 3];
|
|
|
var b = "b";
|
|
|
- compareConcats(a,b);
|
|
|
- var a = [1,2,3];
|
|
|
+ compareConcats(a, b);
|
|
|
+ var a = [1, 2, 3];
|
|
|
var b = true;
|
|
|
- compareConcats(a,b);
|
|
|
+ compareConcats(a, b);
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
name: "override with constructors",
|
|
|
body: function ()
|
|
|
{
|
|
|
- var a = [1,2,3];
|
|
|
- var b = [4.4,5.5,6.6];
|
|
|
+ var a = [1, 2, 3];
|
|
|
+ var b = [4.4, 5.5, 6.6];
|
|
|
var c = [Object, {}, undefined, Math.sin];
|
|
|
for(var i = 0; i < c.length;i++)
|
|
|
{
|
|
|
a['constructor'] = c[i];
|
|
|
- compareConcats(a,b);
|
|
|
+ compareConcats(a, b);
|
|
|
}
|
|
|
var o = [];
|
|
|
o.constructor = function()
|
|
|
@@ -122,9 +123,560 @@ var tests = [
|
|
|
a[0] = 10;
|
|
|
return a;
|
|
|
}
|
|
|
- areEqual([1,2,3],o.concat([1,2,3]));
|
|
|
+ areEqual([1, 2, 3], o.concat([1, 2, 3]));
|
|
|
}
|
|
|
- }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "test ToBoolean on array[@@isConcatSpreadable]",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(x, y, z) {
|
|
|
+
|
|
|
+ var a = [x], b = [y, z];
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable] undefined');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = null;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), '[@@isConcatSpreadable]==null');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), '[@@isConcatSpreadable]==false');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = '';
|
|
|
+ areEqual([x, [y, z]], a.concat(b), '[@@isConcatSpreadable]==\'\'');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = 0;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), '[@@isConcatSpreadable]==0');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = +0.0;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), '[@@isConcatSpreadable]==+0.0');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = -0.0;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), '[@@isConcatSpreadable]==-0.0');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = NaN;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), '[@@isConcatSpreadable]==NaN');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = undefined;
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==undefined');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==true');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = 'abc';
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]=\'abc\'');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = 0.1;
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==0.1');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = -0.1;
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==-0.1');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = Symbol();
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==Symbol()');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = {};
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]=={}');
|
|
|
+
|
|
|
+ delete b[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable] deleted');
|
|
|
+ }
|
|
|
+
|
|
|
+ test(1, 2, 3);
|
|
|
+ test(1.1, 2.2, 3.3);
|
|
|
+ test("a", "b", "c");
|
|
|
+ test(1.1, "b", 3);
|
|
|
+ test(4, 5.5, "f");
|
|
|
+ test(undefined, NaN, function(){});
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "test ToBoolean on object[@@isConcatSpreadable]",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(x, y, z) {
|
|
|
+
|
|
|
+ var a = [x], b = {'0':y, '1':z, 'length':2};
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable] undefined');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = null;
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable]==null');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable]==false');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = '';
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable]==\'\'');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = 0;
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable]==0');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = +0.0;
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable]==+0.0');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = -0.0;
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable]==-0.0');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = NaN;
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable]==NaN');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = undefined;
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable]==undefined');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==true');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = 'abc';
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==\'abc\'');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = 0.1;
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==0.1');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = -0.1;
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==-0.1');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = Symbol();
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]==Symbol()');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = {};
|
|
|
+ areEqual([x, y, z], a.concat(b), '[@@isConcatSpreadable]=={}');
|
|
|
+
|
|
|
+ delete b[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([x, b], a.concat(b), '[@@isConcatSpreadable] deleted');
|
|
|
+ }
|
|
|
+
|
|
|
+ test(1, 2, 3);
|
|
|
+ test(1.1, 2.2, 3.3);
|
|
|
+ test("a", "b", "c");
|
|
|
+ test(1.1, "b", 3);
|
|
|
+ test(4, 5.5, "f");
|
|
|
+ test(undefined, NaN, function(){});
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "two arrays that may share the same type",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(x, y, z) {
|
|
|
+ var a = [x], b = [y, z], c = [y, z];
|
|
|
+ areEqual([x, y, z], a.concat(b), 'b[@@isConcatSpreadable] undefined');
|
|
|
+ areEqual([x, y, z], a.concat(c), 'c[@@isConcatSpreadable] undefined');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), 'b[@@isConcatSpreadable]==false');
|
|
|
+ areEqual([x, y, z], a.concat(c), 'c[@@isConcatSpreadable] undefined');
|
|
|
+
|
|
|
+ c[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), 'b[@@isConcatSpreadable]==false');
|
|
|
+ areEqual([x, [y, z]], a.concat(c), 'c[@@isConcatSpreadable]==false');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([x, y, z], a.concat(b), 'b[@@isConcatSpreadable]==true');
|
|
|
+ areEqual([x, [y, z]], a.concat(c), 'c[@@isConcatSpreadable]==false');
|
|
|
+
|
|
|
+ c[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([x, y, z], a.concat(b), 'b[@@isConcatSpreadable]==true');
|
|
|
+ areEqual([x, y, z], a.concat(c), 'c[@@isConcatSpreadable]==true');
|
|
|
+
|
|
|
+ c[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([x, y, z], a.concat(b), 'b[@@isConcatSpreadable]==true');
|
|
|
+ areEqual([x, [y, z]], a.concat(c), 'c[@@isConcatSpreadable]==false');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([x, [y, z]], a.concat(b), 'b[@@isConcatSpreadable]==false');
|
|
|
+ areEqual([x, [y, z]], a.concat(c), 'c[@@isConcatSpreadable]==false');
|
|
|
+
|
|
|
+ b[Symbol.isConcatSpreadable] = undefined;
|
|
|
+ areEqual([x, y, z], a.concat(b), 'b[@@isConcatSpreadable]==undefined');
|
|
|
+ areEqual([x, [y, z]], a.concat(c), 'c[@@isConcatSpreadable]==false');
|
|
|
+
|
|
|
+ delete b[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([x, y, z], a.concat(b), 'b[@@isConcatSpreadable] deleted');
|
|
|
+ areEqual([x, [y, z]], a.concat(c), 'c[@@isConcatSpreadable]==false');
|
|
|
+
|
|
|
+ delete c[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([x, y, z], a.concat(b), 'b[@@isConcatSpreadable] deleted');
|
|
|
+ areEqual([x, y, z], a.concat(c), 'c[@@isConcatSpreadable] deleted');
|
|
|
+ }
|
|
|
+
|
|
|
+ test(1, 2, 3);
|
|
|
+ test(1.1, 2.2, 3.3);
|
|
|
+ test("a", "b", "c");
|
|
|
+ test(1.1, "b", 3);
|
|
|
+ test(4, 5.5, "f");
|
|
|
+ test(undefined, NaN, function(){});
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "user-defined length",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(a, b, c) {
|
|
|
+ var args = (function() { return arguments; })(a, b, c);
|
|
|
+ args[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([a, b, c, a, b, c], [].concat(args, args), '['+a+', '+b+', '+c+', '+a+', '+b+', '+c+']');
|
|
|
+ Object.defineProperty(args, "length", { value: 6 });
|
|
|
+ areEqual([a, b, c, undefined, undefined, undefined], [].concat(args), '['+a+', '+b+', '+c+', undefined, undefined, undefined]');
|
|
|
+ }
|
|
|
+
|
|
|
+ test(1, 2, 3);
|
|
|
+ test(1.1, 2.2, 3.3);
|
|
|
+ test("a", "b", "c");
|
|
|
+ test(1.1, "b", 3);
|
|
|
+ test(4, 5.5, "f");
|
|
|
+ test(undefined, NaN, function(){});
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "concat a mix of user-constructed objects and arrays",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ class MyObj extends Object {}
|
|
|
+ var a = new MyObj;
|
|
|
+ a.length = 5;
|
|
|
+ a[0] = 'a';
|
|
|
+ a[2] = 'b';
|
|
|
+ a[4] = 'c';
|
|
|
+
|
|
|
+ var b = { length: 3, "0": "a", "1": "b", "2": "c" };
|
|
|
+
|
|
|
+ class MyArray extends Array {}
|
|
|
+ var c = new MyArray("a", "b", "c");
|
|
|
+ var d = ['e', 'f', 'g'];
|
|
|
+
|
|
|
+ a[Symbol.isConcatSpreadable] = true;
|
|
|
+ d[Symbol.isConcatSpreadable] = false;
|
|
|
+
|
|
|
+ areEqual(['a', undefined, 'b', undefined, 'c', b, 'a', 'b', 'c', ['e', 'f', 'g']], Array.prototype.concat.call(a, b, c, d));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "verify ToLength operation",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ var obj = {"length": {valueOf: null, toString: null}};
|
|
|
+ obj[Symbol.isConcatSpreadable] = true;
|
|
|
+ assert.throws(()=>Array.prototype.concat.call(obj), TypeError, '{valueOf: null, toString: null}', "Number expected");
|
|
|
+
|
|
|
+ obj = {"length": {toString: function() { throw new Error('User-defined error in toString'); }, valueOf: null}};
|
|
|
+ obj[Symbol.isConcatSpreadable] = true;
|
|
|
+ assert.throws(()=>Array.prototype.concat.call(obj), Error, 'toString() throws', "User-defined error in toString");
|
|
|
+
|
|
|
+ obj = {"length": {toString: function() { return 'string'; }, valueOf: null}};
|
|
|
+ obj[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([], [].concat(obj), ' toString() returns string');
|
|
|
+
|
|
|
+ obj = {"length": {valueOf: function() { throw new Error('User-defined error in valueOf'); }, toString: null}};
|
|
|
+ obj[Symbol.isConcatSpreadable] = true;
|
|
|
+ assert.throws(()=>Array.prototype.concat.call(obj), Error, 'valueOf() throws', "User-defined error in valueOf");
|
|
|
+
|
|
|
+ obj = {"length": {valueOf: function() { return 'string'; }, toString: null}};
|
|
|
+ obj[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([], [].concat(obj), 'toString() returns string');
|
|
|
+
|
|
|
+ obj = { "length": -4294967294, "0": "a", "2": "b", "4": "c" };
|
|
|
+ obj[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([], [].concat(obj), 'ToLength clamps negative to zero');
|
|
|
+
|
|
|
+ obj.length = -0.0;
|
|
|
+ areEqual([], [].concat(obj), 'ToLength clamps negative to zero');
|
|
|
+
|
|
|
+ obj.length = "-4294967294";
|
|
|
+ areEqual([], [].concat(obj), 'ToLength clamps negative to zero');
|
|
|
+
|
|
|
+ obj.length = "-0.0";
|
|
|
+ areEqual([], [].concat(obj), 'ToLength clamps negative to zero');
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Getter of [@@isConcatSpreadable] throws",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ var obj = {};
|
|
|
+ Object.defineProperty(obj, Symbol.isConcatSpreadable, {
|
|
|
+ get: function() { throw Error('User-defined error in @@isConcatSpreadable getter'); }
|
|
|
+ });
|
|
|
+ assert.throws(()=>[].concat(obj), Error, '[].concat(obj)', "User-defined error in @@isConcatSpreadable getter");
|
|
|
+ assert.throws(()=>Array.prototype.concat.call(obj), Error, 'Array.prototype.concat.call(obj)', "User-defined error in @@isConcatSpreadable getter");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "spread Function object",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(arr) {
|
|
|
+ var func = function(x, y, z){};
|
|
|
+ areEqual([func], [].concat(func), 'Function object');
|
|
|
+
|
|
|
+ func[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([undefined, undefined, undefined], [].concat(func), 'func[Symbol.isConcatSpreadable] == true');
|
|
|
+
|
|
|
+ func[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([func], [].concat(func), 'func[Symbol.isConcatSpreadable] == false');
|
|
|
+
|
|
|
+ func[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([undefined, undefined, undefined], [].concat(func), 'func[Symbol.isConcatSpreadable] == true');
|
|
|
+
|
|
|
+ func[0] = arr[0];
|
|
|
+ func[1] = arr[1];
|
|
|
+ func[2] = arr[2];
|
|
|
+ areEqual(arr, [].concat(func), 'func[0..2] assigned');
|
|
|
+
|
|
|
+ delete func[0];
|
|
|
+ delete func[1];
|
|
|
+ delete func[2];
|
|
|
+ areEqual([undefined, undefined, undefined], [].concat(func), 'func[0..2] deleted');
|
|
|
+
|
|
|
+ delete func[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([func], [].concat(func), 'func[Symbol.isConcatSpreadable] deleted');
|
|
|
+
|
|
|
+ Function.prototype[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([undefined, undefined, undefined], [].concat(func), 'Function.prototype[Symbol.isConcatSpreadable] == true');
|
|
|
+
|
|
|
+ Function.prototype[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([func], [].concat(func), 'Function.prototype[Symbol.isConcatSpreadable] == false');
|
|
|
+
|
|
|
+ Function.prototype[0] = arr[0];
|
|
|
+ Function.prototype[1] = arr[1];
|
|
|
+ Function.prototype[2] = arr[2];
|
|
|
+ areEqual([func], [].concat(func), 'Function.prototype[0..2] assigned');
|
|
|
+
|
|
|
+ Function.prototype[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual(arr, [].concat(func), 'Function.prototype[Symbol.isConcatSpreadable] == true');
|
|
|
+
|
|
|
+ delete Function.prototype[0];
|
|
|
+ delete Function.prototype[1];
|
|
|
+ delete Function.prototype[2];
|
|
|
+ areEqual([undefined, undefined, undefined], [].concat(func), 'Function.prototype[0..2] deleted');
|
|
|
+
|
|
|
+ delete Function.prototype[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([func], [].concat(func), 'Function.prototype[Symbol.isConcatSpreadable] deleted');
|
|
|
+ }
|
|
|
+
|
|
|
+ test([1, 2, 3]);
|
|
|
+ test([1.1, 2.2, 3.3]);
|
|
|
+ test(["a", "b", "c"]);
|
|
|
+ test([2, NaN, function(){}]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "spread Number, Boolean, and RegExp",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(Type, initVal, arr) {
|
|
|
+ var obj = new Type(initVal);
|
|
|
+ areEqual([obj], [].concat(obj), Type.name+' obj');
|
|
|
+
|
|
|
+ obj[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([], [].concat(obj), Type.name+' obj[Symbol.isConcatSpreadable] == true');
|
|
|
+
|
|
|
+ obj.length = arr.length;
|
|
|
+ areEqual(new Array(arr.length), [].concat(obj), Type.name+' obj[length] assigned');
|
|
|
+
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ obj[i] = arr[i];
|
|
|
+ }
|
|
|
+ areEqual(arr, [].concat(obj), Type.name+' obj[0..length] assigned');
|
|
|
+
|
|
|
+ obj[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([obj], [].concat(obj), Type.name+' obj[Symbol.isConcatSpreadable] == false');
|
|
|
+
|
|
|
+ obj[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual(arr, [].concat(obj), Type.name+' obj[Symbol.isConcatSpreadable] == true');
|
|
|
+
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ delete obj[i];
|
|
|
+ }
|
|
|
+ areEqual(new Array(arr.length), [].concat(obj), Type.name+' obj[0..length] deleted');
|
|
|
+
|
|
|
+ delete obj.length;
|
|
|
+ areEqual([], [].concat(obj), Type.name+' obj[length] deleted');
|
|
|
+
|
|
|
+ delete obj[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([obj], [].concat(obj), Type.name+' obj[Symbol.isConcatSpreadable] deleted');
|
|
|
+
|
|
|
+ Type.prototype[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual([], [].concat(obj), Type.name+'.prototype[Symbol.isConcatSpreadable] == true');
|
|
|
+
|
|
|
+ Type.prototype.length = arr.length;
|
|
|
+ areEqual(new Array(arr.length), [].concat(obj), Type.name+'.prototype[length] assigned');
|
|
|
+
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ Type.prototype[i] = arr[i];
|
|
|
+ }
|
|
|
+ areEqual(arr, [].concat(obj), Type.name+'.prototype[0..length] assigned');
|
|
|
+
|
|
|
+ Type.prototype[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([obj], [].concat(obj), Type.name+'.prototype[Symbol.isConcatSpreadable] == false');
|
|
|
+
|
|
|
+ Type.prototype[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual(arr, [].concat(obj), Type.name+'.prototype[Symbol.isConcatSpreadable] == true');
|
|
|
+
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ delete Type.prototype[i];
|
|
|
+ }
|
|
|
+ areEqual(new Array(arr.length), [].concat(obj), Type.name+'.prototype[0..length] deleted');
|
|
|
+
|
|
|
+ delete Type.prototype.length;
|
|
|
+ areEqual([], [].concat(obj), Type.name+'.prototype[length] deleted');
|
|
|
+
|
|
|
+ delete Type.prototype[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([obj], [].concat(obj), Type.name+'.prototype[Symbol.isConcatSpreadable] deleted');
|
|
|
+ }
|
|
|
+
|
|
|
+ test(Number, 0, [1, 2, 3]);
|
|
|
+ test(Number, -0.1, [1.1, 2.2, 3.3]);
|
|
|
+ test(Number, NaN, ["a", "b", "c"]);
|
|
|
+ test(Number, 321, [1, "ab", 2.2, 2, NaN, 3, function(){ }]);
|
|
|
+
|
|
|
+ test(Boolean, true, [1, 2, 3]);
|
|
|
+ test(Boolean, false, [1.1, 2.2, 3.3]);
|
|
|
+ test(Boolean, true, ["a", "b", "c"]);
|
|
|
+ test(Boolean, false, [1, "ab", 2.2, 2, NaN, 3, function(){ }]);
|
|
|
+
|
|
|
+ test(RegExp, /^/, [1, 2, 3]);
|
|
|
+ test(RegExp, /abc/, [1.1, 2.2, 3.3]);
|
|
|
+ test(RegExp, /(\d+)/, ["a", "b", "c"]);
|
|
|
+ test(RegExp, /^\s*\S+\s*$/, [1, "ab", 2.2, 2, NaN, 3, function(){ }]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "spread String object",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test() {
|
|
|
+ var s = new String("abc");
|
|
|
+ areEqual([s], [].concat(s), "string");
|
|
|
+
|
|
|
+ s[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual(['a', 'b', 'c'], [].concat(s), "string s[Symbol.isConcatSpreadable] == true");
|
|
|
+
|
|
|
+ s[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([s], [].concat(s), "string s[Symbol.isConcatSpreadable] == false");
|
|
|
+
|
|
|
+ s[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual(['a', 'b', 'c'], [].concat(s), "string s[Symbol.isConcatSpreadable] == true");
|
|
|
+
|
|
|
+ delete s[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([s], [].concat(s), "string s[Symbol.isConcatSpreadable] deleted");
|
|
|
+
|
|
|
+ String.prototype[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual(['a', 'b', 'c'], [].concat(s), "string.prototype[Symbol.isConcatSpreadable] == true");
|
|
|
+
|
|
|
+ String.prototype[Symbol.isConcatSpreadable] = false;
|
|
|
+ areEqual([s], [].concat(s), "string.prototype[Symbol.isConcatSpreadable] == false");
|
|
|
+
|
|
|
+ String.prototype[Symbol.isConcatSpreadable] = true;
|
|
|
+ areEqual(['a', 'b', 'c'], [].concat(s), "string.prototype[Symbol.isConcatSpreadable] == true");
|
|
|
+
|
|
|
+ delete String.prototype[Symbol.isConcatSpreadable];
|
|
|
+ areEqual([s], [].concat(s), "string.prototype[Symbol.isConcatSpreadable] deleted");
|
|
|
+ }
|
|
|
+
|
|
|
+ test();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Revokable proxy revoked when retrieving [@@isConcatSpreadable]",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ // proxy revoked
|
|
|
+ var obj = {};
|
|
|
+ var pobj = Proxy.revocable(obj, {
|
|
|
+ get: function(target, prop) {
|
|
|
+ if (prop === Symbol.isConcatSpreadable) { pobj.revoke(); }
|
|
|
+ return obj[prop];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ assert.throws(()=>[].concat(pobj.proxy), TypeError, 'proxy revoked', 'method is called on a revoked Proxy object');
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "[@@isConcatSpreadable] getter altering array type",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(arr, idx, elem) {
|
|
|
+ var expected = arr.slice(0);
|
|
|
+ expected[idx] = elem;
|
|
|
+ Object.defineProperty(arr, Symbol.isConcatSpreadable, {
|
|
|
+ get: function() {
|
|
|
+ arr[idx] = elem;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ areEqual(expected, [].concat(arr), 'expecting ['+expected+']');
|
|
|
+ }
|
|
|
+
|
|
|
+ test([1, 2, 3], 1, 'abc');
|
|
|
+ test([1.1, 2.2, 3.3], 0, {});
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "[@@isConcatSpreadable] getter altering binding",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(arr, expected) {
|
|
|
+ Object.defineProperty(arr, Symbol.isConcatSpreadable, {
|
|
|
+ get: function() {
|
|
|
+ arr = [];
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ areEqual(expected, Array.prototype.concat.call(arr, arr), 'expecting ['+expected+']');
|
|
|
+ areEqual([], Array.prototype.concat.call(arr, arr), 'expecting []');
|
|
|
+ }
|
|
|
+
|
|
|
+ test([1, 2, 3], [1, 2, 3, 1, 2, 3]);
|
|
|
+ test([1.1, 2.2, 3.3], [1.1, 2.2, 3.3, 1.1, 2.2, 3.3]);
|
|
|
+ test(["a", "b", "c"], ["a", "b", "c", "a", "b", "c"]);
|
|
|
+ test([1.1, "b", 3], [1.1, "b", 3, 1.1, "b", 3]);
|
|
|
+ test([4, 5.5, "f"], [4, 5.5, "f", 4, 5.5, "f"]);
|
|
|
+ var func = function() {};
|
|
|
+ test([undefined, NaN, func], [undefined, NaN, func, undefined, NaN, func]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "[@@isConcatSpreadable] getter changing array to ES5 array",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(arr, idx, elem) {
|
|
|
+ var expected = arr.slice(0);
|
|
|
+ expected[idx] = elem;
|
|
|
+ Object.defineProperty(arr, Symbol.isConcatSpreadable, {
|
|
|
+ get: function() {
|
|
|
+ Object.defineProperty(arr, idx, { 'get': function(){ return elem; } });
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ areEqual(expected, Array.prototype.concat.call(arr), 'expecting ['+arr+']');
|
|
|
+ }
|
|
|
+
|
|
|
+ test([1, 2, 3], 1, 'abc');
|
|
|
+ test([1.1, 2.2, 3.3], 0, {});
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "[@@isConcatSpreadable] getter setting illegal length property in object",
|
|
|
+ body: function ()
|
|
|
+ {
|
|
|
+ function test(a) {
|
|
|
+ var b = {"0":1, "1":2, "length": 2};
|
|
|
+ Object.defineProperty(b, Symbol.isConcatSpreadable, {
|
|
|
+ get: function() {
|
|
|
+ b.length = 9007199254740989;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ assert.throws(()=>a.concat(b), TypeError, a, "Illegal length and size specified for the array");
|
|
|
+ }
|
|
|
+
|
|
|
+ test([1, 2, 3]);
|
|
|
+ test([1.1, 2.2, 3.3]);
|
|
|
+ test(["a", "b", "c"]);
|
|
|
+ test([1.1, "b", 3]);
|
|
|
+ test([4, 5.5, "f"]);
|
|
|
+ test([undefined, NaN, function(){}]);
|
|
|
+ }
|
|
|
+ },
|
|
|
];
|
|
|
|
|
|
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
|