diff options
Diffstat (limited to '')
-rw-r--r-- | dom/bindings/test/test_observablearray.html | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/dom/bindings/test/test_observablearray.html b/dom/bindings/test/test_observablearray.html new file mode 100644 index 0000000000..313fb67622 --- /dev/null +++ b/dom/bindings/test/test_observablearray.html @@ -0,0 +1,546 @@ +<!-- 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 testObservableArray_length() { + let setCallbackCount = 0; + let deleteCallbackCount = 0; + let deleteCallbackTests = null; + + let m = new TestInterfaceObservableArray({ + setBooleanCallback(value, index) { + setCallbackCount++; + }, + deleteBooleanCallback(value, index) { + deleteCallbackCount++; + if (typeof deleteCallbackTests === 'function') { + deleteCallbackTests(value, index); + } + }, + }); + m.observableArrayBoolean = [true, true, true, true, true]; + + 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 5"); + + [ + // [length, shouldThrow, expectedResult] + ["invalid", true, false], + [b.length + 1, false, false], + [b.length, false, true], + [b.length - 1, false, true], + [0, false, true], + ].forEach(function([length, shouldThrow, expectedResult]) { + // Initialize + let oldValues = b.slice(); + let oldLen = b.length; + let shouldSuccess = !shouldThrow && expectedResult; + setCallbackCount = 0; + deleteCallbackCount = 0; + deleteCallbackTests = null; + if (shouldSuccess) { + let deleteCallbackIndex = b.length - 1; + deleteCallbackTests = function(_value, _index) { + info(`delete callback for ${_index}`); + is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument"); + is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument"); + deleteCallbackIndex--; + }; + } + + // Test + info(`setting length to ${length}`); + try { + b.length = length; + ok(!shouldThrow, `setting length should throw`); + } catch(e) { + ok(shouldThrow, `setting length throws ${e}`); + } + is(setCallbackCount, 0, "setCallback should not be called"); + is(deleteCallbackCount, shouldSuccess ? (oldLen - length) : 0, "deleteCallback count"); + isDeeply(b, shouldSuccess ? oldValues.slice(0, length) : oldValues, "property values"); + is(b.length, shouldSuccess ? length : oldLen, `length of observable array`); + }); +}); + +add_task(function testObservableArray_length_callback_throw() { + let setCallbackCount = 0; + let deleteCallbackCount = 0; + + let m = new TestInterfaceObservableArray({ + setBooleanCallback(value, index) { + setCallbackCount++; + }, + deleteBooleanCallback(value, index) { + deleteCallbackCount++; + if (value) { + throw new Error("deleteBooleanCallback"); + } + }, + }); + m.observableArrayBoolean = [true, true, 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 5"); + + // Initialize + setCallbackCount = 0; + deleteCallbackCount = 0; + + // Test + info(`setting length to 0`); + try { + b.length = 0; + ok(false, `setting length should throw`); + } catch(e) { + ok(true, `setting length throws ${e}`); + } + is(setCallbackCount, 0, "setCallback should not be called"); + is(deleteCallbackCount, 4, "deleteCallback should be called"); + isDeeply(b, [true, true], "property values"); + is(b.length, 2, `length of observable array`); +}); + +add_task(function testObservableArray_setter() { + 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); + } + }, + }); + + 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"); + + [ + // [values, shouldThrow] + ["invalid", true], + [[1,[],{},"invalid"], false], + [[0,NaN,null,undefined,""], false], + [[true,true], false], + [[false,false,false], false], + ].forEach(function([values, shouldThrow]) { + // Initialize + let oldValues = b.slice(); + let oldLen = b.length; + setCallbackCount = 0; + deleteCallbackCount = 0; + setCallbackTests = null; + deleteCallbackTests = null; + if (!shouldThrow) { + let setCallbackIndex = 0; + setCallbackTests = function(_value, _index) { + info(`set callback for ${_index}`); + is(_value, !!values[setCallbackIndex], "setCallbackTests: test value argument"); + is(_index, setCallbackIndex, "setCallbackTests: test index argument"); + setCallbackIndex++; + }; + + let deleteCallbackIndex = b.length - 1; + deleteCallbackTests = function(_value, _index) { + info(`delete callback for ${_index}`); + is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument"); + is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument"); + deleteCallbackIndex--; + }; + } + + // Test + info(`setting value to ${JSON.stringify(values)}`); + try { + m.observableArrayBoolean = values; + ok(!shouldThrow, `setting value should not throw`); + } catch(e) { + ok(shouldThrow, `setting value throws ${e}`); + } + is(setCallbackCount, shouldThrow ? 0 : values.length, "setCallback count"); + is(deleteCallbackCount, oldLen, "deleteCallback should be called"); + isDeeply(b, shouldThrow ? [] : values.map(v => !!v), "property values"); + is(b.length, shouldThrow ? 0 : values.length, `length of observable array`); + }); +}); + +add_task(function testObservableArray_setter_invalid_item() { + let setCallbackCount = 0; + let deleteCallbackCount = 0; + let setCallbackTests = null; + let deleteCallbackTests = null; + + let m = new TestInterfaceObservableArray({ + setInterfaceCallback(value, index) { + setCallbackCount++; + if (typeof setCallbackTests === 'function') { + setCallbackTests(value, index); + } + }, + deleteInterfaceCallback(value, index) { + deleteCallbackCount++; + if (typeof deleteCallbackTests === 'function') { + deleteCallbackTests(value, index); + } + }, + }); + + let b = m.observableArrayInterface; + ok(Array.isArray(b), "observable array should be an array type"); + is(b.length, 0, "length of observable array should be 0"); + + [ + // [values, shouldThrow] + [[m,m,m,m], false], + [["invalid"], true], + [[m,m], false], + [[m,"invalid"], true], + [[m,m,m], false], + ].forEach(function([values, shouldThrow]) { + // Initialize + let oldValues = b.slice(); + let oldLen = b.length; + let setCallbackIndex = 0; + setCallbackTests = function(_value, _index) { + info(`set callback for ${_index}`); + is(_value, values[setCallbackIndex], "setCallbackTests: test value argument"); + is(_index, setCallbackIndex, "setCallbackTests: test index argument"); + setCallbackIndex++; + }; + let deleteCallbackIndex = b.length - 1; + deleteCallbackTests = function(_value, _index) { + info(`delete callback for ${_index}`); + is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument"); + is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument"); + deleteCallbackIndex--; + }; + setCallbackCount = 0; + deleteCallbackCount = 0; + + // Test + info(`setting value to ${values}`); + try { + m.observableArrayInterface = values; + ok(!shouldThrow, `setting value should not throw`); + } catch(e) { + ok(shouldThrow, `setting value throws ${e}`); + } + is(setCallbackCount, shouldThrow ? 0 : values.length, "setCallback count"); + is(deleteCallbackCount, shouldThrow ? 0 : oldLen, "deleteCallback should be called"); + isDeeply(b, shouldThrow ? oldValues : values, "property values"); + is(b.length, shouldThrow ? oldLen : values.length, `length of observable array`); + }); +}); + +add_task(function testObservableArray_setter_callback_throw() { + let setCallbackCount = 0; + let deleteCallbackCount = 0; + + let m = new TestInterfaceObservableArray({ + setBooleanCallback(value, index) { + setCallbackCount++; + if (index >= 3) { + throw new Error("setBooleanCallback"); + } + }, + deleteBooleanCallback(value, index) { + deleteCallbackCount++; + if (value) { + throw new Error("deleteBooleanCallback"); + } + }, + }); + m.observableArrayBoolean = [false, false, false]; + + 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"); + + [ + // [values, shouldThrow, expectedLength, expectedSetCbCount, expectedDeleteCbCount] + [[false,false], false, 2, 2, 3], + [[false,true,false,false], true, 3, 4, 2], + [[false,false,true], true, 2, 0, 2], + ].forEach(function([values, shouldThrow, expectedLength, expectedSetCbCount, + expectedDeleteCbCount]) { + // Initialize + setCallbackCount = 0; + deleteCallbackCount = 0; + + // Test + info(`setting value to ${values}`); + try { + m.observableArrayBoolean = values; + ok(!shouldThrow, `setting value should not throw`); + } catch(e) { + ok(shouldThrow, `setting length throws ${e}`); + } + is(setCallbackCount, expectedSetCbCount, "setCallback should be called"); + is(deleteCallbackCount, expectedDeleteCbCount, "deleteCallback should be called"); + is(b.length, expectedLength, `length of observable array`); + }); +}); + +add_task(function testObservableArray_indexed_setter() { + 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); + } + }, + }); + + 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"); + + [ + // [index, value, expectedResult] + [b.length + 1, false, false], + [b.length, false, true], + [b.length + 1, false, true], + [b.length + 1, true, true], + ].forEach(function([index, value, expectedResult]) { + // Initialize + let oldValue = b[index]; + let oldLen = b.length; + setCallbackCount = 0; + deleteCallbackCount = 0; + setCallbackTests = function(_value, _index) { + info(`set callback for ${_index}`); + is(_value, value, "setCallbackTests: test value argument"); + is(_index, index, "setCallbackTests: test index argument"); + }; + deleteCallbackTests = function(_value, _index) { + info(`delete callback for ${_index}`); + is(_value, oldValue, "deleteCallbackTests: test value argument"); + is(_index, index, "deleteCallbackTests: test index argument"); + }; + + // Test + info(`setting value of property ${index} to ${value}`); + try { + b[index] = value; + ok(true, `setting value should not throw`); + } catch(e) { + ok(false, `setting value throws ${e}`); + } + is(setCallbackCount, expectedResult ? 1 : 0, "setCallback should be called"); + is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback should be called"); + is(b[index], expectedResult ? value : oldValue, `property value`); + is(b.length, expectedResult ? Math.max(oldLen, index + 1) : oldLen, `length of observable array`); + }); +}); + +add_task(function testObservableArray_indexed_setter_invalid() { + let setCallbackCount = 0; + let deleteCallbackCount = 0; + let setCallbackTests = null; + let deleteCallbackTests = null; + + let m = new TestInterfaceObservableArray({ + setInterfaceCallback(value, index) { + setCallbackCount++; + if (typeof setCallbackTests === 'function') { + setCallbackTests(value, index); + } + }, + deleteInterfaceCallback(value, index) { + deleteCallbackCount++; + if (typeof deleteCallbackTests === 'function') { + deleteCallbackTests(value, index); + } + }, + }); + + let b = m.observableArrayInterface; + ok(Array.isArray(b), "observable array should be an array type"); + is(b.length, 0, "length of observable array should be 0"); + + [ + // [index, value, shouldThrow] + [b.length, "invalid", true], + [b.length, m, false], + [b.length + 1, m, false], + [b.length + 1, "invalid", true], + ].forEach(function([index, value, shouldThrow]) { + // Initialize + let oldValue = b[index]; + let oldLen = b.length; + setCallbackCount = 0; + deleteCallbackCount = 0; + setCallbackTests = function(_value, _index) { + info(`set callback for ${_index}`); + is(_value, value, "setCallbackTests: test value argument"); + is(_index, index, "setCallbackTests: test index argument"); + }; + deleteCallbackTests = function(_value, _index) { + info(`delete callback for ${_index}`); + is(_value, oldValue, "deleteCallbackTests: test value argument"); + is(_index, index, "deleteCallbackTests: test index argument"); + }; + + // Test + info(`setting value of property ${index} to ${value}`); + try { + b[index] = value; + ok(!shouldThrow, `setting value should not throw`); + } catch(e) { + ok(shouldThrow, `setting value throws ${e}`); + } + is(setCallbackCount, shouldThrow ? 0 : 1, "setCallback count"); + is(deleteCallbackCount, ((oldLen > index) && !shouldThrow) ? 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`); + }); +}); + +add_task(function testObservableArray_indexed_setter_callback_throw() { + let setCallbackCount = 0; + let deleteCallbackCount = 0; + + let m = new TestInterfaceObservableArray({ + setBooleanCallback(value, index) { + setCallbackCount++; + if (value) { + throw new Error("setBooleanCallback"); + } + }, + deleteBooleanCallback(value, index) { + deleteCallbackCount++; + if (index < 2) { + throw new Error("deleteBooleanCallback"); + } + }, + }); + m.observableArrayBoolean = [false, false, false]; + + 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"); + + [ + // [index, value, shouldThrow] + [b.length, true, true], + [b.length, false, false], + [b.length, 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 value of property ${index} to ${value}`); + try { + b[index] = value; + ok(!shouldThrow, `setting value should not throw`); + } catch(e) { + ok(shouldThrow, `setting value throws ${e}`); + } + is(setCallbackCount, (shouldThrow && index < 2) ? 0 : 1, "setCallback should be called"); + is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback should be called"); + is(b[index], shouldThrow ? oldValue : value, "property value"); + is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), `length of observable array`); + }); +}); + +add_task(function testObservableArray_object() { + let setCallbackCount = 0; + let deleteCallbackCount = 0; + let callbackIndex = 0; + + let values = [ + {property1: false, property2: "property2"}, + {property1: [], property2: 2}, + ]; + + let m = new TestInterfaceObservableArray({ + setObjectCallback(value, index) { + setCallbackCount++; + is(index, callbackIndex++, "setCallbackTests: test index argument"); + isDeeply(values[index], value, "setCallbackTests: test value argument"); + }, + deleteObjectCallback(value, index) { + deleteCallbackCount++; + }, + }); + + m.observableArrayObject = values; + + let b = m.observableArrayObject; + ok(Array.isArray(b), "observable array should be an array type"); + is(b.length, 2, "length of observable array should be 2"); + is(setCallbackCount, values.length, "setCallback should be called"); + is(deleteCallbackCount, 0, "deleteCallback should not be called"); + + for(let i = 0; i < values.length; i++) { + isDeeply(values[i], b[i], `check index ${i}`); + } +}); + +add_task(function testObservableArray_xrays() { + let m = new TestInterfaceObservableArray({ + setObjectCallback(value, index) { + ok(false, "Shouldn't reach setObjectCallback"); + }, + deleteObjectCallback(value, index) { + ok(false, "Shouldn't reach deleteObjectCallback"); + }, + }); + + let wrapper = SpecialPowers.wrap(m); + ok(SpecialPowers.Cu.isXrayWrapper(wrapper), "Should be a wrapper"); + let observableArray = wrapper.observableArrayBoolean; + ok(!!observableArray, "Should not throw"); + is("length" in observableArray, false, "Should be opaque"); + + try { + wrapper.observableArrayBoolean = [true, false, false]; + ok(false, "Expected to throw, for now"); + } catch (ex) {} +}); + +</script> +</body> +</html> |