859 lines
30 KiB
HTML
859 lines
30 KiB
HTML
<!-- Any copyright is dedicated to the Public Domain.
|
|
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
|
<!DOCTYPE HTML>
|
|
<html>
|
|
<head>
|
|
<title>Test Observable Array Type</title>
|
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
</head>
|
|
<body>
|
|
<script>
|
|
/* global TestInterfaceObservableArray */
|
|
|
|
add_task(async function init() {
|
|
await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_defineProperty() {
|
|
let setCallbackCount = 0;
|
|
let deleteCallbackCount = 0;
|
|
let setCallbackTests = null;
|
|
let deleteCallbackTests = null;
|
|
|
|
let m = new TestInterfaceObservableArray({
|
|
setBooleanCallback(value, index) {
|
|
setCallbackCount++;
|
|
if (typeof setCallbackTests === 'function') {
|
|
setCallbackTests(value, index);
|
|
}
|
|
},
|
|
deleteBooleanCallback(value, index) {
|
|
deleteCallbackCount++;
|
|
if (typeof deleteCallbackTests === 'function') {
|
|
deleteCallbackTests(value, index);
|
|
}
|
|
},
|
|
});
|
|
m.observableArrayBoolean = [true, true, true];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 3, "length of observable array should be 0");
|
|
|
|
// Test length
|
|
[
|
|
// [descriptor, shouldThrow, expectedResult]
|
|
// Invalid descriptor
|
|
[{configurable: true, value: 0}, false, false],
|
|
[{enumerable: true, value: 0}, false, false],
|
|
[{writable: false, value: 0}, false, false],
|
|
[{get: ()=>{}}, false, false],
|
|
[{set: ()=>{}}, false, false],
|
|
[{get: ()=>{}, set: ()=>{}}, false, false],
|
|
[{get: ()=>{}, value: 0}, true],
|
|
// Invalid length value
|
|
[{value: 1.9}, true],
|
|
[{value: "invalid"}, true],
|
|
[{value: {}}, true],
|
|
// length value should not greater than current length
|
|
[{value: b.length + 1}, false, false],
|
|
// descriptor without value
|
|
[{configurable: false, enumerable: false, writable: true}, false, true],
|
|
// Success
|
|
[{value: b.length}, false, true],
|
|
[{value: b.length - 1}, false, true],
|
|
[{value: 0}, false, true],
|
|
].forEach(function([descriptor, shouldThrow, expectedResult]) {
|
|
// Initialize
|
|
let oldLen = b.length;
|
|
let oldValues = b.slice();
|
|
let deleteCallbackIndex = oldLen - 1;
|
|
let success = expectedResult && "value" in descriptor;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
setCallbackTests = null;
|
|
deleteCallbackTests = function(_value, _index) {
|
|
is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument");
|
|
is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument");
|
|
deleteCallbackIndex--;
|
|
};
|
|
|
|
// Test
|
|
info(`defining "length" property with ${JSON.stringify(descriptor)}`);
|
|
try {
|
|
is(Reflect.defineProperty(b, "length", descriptor), expectedResult,
|
|
`Reflect.defineProperty should return ${expectedResult}`);
|
|
ok(!shouldThrow, "Reflect.defineProperty should not throw");
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, success ? oldLen - descriptor.value : 0, "deleteCallback count");
|
|
isDeeply(b, success ? oldValues.slice(0, descriptor.value) : oldValues, "property values");
|
|
is(b.length, success ? descriptor.value : oldLen, "length of observable array");
|
|
});
|
|
|
|
// Test indexed value
|
|
[
|
|
// [index, descriptor, shouldThrow, expectedResult]
|
|
// Invalid descriptor
|
|
[0, {configurable: false, value: true}, false, false],
|
|
[0, {enumerable: false, value: true}, false, false],
|
|
[0, {writable: false, value: true}, false, false],
|
|
[0, {get: ()=>{}}, false, false],
|
|
[0, {set: ()=>{}}, false, false],
|
|
[0, {get: ()=>{}, set: ()=>{}}, false, false],
|
|
[0, {get: ()=>{}, value: true}, true],
|
|
// Index could not greater than last index + 1.
|
|
[b.length + 1, {configurable: true, enumerable: true, value: true}, false, false],
|
|
// descriptor without value
|
|
[b.length, {configurable: true, enumerable: true}, false, true],
|
|
// Success
|
|
[b.length, {configurable: true, enumerable: true, value: true}, false, true],
|
|
[b.length + 1, {configurable: true, enumerable: true, value: true}, false, true],
|
|
].forEach(function([index, descriptor, shouldThrow, expectedResult]) {
|
|
// Initialize
|
|
let oldLen = b.length;
|
|
let oldValue = b[index];
|
|
let success = expectedResult && "value" in descriptor;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
setCallbackTests = function(_value, _index) {
|
|
is(_value, descriptor.value, "setCallbackTests: test value argument");
|
|
is(_index, index, "setCallbackTests: test index argument");
|
|
};
|
|
deleteCallbackTests = function(_value, _index) {
|
|
is(_value, oldValue, "deleteCallbackTests: test value argument");
|
|
is(_index, index, "deleteCallbackTests: test index argument");
|
|
};
|
|
|
|
// Test
|
|
info(`defining ${index} property with ${JSON.stringify(descriptor)}`);
|
|
try {
|
|
is(Reflect.defineProperty(b, index, descriptor), expectedResult,
|
|
`Reflect.defineProperty should return ${expectedResult}`);
|
|
ok(!shouldThrow, "Reflect.defineProperty should not throw");
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
|
|
}
|
|
is(setCallbackCount, success ? 1 : 0, "setCallback count");
|
|
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
|
|
is(b[index], success ? descriptor.value : oldValue, "property value");
|
|
is(b.length, success ? Math.max(index + 1, oldLen) : oldLen, "length of observable array");
|
|
});
|
|
|
|
// Test other property
|
|
[
|
|
// [property, descriptor, shouldThrow, expectedResult]
|
|
["prop1", {configurable: false, value: "value1"}, false, true],
|
|
["prop1", {configurable: true, value: "value2"}, false, false],
|
|
["prop2", {enumerable: false, value: 5}, false, true],
|
|
["prop3", {enumerable: false, value: []}, false, true],
|
|
["prop4", {enumerable: false, value: {}}, false, true],
|
|
["prop5", {get: ()=>{}, value: true}, true, false],
|
|
["prop6", {get: ()=>{}, set: ()=>{}}, false, true],
|
|
].forEach(function([property, descriptor, shouldThrow, expectedResult]) {
|
|
// Initialize
|
|
let oldValue = b[property];
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
setCallbackTests = null;
|
|
deleteCallbackTests = null;
|
|
|
|
// Test
|
|
info(`defining ${property} property with ${JSON.stringify(descriptor)}`);
|
|
try {
|
|
is(Reflect.defineProperty(b, property, descriptor), expectedResult,
|
|
`Reflect.defineProperty should return ${expectedResult}`);
|
|
ok(!shouldThrow, "Reflect.defineProperty should not throw");
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, 0, "deleteCallback count");
|
|
is(b[property], expectedResult ? descriptor.value : oldValue, "property value");
|
|
is(b.length, oldLen, "length of observable array");
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_defineProperty_callback_throw() {
|
|
let setCallbackCount = 0;
|
|
let deleteCallbackCount = 0;
|
|
|
|
const minLen = 3;
|
|
let m = new TestInterfaceObservableArray({
|
|
setBooleanCallback(value) {
|
|
setCallbackCount++;
|
|
if (value) {
|
|
throw new Error("setBooleanCallback");
|
|
}
|
|
},
|
|
deleteBooleanCallback(value, index) {
|
|
deleteCallbackCount++;
|
|
if (index < minLen) {
|
|
throw new Error("deleteBooleanCallback");
|
|
}
|
|
},
|
|
});
|
|
m.observableArrayBoolean = [false, false, false, false, false];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 5, "length of observable array should be 3");
|
|
|
|
// Test length
|
|
[
|
|
// [length, shouldThrow]
|
|
[b.length, false],
|
|
[b.length - 1, false],
|
|
[0, true],
|
|
].forEach(function([length, shouldThrow]) {
|
|
// Initialize
|
|
let oldValues = b.slice();
|
|
let oldLen = b.length;
|
|
let descriptor = {value: length};
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
|
|
// Test
|
|
info(`defining "length" property with ${JSON.stringify(descriptor)}`);
|
|
try {
|
|
ok(Reflect.defineProperty(b, "length", descriptor),
|
|
"Reflect.defineProperty should return true");
|
|
ok(!shouldThrow, "Reflect.defineProperty should not throw");
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, oldLen - (shouldThrow ? minLen - 1 : length), "deleteCallback count");
|
|
isDeeply(b, oldValues.slice(0, shouldThrow ? minLen : length), "property values");
|
|
is(b.length, shouldThrow ? minLen : length, "length of observable array");
|
|
});
|
|
|
|
// Test indexed value
|
|
[
|
|
// [index, value, shouldThrow]
|
|
[b.length, true, true],
|
|
[b.length, false, false],
|
|
[b.length + 1, false, false],
|
|
[b.length + 1, true, true],
|
|
[0, true, true],
|
|
[0, false, true],
|
|
].forEach(function([index, value, shouldThrow]) {
|
|
// Initialize
|
|
let oldValue = b[index];
|
|
let oldLen = b.length;
|
|
let descriptor = {configurable: true, enumerable: true, value};
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
|
|
// Test
|
|
info(`defining ${index} property with ${JSON.stringify(descriptor)}`);
|
|
try {
|
|
ok(Reflect.defineProperty(b, index, descriptor), "Reflect.defineProperty should return true");
|
|
ok(!shouldThrow, "Reflect.defineProperty should not throw");
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
|
|
}
|
|
is(setCallbackCount, (index < minLen) ? 0 : 1, "setCallback count");
|
|
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
|
|
is(b[index], shouldThrow ? oldValue : value, "property value");
|
|
is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), "length of observable array");
|
|
});
|
|
|
|
// Test other property
|
|
[
|
|
// [property, descriptor, expectedResult]
|
|
["prop1", {configurable: false, value: "value1"}, true],
|
|
["prop1", {configurable: true, value: "value2"}, false],
|
|
["prop2", {enumerable: false, value: 5}, true],
|
|
["prop3", {enumerable: false, value: []}, true],
|
|
["prop4", {enumerable: false, value: {}}, true],
|
|
].forEach(function([property, descriptor, expectedResult]) {
|
|
// Initialize
|
|
let oldValue = b[property];
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
|
|
// Test
|
|
info(`defining ${property} property with ${JSON.stringify(descriptor)}`);
|
|
try {
|
|
is(Reflect.defineProperty(b, property, descriptor), expectedResult,
|
|
`Reflect.defineProperty should return ${expectedResult}`);
|
|
ok(true, "Reflect.defineProperty should not throw");
|
|
} catch(e) {
|
|
ok(false, `Reflect.defineProperty throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, 0, "deleteCallback count");
|
|
is(b[property], expectedResult ? descriptor.value : oldValue, "property value");
|
|
is(b.length, oldLen, "length of observable array");
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_deleteProperty() {
|
|
let setCallbackCount = 0;
|
|
let deleteCallbackCount = 0;
|
|
let deleteCallbackTests = null;
|
|
|
|
let m = new TestInterfaceObservableArray({
|
|
setBooleanCallback() {
|
|
setCallbackCount++;
|
|
},
|
|
deleteBooleanCallback(value, index) {
|
|
deleteCallbackCount++;
|
|
if (typeof deleteCallbackTests === 'function') {
|
|
deleteCallbackTests(value, index);
|
|
}
|
|
},
|
|
});
|
|
m.observableArrayBoolean = [true, true];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 2, "length of observable array should be 2");
|
|
|
|
// Test length
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
info("deleting length property");
|
|
ok(!Reflect.deleteProperty(b, "length"), "test result of deleting length property");
|
|
is(setCallbackCount, 0, "setCallback should not be called");
|
|
is(deleteCallbackCount, 0, "deleteCallback should not be called");
|
|
is(b.length, 2, "length should still be 2");
|
|
|
|
// Test indexed value
|
|
[
|
|
// [index, expectedResult]
|
|
[2, false],
|
|
[0, false],
|
|
[1, true],
|
|
].forEach(function([index, expectedResult]) {
|
|
// Initialize
|
|
let oldLen = b.length;
|
|
let oldValue = b[index];
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
deleteCallbackTests = function(_value, _index) {
|
|
is(_value, oldValue, "deleteCallbackTests: test value argument");
|
|
is(_index, index, "deleteCallbackTests: test index argument");
|
|
};
|
|
|
|
// Test
|
|
info(`deleting ${index} property`);
|
|
is(Reflect.deleteProperty(b, index), expectedResult,
|
|
`Reflect.deleteProperty should return ${expectedResult}`);
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, expectedResult ? 1 : 0, "deleteCallback count");
|
|
is(b[index], expectedResult ? undefined : oldValue, "property value");
|
|
is(b.length, expectedResult ? oldLen - 1 : oldLen,
|
|
"length of observable array");
|
|
});
|
|
|
|
// Test other property
|
|
[
|
|
// [property, value]
|
|
["prop1", "value1"],
|
|
["prop2", 5],
|
|
["prop3", []],
|
|
["prop4", {}],
|
|
].forEach(function([property, value]) {
|
|
// Initialize
|
|
b[property] = value;
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
deleteCallbackTests = null;
|
|
|
|
// Test
|
|
info(`deleting ${property} property`);
|
|
is(b[property], value, `property value should be ${value} before deleting`);
|
|
ok(Reflect.deleteProperty(b, property), "Reflect.deleteProperty should return true");
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, 0, "deleteCallback count");
|
|
is(b[property], undefined, "property value should be undefined after deleting");
|
|
is(b.length, oldLen, "length of observable array");
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_deleteProperty_callback_throw() {
|
|
let setCallbackCount = 0;
|
|
let deleteCallbackCount = 0;
|
|
|
|
let m = new TestInterfaceObservableArray({
|
|
setBooleanCallback() {
|
|
setCallbackCount++;
|
|
},
|
|
deleteBooleanCallback(value) {
|
|
deleteCallbackCount++;
|
|
if (value) {
|
|
throw new Error("deleteBooleanCallback");
|
|
}
|
|
},
|
|
});
|
|
m.observableArrayBoolean = [true, false];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 2, "length of observable array should be 2");
|
|
|
|
// Test indexed value
|
|
let index = b.length;
|
|
while (index--) {
|
|
// Initialize
|
|
let oldValue = b[index];
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
|
|
// Test
|
|
info(`deleting index ${index}`);
|
|
try {
|
|
ok(Reflect.deleteProperty(b, index), "Reflect.deleteProperty should return true");
|
|
ok(!oldValue, "Reflect.deleteProperty should not throw");
|
|
} catch(e) {
|
|
ok(oldValue, `Reflect.deleteProperty throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, 1, "deleteCallback count");
|
|
is(b[index], oldValue ? oldValue : undefined, "property value");
|
|
is(b.length, oldValue ? oldLen : oldLen - 1, "length of observable array");
|
|
}
|
|
|
|
// Test other property
|
|
[
|
|
// [property, value]
|
|
["prop1", "value1"],
|
|
["prop2", 5],
|
|
["prop3", []],
|
|
["prop4", {}],
|
|
["prop5", false],
|
|
].forEach(function([property, value]) {
|
|
// Initialize
|
|
b[property] = value;
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
|
|
// Test
|
|
info(`deleting ${property} property`);
|
|
is(b[property], value, `property value should be ${JSON.stringify(value)} before deleting`);
|
|
try {
|
|
ok(Reflect.deleteProperty(b, property), `Reflect.deleteProperty should return true`);
|
|
ok(true, "Reflect.deleteProperty should not throw");
|
|
} catch(e) {
|
|
ok(false, `Reflect.deleteProperty throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, 0, "deleteCallback count");
|
|
is(b[property], undefined, `property value should be undefined after deleting`);
|
|
is(b.length, oldLen, "length of observable array");
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_get() {
|
|
let m = new TestInterfaceObservableArray();
|
|
m.observableArrayBoolean = [true, false];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 2, "length of observable array should be 2");
|
|
|
|
// Test length
|
|
is(Reflect.get(b, "length"), 2, "test result of getting length property");
|
|
|
|
// Test indexed value
|
|
is(Reflect.get(b, 0), true, "test result of getting index 0");
|
|
is(Reflect.get(b, 1), false, "test result of getting index 1");
|
|
is(Reflect.get(b, 2), undefined, "test result of getting index 2");
|
|
|
|
// Test other property
|
|
[
|
|
// [property, value]
|
|
["prop1", "value1"],
|
|
["prop2", 5],
|
|
["prop3", []],
|
|
["prop4", {}],
|
|
].forEach(function([property, value]) {
|
|
is(Reflect.get(b, property), undefined, `test ${property} property before setting property value`);
|
|
b[property] = value;
|
|
is(Reflect.get(b, property), value, `test ${property} property after setting property value`);
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_getOwnPropertyDescriptor() {
|
|
function TestDescriptor(object, property, exist, configurable, enumerable,
|
|
writable, value) {
|
|
let descriptor = Reflect.getOwnPropertyDescriptor(object, property);
|
|
if (!exist) {
|
|
is(descriptor, undefined, `descriptor of ${property} property should be undefined`);
|
|
return;
|
|
}
|
|
|
|
is(descriptor.configurable, configurable, `test descriptor of ${property} property (configurable)`);
|
|
is(descriptor.enumerable, enumerable, `test descriptor of ${property} property (enumerable)`);
|
|
is(descriptor.writable, writable, `test descriptor of ${property} property (writable)`);
|
|
is(descriptor.value, value, `test descriptor of ${property} property (value)`);
|
|
}
|
|
|
|
let m = new TestInterfaceObservableArray();
|
|
m.observableArrayBoolean = [true, false];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 2, "length of observable array should be 2");
|
|
|
|
// Test length
|
|
TestDescriptor(b, "length", true, false /* configurable */,
|
|
false /* enumerable */, true /* writable */ , 2 /* value */);
|
|
|
|
// Test indexed value
|
|
TestDescriptor(b, 0, true, true /* configurable */, true /* enumerable */,
|
|
true /* writable */ , true /* value */);
|
|
TestDescriptor(b, 1, true, true /* configurable */, true /* enumerable */,
|
|
true /* writable */ , false /* value */);
|
|
TestDescriptor(b, 2, false);
|
|
|
|
// Test other property
|
|
[
|
|
// [property, value, configurable, enumerable, writable]
|
|
["prop1", "value1", true, true, true],
|
|
["prop2", 5, true, true, false],
|
|
["prop3", [], true, false, false],
|
|
["prop4", {}, false, false, false],
|
|
].forEach(function([property, value, configurable, enumerable, writable]) {
|
|
Object.defineProperty(b, property, {
|
|
value,
|
|
configurable,
|
|
enumerable,
|
|
writable,
|
|
});
|
|
TestDescriptor(b, property, true, configurable, enumerable, writable , value);
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_has() {
|
|
let m = new TestInterfaceObservableArray();
|
|
m.observableArrayBoolean = [true, false];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 2, "length of observable array should be 2");
|
|
|
|
// Test length
|
|
ok(Reflect.has(b, "length"), `test length property`);
|
|
|
|
// Test indexed value
|
|
ok(Reflect.has(b, 0), `test 0 property`);
|
|
ok(Reflect.has(b, 1), `test 1 property`);
|
|
ok(!Reflect.has(b, 2), `test 2 property`);
|
|
|
|
// Test other property
|
|
[
|
|
// [property, value]
|
|
["prop1", "value1"],
|
|
["prop2", 5],
|
|
["prop3", []],
|
|
["prop4", {}],
|
|
].forEach(function([property, value]) {
|
|
ok(!Reflect.has(b, property), `test ${property} property before setting property value`);
|
|
b[property] = value;
|
|
ok(Reflect.has(b, property), `test ${property} property after setting property value`);
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_ownKeys() {
|
|
let m = new TestInterfaceObservableArray();
|
|
m.observableArrayBoolean = [true, false];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 2, "length of observable array should be 2");
|
|
|
|
// Add other properties
|
|
b.prop1 = "value1";
|
|
b.prop2 = 5;
|
|
b.prop3 = [];
|
|
b.prop4 = {};
|
|
|
|
let keys = Reflect.ownKeys(b);
|
|
SimpleTest.isDeeply(keys, ["0", "1", "length", "prop1", "prop2", "prop3", "prop4"], `test property keys`);
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_preventExtensions() {
|
|
let m = new TestInterfaceObservableArray();
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 0, "length of observable array should be 0");
|
|
|
|
// Test preventExtensions
|
|
ok(Reflect.isExtensible(b), "test isExtensible before preventExtensions");
|
|
ok(!Reflect.preventExtensions(b), "test preventExtensions");
|
|
ok(Reflect.isExtensible(b), "test isExtensible after preventExtensions");
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_set() {
|
|
let setCallbackCount = 0;
|
|
let deleteCallbackCount = 0;
|
|
let setCallbackTests = null;
|
|
let deleteCallbackTests = null;
|
|
|
|
let m = new TestInterfaceObservableArray({
|
|
setBooleanCallback(value, index) {
|
|
setCallbackCount++;
|
|
if (typeof setCallbackTests === 'function') {
|
|
setCallbackTests(value, index);
|
|
}
|
|
},
|
|
deleteBooleanCallback(value, index) {
|
|
deleteCallbackCount++;
|
|
if (typeof deleteCallbackTests === 'function') {
|
|
deleteCallbackTests(value, index);
|
|
}
|
|
},
|
|
});
|
|
m.observableArrayBoolean = [true, true, true];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 3, "length of observable array should be 3");
|
|
|
|
// Test length
|
|
[
|
|
// [length, shouldThrow, expectedResult]
|
|
// Invalid length value
|
|
[1.9, true],
|
|
['invalid', true],
|
|
[{}, true],
|
|
// length value should not greater than current length
|
|
[b.length + 1, false, false],
|
|
// Success
|
|
[b.length, false, true],
|
|
[b.length - 1, false, true],
|
|
[0, false, true],
|
|
].forEach(function([length, shouldThrow, expectedResult]) {
|
|
// Initialize
|
|
let oldLen = b.length;
|
|
let oldValues = b.slice();
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
setCallbackTests = null;
|
|
let deleteCallbackIndex = oldLen - 1;
|
|
deleteCallbackTests = function(_value, _index) {
|
|
is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument");
|
|
is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument");
|
|
deleteCallbackIndex--;
|
|
};
|
|
|
|
// Test
|
|
info(`setting "length" property value to ${length}`);
|
|
try {
|
|
is(Reflect.set(b, "length", length), expectedResult, `Reflect.set should return ${expectedResult}`);
|
|
ok(!shouldThrow, "Reflect.set should not throw");
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.set throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, expectedResult ? oldLen - length : 0, "deleteCallback count");
|
|
isDeeply(b, expectedResult ? oldValues.slice(0, length) : oldValues, "property values");
|
|
is(b.length, expectedResult ? length : oldLen, "length of observable array");
|
|
});
|
|
|
|
// Test indexed value
|
|
[
|
|
// [index, value, shouldThrow, expectedResult]
|
|
// Index could not greater than last index.
|
|
[b.length + 1, true, false, false],
|
|
// Success
|
|
[b.length, true, false, true],
|
|
[b.length + 1, true, false, true],
|
|
].forEach(function([index, value, shouldThrow, expectedResult]) {
|
|
// Initialize
|
|
let oldLen = b.length;
|
|
let oldValue = b[index];
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
setCallbackTests = function(_value, _index) {
|
|
is(_value, value, "setCallbackTests: test value argument");
|
|
is(_index, index, "setCallbackTests: test index argument");
|
|
};
|
|
deleteCallbackTests = function(_value, _index) {
|
|
is(_value, oldValue, "deleteCallbackTests: test value argument");
|
|
is(_index, index, "deleteCallbackTests: test index argument");
|
|
};
|
|
|
|
// Test
|
|
info(`setting ${index} property to ${value}`);
|
|
try {
|
|
is(Reflect.set(b, index, value), expectedResult, `Reflect.set should return ${expectedResult}`);
|
|
ok(!shouldThrow, "Reflect.set should not throw");
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.set throws ${e}`);
|
|
}
|
|
is(setCallbackCount, expectedResult ? 1 : 0, "setCallback count");
|
|
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
|
|
is(b[index], expectedResult ? value : oldValue, "property value");
|
|
is(b.length, expectedResult ? Math.max(index + 1, oldLen) : oldLen, "length of observable array");
|
|
});
|
|
|
|
// Test other property
|
|
[
|
|
// [property, value]
|
|
["prop1", "value1"],
|
|
["prop1", "value2"],
|
|
["prop2", 5],
|
|
["prop3", []],
|
|
["prop4", {}],
|
|
].forEach(function([property, value]) {
|
|
// Initialize
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
setCallbackTests = null;
|
|
deleteCallbackTests = null;
|
|
|
|
// Test
|
|
info(`setting ${property} property to ${value}`);
|
|
ok(Reflect.set(b, property, value), "Reflect.defineProperty should return true");
|
|
is(setCallbackCount, 0, "setCallback count");
|
|
is(deleteCallbackCount, 0, "deleteCallback count");
|
|
is(b[property], value, "property value");
|
|
is(b.length, oldLen, "length of observable array");
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_set_callback_throw() {
|
|
let setCallbackCount = 0;
|
|
let deleteCallbackCount = 0;
|
|
|
|
const minLen = 3;
|
|
let m = new TestInterfaceObservableArray({
|
|
setBooleanCallback(value) {
|
|
setCallbackCount++;
|
|
if (value) {
|
|
throw new Error("setBooleanCallback");
|
|
}
|
|
},
|
|
deleteBooleanCallback(value, index) {
|
|
deleteCallbackCount++;
|
|
if (index < minLen) {
|
|
throw new Error("deleteBooleanCallback");
|
|
}
|
|
},
|
|
});
|
|
m.observableArrayBoolean = [false, false, false, false, false];
|
|
|
|
let b = m.observableArrayBoolean;
|
|
ok(Array.isArray(b), "observable array should be an array type");
|
|
is(b.length, 5, "length of observable array should be 3");
|
|
|
|
// Test length
|
|
[
|
|
// [value, shouldThrow]
|
|
[b.length, false],
|
|
[b.length - 1, false],
|
|
[0, true],
|
|
].forEach(function([length, shouldThrow]) {
|
|
// Initialize
|
|
let oldValues = b.slice();
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
|
|
// Test
|
|
info(`setting "length" property to ${length}`);
|
|
try {
|
|
ok(Reflect.set(b, "length", length), "Reflect.set should return true");
|
|
ok(!shouldThrow, `Reflect.set should not throw`);
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.set throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback should not be called");
|
|
is(deleteCallbackCount, oldLen - (shouldThrow ? minLen - 1 : length), "deleteCallback count");
|
|
isDeeply(b, oldValues.slice(0, shouldThrow ? minLen : length), "property values");
|
|
is(b.length, shouldThrow ? minLen : length, "length of observable array");
|
|
});
|
|
|
|
// Test indexed value
|
|
[
|
|
// [index, value, shouldThrow]
|
|
[b.length, true, true],
|
|
[b.length, false, false],
|
|
[b.length + 1, false, false],
|
|
[b.length + 1, true, true],
|
|
[0, false, true],
|
|
[0, true, true],
|
|
].forEach(function([index, value, shouldThrow]) {
|
|
// Initialize
|
|
let oldValue = b[index];
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
|
|
// Test
|
|
info(`setting ${index} property to ${value}`);
|
|
try {
|
|
ok(Reflect.set(b, index, value), "Reflect.set should return true");
|
|
ok(!shouldThrow, `Reflect.set should not throw`);
|
|
} catch(e) {
|
|
ok(shouldThrow, `Reflect.set throws ${e}`);
|
|
}
|
|
is(setCallbackCount, (index < minLen) ? 0 : 1, "setCallback count");
|
|
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
|
|
is(b[index], shouldThrow ? oldValue : value, "property value");
|
|
is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), "length of observable array");
|
|
});
|
|
|
|
// Test other property
|
|
[
|
|
["prop1", "value1"],
|
|
["prop1", "value2"],
|
|
["prop2", 5],
|
|
["prop3", []],
|
|
["prop4", {}],
|
|
].forEach(function([property, value]) {
|
|
// Initialize
|
|
let oldLen = b.length;
|
|
setCallbackCount = 0;
|
|
deleteCallbackCount = 0;
|
|
|
|
// Test
|
|
info(`setting ${property} property to ${JSON.stringify(value)}`);
|
|
try {
|
|
ok(Reflect.set(b, property, value), "Reflect.set should return true");
|
|
ok(true, `Reflect.set should not throw`);
|
|
} catch(e) {
|
|
ok(false, `Reflect.set throws ${e}`);
|
|
}
|
|
is(setCallbackCount, 0, "setCallback should not be called");
|
|
is(deleteCallbackCount, 0, "deleteCallback should be called");
|
|
is(b[property], value, "property value");
|
|
is(b.length, oldLen, "length of observable array");
|
|
});
|
|
});
|
|
|
|
add_task(function testObservableArrayExoticObjects_invalidtype() {
|
|
let m = new TestInterfaceObservableArray();
|
|
let i = m.observableArrayInterface;
|
|
ok(Array.isArray(i), "Observable array should be an array type");
|
|
is(i.length, 0, "length should be 0");
|
|
|
|
[true, "invalid"].forEach(function(value) {
|
|
SimpleTest.doesThrow(() => {
|
|
let descriptor = {configurable: true, enumerable: true, writable: true, value};
|
|
Reflect.defineProperty(i, i.length, descriptor);
|
|
}, `defining ${i.length} property with ${JSON.stringify(value)} should throw`);
|
|
|
|
SimpleTest.doesThrow(() => {
|
|
Reflect.set(i, i.length, value);
|
|
}, `setting ${i.length} property to ${JSON.stringify(value)} should throw`);
|
|
});
|
|
|
|
is(i.length, 0, "length should still be 0");
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|