1 <!-- Any copyright is dedicated to the Public Domain.
2 - http://creativecommons.org/publicdomain/zero/1.0/ -->
6 <title>Test Observable Array Type
</title>
7 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
8 <link rel=
"stylesheet" type=
"text/css" href=
"/tests/SimpleTest/test.css" />
12 /* global TestInterfaceObservableArray */
14 add_task(async
function init() {
15 await SpecialPowers
.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
18 add_task(function testObservableArray_length() {
19 let setCallbackCount
= 0;
20 let deleteCallbackCount
= 0;
21 let deleteCallbackTests
= null;
23 let m
= new TestInterfaceObservableArray({
24 setBooleanCallback() {
27 deleteBooleanCallback(value
, index
) {
28 deleteCallbackCount
++;
29 if (typeof deleteCallbackTests
=== 'function') {
30 deleteCallbackTests(value
, index
);
34 m
.observableArrayBoolean
= [true, true, true, true, true];
36 let b
= m
.observableArrayBoolean
;
37 ok(Array
.isArray(b
), "observable array should be an array type");
38 is(b
.length
, 5, "length of observable array should be 5");
41 // [length, shouldThrow, expectedResult]
42 ["invalid", true, false],
43 [b
.length
+ 1, false, false],
44 [b
.length
, false, true],
45 [b
.length
- 1, false, true],
47 ].forEach(function([length
, shouldThrow
, expectedResult
]) {
49 let oldValues
= b
.slice();
50 let oldLen
= b
.length
;
51 let shouldSuccess
= !shouldThrow
&& expectedResult
;
53 deleteCallbackCount
= 0;
54 deleteCallbackTests
= null;
56 let deleteCallbackIndex
= b
.length
- 1;
57 deleteCallbackTests = function(_value
, _index
) {
58 info(`delete callback for ${_index}`);
59 is(_value
, oldValues
[deleteCallbackIndex
], "deleteCallbackTests: test value argument");
60 is(_index
, deleteCallbackIndex
, "deleteCallbackTests: test index argument");
61 deleteCallbackIndex
--;
66 info(`setting length to ${length}`);
69 ok(!shouldThrow
, `setting length should throw`);
71 ok(shouldThrow
, `setting length throws ${e}`);
73 is(setCallbackCount
, 0, "setCallback should not be called");
74 is(deleteCallbackCount
, shouldSuccess
? (oldLen
- length
) : 0, "deleteCallback count");
75 isDeeply(b
, shouldSuccess
? oldValues
.slice(0, length
) : oldValues
, "property values");
76 is(b
.length
, shouldSuccess
? length
: oldLen
, `length of observable array`);
80 add_task(function testObservableArray_length_callback_throw() {
81 let setCallbackCount
= 0;
82 let deleteCallbackCount
= 0;
84 let m
= new TestInterfaceObservableArray({
85 setBooleanCallback() {
88 deleteBooleanCallback(value
) {
89 deleteCallbackCount
++;
91 throw new Error("deleteBooleanCallback");
95 m
.observableArrayBoolean
= [true, true, false, false, false];
97 let b
= m
.observableArrayBoolean
;
98 ok(Array
.isArray(b
), "observable array should be an array type");
99 is(b
.length
, 5, "length of observable array should be 5");
102 setCallbackCount
= 0;
103 deleteCallbackCount
= 0;
106 info(`setting length to 0`);
109 ok(false, `setting length should throw`);
111 ok(true, `setting length throws ${e}`);
113 is(setCallbackCount
, 0, "setCallback should not be called");
114 is(deleteCallbackCount
, 4, "deleteCallback should be called");
115 isDeeply(b
, [true, true], "property values");
116 is(b
.length
, 2, `length of observable array`);
119 add_task(function testObservableArray_setter() {
120 let setCallbackCount
= 0;
121 let deleteCallbackCount
= 0;
122 let setCallbackTests
= null;
123 let deleteCallbackTests
= null;
125 let m
= new TestInterfaceObservableArray({
126 setBooleanCallback(value
, index
) {
128 if (typeof setCallbackTests
=== 'function') {
129 setCallbackTests(value
, index
);
132 deleteBooleanCallback(value
, index
) {
133 deleteCallbackCount
++;
134 if (typeof deleteCallbackTests
=== 'function') {
135 deleteCallbackTests(value
, index
);
140 let b
= m
.observableArrayBoolean
;
141 ok(Array
.isArray(b
), "observable array should be an array type");
142 is(b
.length
, 0, "length of observable array should be 0");
145 // [values, shouldThrow]
147 [[1,[],{},"invalid"], false],
148 [[0,NaN
,null,undefined,""], false],
149 [[true,true], false],
150 [[false,false,false], false],
151 ].forEach(function([values
, shouldThrow
]) {
153 let oldValues
= b
.slice();
154 let oldLen
= b
.length
;
155 setCallbackCount
= 0;
156 deleteCallbackCount
= 0;
157 setCallbackTests
= null;
158 deleteCallbackTests
= null;
160 let setCallbackIndex
= 0;
161 setCallbackTests = function(_value
, _index
) {
162 info(`set callback for ${_index}`);
163 is(_value
, !!values
[setCallbackIndex
], "setCallbackTests: test value argument");
164 is(_index
, setCallbackIndex
, "setCallbackTests: test index argument");
168 let deleteCallbackIndex
= b
.length
- 1;
169 deleteCallbackTests = function(_value
, _index
) {
170 info(`delete callback for ${_index}`);
171 is(_value
, oldValues
[deleteCallbackIndex
], "deleteCallbackTests: test value argument");
172 is(_index
, deleteCallbackIndex
, "deleteCallbackTests: test index argument");
173 deleteCallbackIndex
--;
178 info(`setting value to ${JSON.stringify(values)}`);
180 m
.observableArrayBoolean
= values
;
181 ok(!shouldThrow
, `setting value should not throw`);
183 ok(shouldThrow
, `setting value throws ${e}`);
185 is(setCallbackCount
, shouldThrow
? 0 : values
.length
, "setCallback count");
186 is(deleteCallbackCount
, oldLen
, "deleteCallback should be called");
187 isDeeply(b
, shouldThrow
? [] : values
.map(v
=> !!v
), "property values");
188 is(b
.length
, shouldThrow
? 0 : values
.length
, `length of observable array`);
192 add_task(function testObservableArray_setter_invalid_item() {
193 let setCallbackCount
= 0;
194 let deleteCallbackCount
= 0;
195 let setCallbackTests
= null;
196 let deleteCallbackTests
= null;
198 let m
= new TestInterfaceObservableArray({
199 setInterfaceCallback(value
, index
) {
201 if (typeof setCallbackTests
=== 'function') {
202 setCallbackTests(value
, index
);
205 deleteInterfaceCallback(value
, index
) {
206 deleteCallbackCount
++;
207 if (typeof deleteCallbackTests
=== 'function') {
208 deleteCallbackTests(value
, index
);
213 let b
= m
.observableArrayInterface
;
214 ok(Array
.isArray(b
), "observable array should be an array type");
215 is(b
.length
, 0, "length of observable array should be 0");
218 // [values, shouldThrow]
222 [[m
,"invalid"], true],
224 ].forEach(function([values
, shouldThrow
]) {
226 let oldValues
= b
.slice();
227 let oldLen
= b
.length
;
228 let setCallbackIndex
= 0;
229 setCallbackTests = function(_value
, _index
) {
230 info(`set callback for ${_index}`);
231 is(_value
, values
[setCallbackIndex
], "setCallbackTests: test value argument");
232 is(_index
, setCallbackIndex
, "setCallbackTests: test index argument");
235 let deleteCallbackIndex
= b
.length
- 1;
236 deleteCallbackTests = function(_value
, _index
) {
237 info(`delete callback for ${_index}`);
238 is(_value
, oldValues
[deleteCallbackIndex
], "deleteCallbackTests: test value argument");
239 is(_index
, deleteCallbackIndex
, "deleteCallbackTests: test index argument");
240 deleteCallbackIndex
--;
242 setCallbackCount
= 0;
243 deleteCallbackCount
= 0;
246 info(`setting value to ${values}`);
248 m
.observableArrayInterface
= values
;
249 ok(!shouldThrow
, `setting value should not throw`);
251 ok(shouldThrow
, `setting value throws ${e}`);
253 is(setCallbackCount
, shouldThrow
? 0 : values
.length
, "setCallback count");
254 is(deleteCallbackCount
, shouldThrow
? 0 : oldLen
, "deleteCallback should be called");
255 isDeeply(b
, shouldThrow
? oldValues
: values
, "property values");
256 is(b
.length
, shouldThrow
? oldLen
: values
.length
, `length of observable array`);
260 add_task(function testObservableArray_setter_callback_throw() {
261 let setCallbackCount
= 0;
262 let deleteCallbackCount
= 0;
264 let m
= new TestInterfaceObservableArray({
265 setBooleanCallback(value
, index
) {
268 throw new Error("setBooleanCallback");
271 deleteBooleanCallback(value
) {
272 deleteCallbackCount
++;
274 throw new Error("deleteBooleanCallback");
278 m
.observableArrayBoolean
= [false, false, false];
280 let b
= m
.observableArrayBoolean
;
281 ok(Array
.isArray(b
), "observable array should be an array type");
282 is(b
.length
, 3, "length of observable array should be 3");
285 // [values, shouldThrow, expectedLength, expectedSetCbCount, expectedDeleteCbCount]
286 [[false,false], false, 2, 2, 3],
287 [[false,true,false,false], true, 3, 4, 2],
288 [[false,false,true], true, 2, 0, 2],
289 ].forEach(function([values
, shouldThrow
, expectedLength
, expectedSetCbCount
,
290 expectedDeleteCbCount
]) {
292 setCallbackCount
= 0;
293 deleteCallbackCount
= 0;
296 info(`setting value to ${values}`);
298 m
.observableArrayBoolean
= values
;
299 ok(!shouldThrow
, `setting value should not throw`);
301 ok(shouldThrow
, `setting length throws ${e}`);
303 is(setCallbackCount
, expectedSetCbCount
, "setCallback should be called");
304 is(deleteCallbackCount
, expectedDeleteCbCount
, "deleteCallback should be called");
305 is(b
.length
, expectedLength
, `length of observable array`);
309 add_task(function testObservableArray_indexed_setter() {
310 let setCallbackCount
= 0;
311 let deleteCallbackCount
= 0;
312 let setCallbackTests
= null;
313 let deleteCallbackTests
= null;
315 let m
= new TestInterfaceObservableArray({
316 setBooleanCallback(value
, index
) {
318 if (typeof setCallbackTests
=== 'function') {
319 setCallbackTests(value
, index
);
322 deleteBooleanCallback(value
, index
) {
323 deleteCallbackCount
++;
324 if (typeof deleteCallbackTests
=== 'function') {
325 deleteCallbackTests(value
, index
);
330 let b
= m
.observableArrayBoolean
;
331 ok(Array
.isArray(b
), "observable array should be an array type");
332 is(b
.length
, 0, "length of observable array should be 0");
335 // [index, value, expectedResult]
336 [b
.length
+ 1, false, false],
337 [b
.length
, false, true],
338 [b
.length
+ 1, false, true],
339 [b
.length
+ 1, true, true],
340 ].forEach(function([index
, value
, expectedResult
]) {
342 let oldValue
= b
[index
];
343 let oldLen
= b
.length
;
344 setCallbackCount
= 0;
345 deleteCallbackCount
= 0;
346 setCallbackTests = function(_value
, _index
) {
347 info(`set callback for ${_index}`);
348 is(_value
, value
, "setCallbackTests: test value argument");
349 is(_index
, index
, "setCallbackTests: test index argument");
351 deleteCallbackTests = function(_value
, _index
) {
352 info(`delete callback for ${_index}`);
353 is(_value
, oldValue
, "deleteCallbackTests: test value argument");
354 is(_index
, index
, "deleteCallbackTests: test index argument");
358 info(`setting value of property ${index} to ${value}`);
361 ok(true, `setting value should not throw`);
363 ok(false, `setting value throws ${e}`);
365 is(setCallbackCount
, expectedResult
? 1 : 0, "setCallback should be called");
366 is(deleteCallbackCount
, (oldLen
> index
) ? 1 : 0, "deleteCallback should be called");
367 is(b
[index
], expectedResult
? value
: oldValue
, `property value`);
368 is(b
.length
, expectedResult
? Math
.max(oldLen
, index
+ 1) : oldLen
, `length of observable array`);
372 add_task(function testObservableArray_indexed_setter_invalid() {
373 let setCallbackCount
= 0;
374 let deleteCallbackCount
= 0;
375 let setCallbackTests
= null;
376 let deleteCallbackTests
= null;
378 let m
= new TestInterfaceObservableArray({
379 setInterfaceCallback(value
, index
) {
381 if (typeof setCallbackTests
=== 'function') {
382 setCallbackTests(value
, index
);
385 deleteInterfaceCallback(value
, index
) {
386 deleteCallbackCount
++;
387 if (typeof deleteCallbackTests
=== 'function') {
388 deleteCallbackTests(value
, index
);
393 let b
= m
.observableArrayInterface
;
394 ok(Array
.isArray(b
), "observable array should be an array type");
395 is(b
.length
, 0, "length of observable array should be 0");
398 // [index, value, shouldThrow]
399 [b
.length
, "invalid", true],
400 [b
.length
, m
, false],
401 [b
.length
+ 1, m
, false],
402 [b
.length
+ 1, "invalid", true],
403 ].forEach(function([index
, value
, shouldThrow
]) {
405 let oldValue
= b
[index
];
406 let oldLen
= b
.length
;
407 setCallbackCount
= 0;
408 deleteCallbackCount
= 0;
409 setCallbackTests = function(_value
, _index
) {
410 info(`set callback for ${_index}`);
411 is(_value
, value
, "setCallbackTests: test value argument");
412 is(_index
, index
, "setCallbackTests: test index argument");
414 deleteCallbackTests = function(_value
, _index
) {
415 info(`delete callback for ${_index}`);
416 is(_value
, oldValue
, "deleteCallbackTests: test value argument");
417 is(_index
, index
, "deleteCallbackTests: test index argument");
421 info(`setting value of property ${index} to ${value}`);
424 ok(!shouldThrow
, `setting value should not throw`);
426 ok(shouldThrow
, `setting value throws ${e}`);
428 is(setCallbackCount
, shouldThrow
? 0 : 1, "setCallback count");
429 is(deleteCallbackCount
, ((oldLen
> index
) && !shouldThrow
) ? 1 : 0, "deleteCallback count");
430 is(b
[index
], shouldThrow
? oldValue
: value
, `property value`);
431 is(b
.length
, shouldThrow
? oldLen
: Math
.max(oldLen
, index
+ 1), `length of observable array`);
435 add_task(function testObservableArray_indexed_setter_callback_throw() {
436 let setCallbackCount
= 0;
437 let deleteCallbackCount
= 0;
439 let m
= new TestInterfaceObservableArray({
440 setBooleanCallback(value
) {
443 throw new Error("setBooleanCallback");
446 deleteBooleanCallback(value
, index
) {
447 deleteCallbackCount
++;
449 throw new Error("deleteBooleanCallback");
453 m
.observableArrayBoolean
= [false, false, false];
455 let b
= m
.observableArrayBoolean
;
456 ok(Array
.isArray(b
), "observable array should be an array type");
457 is(b
.length
, 3, "length of observable array should be 3");
460 // [index, value, shouldThrow]
461 [b
.length
, true, true],
462 [b
.length
, false, false],
463 [b
.length
, true, true],
466 ].forEach(function([index
, value
, shouldThrow
]) {
468 let oldValue
= b
[index
];
469 let oldLen
= b
.length
;
470 setCallbackCount
= 0;
471 deleteCallbackCount
= 0;
474 info(`setting value of property ${index} to ${value}`);
477 ok(!shouldThrow
, `setting value should not throw`);
479 ok(shouldThrow
, `setting value throws ${e}`);
481 is(setCallbackCount
, (shouldThrow
&& index
< 2) ? 0 : 1, "setCallback should be called");
482 is(deleteCallbackCount
, (oldLen
> index
) ? 1 : 0, "deleteCallback should be called");
483 is(b
[index
], shouldThrow
? oldValue
: value
, "property value");
484 is(b
.length
, shouldThrow
? oldLen
: Math
.max(oldLen
, index
+ 1), `length of observable array`);
488 add_task(function testObservableArray_object() {
489 let setCallbackCount
= 0;
490 let deleteCallbackCount
= 0;
491 let callbackIndex
= 0;
494 {property1
: false, property2
: "property2"},
495 {property1
: [], property2
: 2},
498 let m
= new TestInterfaceObservableArray({
499 setObjectCallback(value
, index
) {
501 is(index
, callbackIndex
++, "setCallbackTests: test index argument");
502 isDeeply(values
[index
], value
, "setCallbackTests: test value argument");
504 deleteObjectCallback() {
505 deleteCallbackCount
++;
509 m
.observableArrayObject
= values
;
511 let b
= m
.observableArrayObject
;
512 ok(Array
.isArray(b
), "observable array should be an array type");
513 is(b
.length
, 2, "length of observable array should be 2");
514 is(setCallbackCount
, values
.length
, "setCallback should be called");
515 is(deleteCallbackCount
, 0, "deleteCallback should not be called");
517 for(let i
= 0; i
< values
.length
; i
++) {
518 isDeeply(values
[i
], b
[i
], `check index ${i}`);
522 add_task(function testObservableArray_xrays() {
523 let m
= new TestInterfaceObservableArray({
524 setObjectCallback() {
525 ok(false, "Shouldn't reach setObjectCallback");
527 deleteObjectCallback() {
528 ok(false, "Shouldn't reach deleteObjectCallback");
532 let wrapper
= SpecialPowers
.wrap(m
);
533 ok(SpecialPowers
.Cu
.isXrayWrapper(wrapper
), "Should be a wrapper");
534 let observableArray
= wrapper
.observableArrayBoolean
;
535 ok(!!observableArray
, "Should not throw");
536 is("length" in observableArray
, false, "Should be opaque");
539 wrapper
.observableArrayBoolean
= [true, false, false];
540 ok(false, "Expected to throw, for now");