toolkit/content/tests/widgets/tree_shared.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:fbae83445c36
1 var columns_simpletree =
2 [
3 { name: "name", label: "Name", key: true, properties: "one two" },
4 { name: "address", label: "Address" }
5 ];
6
7 var columns_hiertree =
8 [
9 { name: "name", label: "Name", primary: true, key: true, properties: "one two" },
10 { name: "address", label: "Address" },
11 { name: "planet", label: "Planet" },
12 { name: "gender", label: "Gender", cycler: true }
13 ];
14
15 // XXXndeakin still to add some tests for:
16 // cycler columns, checkbox cells, progressmeter cells
17
18 // this test function expects a tree to have 8 rows in it when it isn't
19 // expanded. The tree should only display four rows at a time. If editable,
20 // the cell at row 1 and column 0 must be editable, and the cell at row 2 and
21 // column 1 must not be editable.
22 function testtag_tree(treeid, treerowinfoid, seltype, columnstype, testid)
23 {
24 // Stop keystrokes that aren't handled by the tree from leaking out and
25 // scrolling the main Mochitests window!
26 function preventDefault(event) {
27 event.preventDefault();
28 }
29 document.addEventListener("keypress", preventDefault, false);
30
31 var multiple = (seltype == "multiple");
32
33 var tree = document.getElementById(treeid);
34 var treerowinfo = document.getElementById(treerowinfoid);
35 var rowInfo;
36 if (testid =="tree view")
37 rowInfo = getCustomTreeViewCellInfo();
38 else
39 rowInfo = convertDOMtoTreeRowInfo(treerowinfo, 0, { value: -1 });
40 var columnInfo = (columnstype == "simple") ? columns_simpletree : columns_hiertree;
41
42 is(tree.view.selection.currentColumn, null, testid + " initial currentColumn");
43 is(tree.selType, seltype == "multiple" ? "" : seltype, testid + " seltype");
44
45 // note: the functions below should be in this order due to changes made in later tests
46
47 // select the first column in cell selection mode so that the selection
48 // functions can be tested
49 if (seltype == "cell")
50 tree.view.selection.currentColumn = tree.columns[0];
51
52 testtag_tree_columns(tree, columnInfo, testid);
53 testtag_tree_TreeSelection(tree, testid, multiple);
54 testtag_tree_TreeSelection_UI(tree, testid, multiple);
55 if (seltype == "cell")
56 testtag_tree_TreeSelection_UI_cell(tree, testid, rowInfo);
57
58 testtag_tree_TreeView(tree, testid, rowInfo);
59
60 is(tree.editable, false, "tree should not be editable");
61 // currently, the editable flag means that tree editing cannot be invoked
62 // by the user. However, editing can still be started with a script.
63 is(tree.editingRow, -1, testid + " initial editingRow");
64 is(tree.editingColumn, null, testid + " initial editingColumn");
65
66 testtag_tree_UI_editing(tree, testid, rowInfo);
67
68 is(tree.editable, false, "tree should not be editable after testtag_tree_UI_editing");
69 // currently, the editable flag means that tree editing cannot be invoked
70 // by the user. However, editing can still be started with a script.
71 is(tree.editingRow, -1, testid + " initial editingRow (continued)");
72 is(tree.editingColumn, null, testid + " initial editingColumn (continued)");
73
74 var ecolumn = tree.columns[0];
75 ok(!tree.startEditing(1, ecolumn), "non-editable trees shouldn't start editing");
76 is(tree.editingRow, -1, testid + " failed startEditing shouldn't set editingRow");
77 is(tree.editingColumn, null, testid + " failed startEditing shouldn't set editingColumn");
78
79 tree.editable = true;
80
81 ok(tree.startEditing(1, ecolumn), "startEditing should have returned true");
82 is(tree.editingRow, 1, testid + " startEditing editingRow");
83 is(tree.editingColumn, ecolumn, testid + " startEditing editingColumn");
84 is(tree.getAttribute("editing"), "true", testid + " startEditing editing attribute");
85
86 tree.stopEditing(true);
87 is(tree.editingRow, -1, testid + " stopEditing editingRow");
88 is(tree.editingColumn, null, testid + " stopEditing editingColumn");
89 is(tree.hasAttribute("editing"), false, testid + " stopEditing editing attribute");
90
91 tree.startEditing(-1, ecolumn);
92 is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing -1 editingRow");
93 tree.startEditing(15, ecolumn);
94 is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing 15 editingRow");
95 tree.startEditing(1, null);
96 is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing null column editingRow");
97 tree.startEditing(2, tree.columns[1]);
98 is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing non editable cell editingRow");
99
100 tree.startEditing(1, ecolumn);
101 var inputField = tree.inputField;
102 is(inputField instanceof Components.interfaces.nsIDOMXULTextBoxElement, true, testid + "inputField");
103 inputField.value = "Changed Value";
104 tree.stopEditing(true);
105 is(tree.view.getCellText(1, ecolumn), "Changed Value", testid + "edit cell accept");
106
107 // this cell can be edited, but stopEditing(false) means don't accept the change.
108 tree.startEditing(1, ecolumn);
109 inputField.value = "Second Value";
110 tree.stopEditing(false);
111 is(tree.view.getCellText(1, ecolumn), "Changed Value", testid + "edit cell no accept");
112
113 tree.editable = false;
114
115 // do the sorting tests last as it will cause the rows to rearrange
116 // skip them for the custom tree view
117 if (testid !="tree view")
118 testtag_tree_TreeView_rows_sort(tree, testid, rowInfo);
119
120 testtag_tree_wheel(tree);
121
122 document.removeEventListener("keypress", preventDefault, false);
123
124 SimpleTest.finish();
125 }
126
127 function testtag_tree_columns(tree, expectedColumns, testid)
128 {
129 testid += " ";
130
131 var columns = tree.columns;
132
133 is(columns instanceof TreeColumns, true, testid + "columns is a TreeColumns");
134 is(columns.count, expectedColumns.length, testid + "TreeColumns count");
135 is(columns.length, expectedColumns.length, testid + "TreeColumns length");
136
137 var treecols = tree.getElementsByTagName("treecols")[0];
138 var treecol = treecols.getElementsByTagName("treecol");
139
140 var x = 0;
141 var primary = null, sorted = null, key = null;
142 for (var c = 0; c < expectedColumns.length; c++) {
143 var adjtestid = testid + " column " + c + " ";
144 var column = columns[c];
145 var expectedColumn = expectedColumns[c];
146 is(columns.getColumnAt(c), column, adjtestid + "getColumnAt");
147 is(columns.getNamedColumn(expectedColumn.name), column, adjtestid + "getNamedColumn");
148 is(columns.getColumnFor(treecol[c]), column, adjtestid + "getColumnFor");
149 if (expectedColumn.primary)
150 primary = column;
151 if (expectedColumn.sorted)
152 sorted = column;
153 if (expectedColumn.key)
154 key = column;
155
156 // XXXndeakin on Windows and Linux, some columns are one pixel to the
157 // left of where they should be. Could just be a rounding issue.
158 var adj = 1;
159 is(column.x + adj >= x, true, adjtestid + "position is after last column " +
160 column.x + "," + column.width + "," + x);
161 is(column.width > 0, true, adjtestid + "width is greater than 0");
162 x = column.x + column.width;
163
164 // now check the TreeColumn properties
165 is(column instanceof TreeColumn, true, adjtestid + "is a TreeColumn");
166 is(column.element, treecol[c], adjtestid + "element is treecol");
167 is(column.columns, columns, adjtestid + "columns is TreeColumns");
168 is(column.id, expectedColumn.name, adjtestid + "name");
169 is(column.index, c, adjtestid + "index");
170 is(column.primary, primary == column, adjtestid + "column is primary");
171
172 is(column.cycler, "cycler" in expectedColumn && expectedColumn.cycler,
173 adjtestid + "column is cycler");
174 is(column.selectable, true, adjtestid + "column is selectable");
175 is(column.editable, "editable" in expectedColumn && expectedColumn.editable,
176 adjtestid + "column is editable");
177
178 is(column.type, "type" in expectedColumn ? expectedColumn.type : 1, adjtestid + "type");
179
180 is(column.getPrevious(), c > 0 ? columns[c - 1] : null, adjtestid + "getPrevious");
181 is(column.getNext(), c < columns.length - 1 ? columns[c + 1] : null, adjtestid + "getNext");
182
183 // check the view's getColumnProperties method
184 var properties = tree.view.getColumnProperties(column);
185 var expectedProperties = expectedColumn.properties;
186 is(properties, expectedProperties ? expectedProperties : "", adjtestid + "getColumnProperties");
187 }
188
189 is(columns.getFirstColumn(), columns[0], testid + "getFirstColumn");
190 is(columns.getLastColumn(), columns[columns.length - 1], testid + "getLastColumn");
191 is(columns.getPrimaryColumn(), primary, testid + "getPrimaryColumn");
192 is(columns.getSortedColumn(), sorted, testid + "getSortedColumn");
193 is(columns.getKeyColumn(), key, testid + "getKeyColumn");
194
195 is(columns.getColumnAt(-1), null, testid + "getColumnAt under");
196 is(columns.getColumnAt(columns.length), null, testid + "getColumnAt over");
197 is(columns.getNamedColumn(""), null, testid + "getNamedColumn null");
198 is(columns.getNamedColumn("unknown"), null, testid + "getNamedColumn unknown");
199 is(columns.getColumnFor(null), null, testid + "getColumnFor null");
200 is(columns.getColumnFor(tree), null, testid + "getColumnFor other");
201 }
202
203 function testtag_tree_TreeSelection(tree, testid, multiple)
204 {
205 testid += " selection ";
206
207 var selection = tree.view.selection;
208 is(selection instanceof Components.interfaces.nsITreeSelection, true,
209 testid + "selection is a TreeSelection");
210 is(selection.single, !multiple, testid + "single");
211
212 testtag_tree_TreeSelection_State(tree, testid + "initial", -1, []);
213 is(selection.shiftSelectPivot, -1, testid + "initial shiftSelectPivot");
214
215 selection.currentIndex = 2;
216 testtag_tree_TreeSelection_State(tree, testid + "set currentIndex", 2, []);
217 tree.currentIndex = 3;
218 testtag_tree_TreeSelection_State(tree, testid + "set tree.currentIndex", 3, []);
219
220 // test the select() method, which should deselect all rows and select
221 // a single row
222 selection.select(1);
223 testtag_tree_TreeSelection_State(tree, testid + "select 1", 1, [1]);
224 selection.select(3);
225 testtag_tree_TreeSelection_State(tree, testid + "select 2", 3, [3]);
226 selection.select(3);
227 testtag_tree_TreeSelection_State(tree, testid + "select same", 3, [3]);
228
229 selection.currentIndex = 1;
230 testtag_tree_TreeSelection_State(tree, testid + "set currentIndex with single selection", 1, [3]);
231
232 tree.currentIndex = 2;
233 testtag_tree_TreeSelection_State(tree, testid + "set tree.currentIndex with single selection", 2, [3]);
234
235 // check the toggleSelect method. In single selection mode, it only toggles on when
236 // there isn't currently a selection.
237 selection.toggleSelect(2);
238 testtag_tree_TreeSelection_State(tree, testid + "toggleSelect 1", 2, multiple ? [2, 3] : [3]);
239 selection.toggleSelect(2);
240 selection.toggleSelect(3);
241 testtag_tree_TreeSelection_State(tree, testid + "toggleSelect 2", 3, []);
242
243 // the current index doesn't change after a selectAll, so it should still be set to 1
244 // selectAll has no effect on single selection trees
245 selection.currentIndex = 1;
246 selection.selectAll();
247 testtag_tree_TreeSelection_State(tree, testid + "selectAll 1", 1, multiple ? [0, 1, 2, 3, 4, 5, 6 , 7] : []);
248 selection.toggleSelect(2);
249 testtag_tree_TreeSelection_State(tree, testid + "toggleSelect after selectAll", 2,
250 multiple ? [0, 1, 3, 4, 5, 6, 7] : [2]);
251 selection.clearSelection();
252 testtag_tree_TreeSelection_State(tree, testid + "clearSelection", 2, []);
253 selection.toggleSelect(3);
254 selection.toggleSelect(1);
255 if (multiple) {
256 selection.selectAll();
257 testtag_tree_TreeSelection_State(tree, testid + "selectAll 2", 1, [0, 1, 2, 3, 4, 5, 6, 7]);
258 }
259 selection.currentIndex = 2;
260 selection.clearSelection();
261 testtag_tree_TreeSelection_State(tree, testid + "clearSelection after selectAll", 2, []);
262
263 // XXXndeakin invertSelection isn't implemented
264 // selection.invertSelection();
265
266 is(selection.shiftSelectPivot, -1, testid + "shiftSelectPivot set to -1");
267
268 // rangedSelect and clearRange set the currentIndex to the endIndex. The
269 // shiftSelectPivot property will be set to startIndex.
270 selection.rangedSelect(1, 3, false);
271 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect no augment",
272 multiple ? 3 : 2, multiple ? [1, 2, 3] : []);
273 is(selection.shiftSelectPivot, multiple ? 1 : -1,
274 testid + "shiftSelectPivot after rangedSelect no augment");
275 if (multiple) {
276 selection.select(1);
277 selection.rangedSelect(0, 2, true);
278 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment", 2, [0, 1, 2]);
279 is(selection.shiftSelectPivot, 0, testid + "shiftSelectPivot after rangedSelect augment");
280
281 selection.clearRange(1, 3);
282 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment", 3, [0]);
283
284 // check that rangedSelect can take a start value higher than end
285 selection.rangedSelect(3, 1, false);
286 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect reverse", 1, [1, 2, 3]);
287 is(selection.shiftSelectPivot, 3, testid + "shiftSelectPivot after rangedSelect reverse");
288
289 // check that setting the current index doesn't change the selection
290 selection.currentIndex = 0;
291 testtag_tree_TreeSelection_State(tree, testid + "currentIndex with range selection", 0, [1, 2, 3]);
292 }
293
294 // both values of rangedSelect may be the same
295 selection.rangedSelect(2, 2, false);
296 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect one row", 2, [2]);
297 is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot after selecting one row");
298
299 if (multiple) {
300 selection.rangedSelect(2, 3, true);
301
302 // a start index of -1 means from the last point
303 selection.rangedSelect(-1, 0, true);
304 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect -1 existing selection", 0, [0, 1, 2, 3]);
305 is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot after -1 existing selection");
306
307 selection.currentIndex = 2;
308 selection.rangedSelect(-1, 0, false);
309 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect -1 from currentIndex", 0, [0, 1, 2]);
310 is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot -1 from currentIndex");
311 }
312
313 // XXXndeakin need to test out of range values but these don't work properly
314 /*
315 selection.select(-1);
316 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment -1", -1, []);
317
318 selection.select(8);
319 testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment 8", 3, [0]);
320 */
321 }
322
323 function testtag_tree_TreeSelection_UI(tree, testid, multiple)
324 {
325 testid += " selection UI ";
326
327 var selection = tree.view.selection;
328 selection.clearSelection();
329 selection.currentIndex = 0;
330 tree.focus();
331
332 var keydownFired = 0;
333 var keypressFired = 0;
334 function keydownListener(event)
335 {
336 keydownFired++;
337 }
338 function keypressListener(event) {
339 keypressFired++;
340 }
341
342 // check that cursor up and down keys navigate up and down
343 // select event fires after a delay so don't expect it. The reason it fires after a delay
344 // is so that cursor navigation allows quicking skimming over a set of items without
345 // actually firing events in-between, improving performance. The select event will only
346 // be fired on the row where the cursor stops.
347 window.addEventListener("keydown", keydownListener, false);
348 window.addEventListener("keypress", keypressListener, false);
349
350 synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down");
351 testtag_tree_TreeSelection_State(tree, testid + "key down", 1, [1], 0);
352
353 synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up");
354 testtag_tree_TreeSelection_State(tree, testid + "key up", 0, [0], 0);
355
356 synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up at start");
357 testtag_tree_TreeSelection_State(tree, testid + "key up at start", 0, [0], 0);
358
359 // pressing down while the last row is selected should not fire a select event,
360 // as the selection won't have changed. Also the view is not scrolled in this case.
361 selection.select(7);
362 synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down at end");
363 testtag_tree_TreeSelection_State(tree, testid + "key down at end", 7, [7], 0);
364
365 // pressing keys while at the edge of the visible rows should scroll the list
366 tree.treeBoxObject.scrollToRow(4);
367 selection.select(4);
368 synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up with scroll");
369 is(tree.treeBoxObject.getFirstVisibleRow(), 3, testid + "key up with scroll");
370
371 tree.treeBoxObject.scrollToRow(0);
372 selection.select(3);
373 synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down with scroll");
374 is(tree.treeBoxObject.getFirstVisibleRow(), 1, testid + "key down with scroll");
375
376 // accel key and cursor movement adjust currentIndex but should not change
377 // the selection. In single selection mode, the selection will not change,
378 // but instead will just scroll up or down a line
379 tree.treeBoxObject.scrollToRow(0);
380 selection.select(1);
381 synthesizeKeyExpectEvent("VK_DOWN", { accelKey: true }, tree, "!select", "key down with accel");
382 testtag_tree_TreeSelection_State(tree, testid + "key down with accel", multiple ? 2 : 1, [1]);
383 if (!multiple)
384 is(tree.treeBoxObject.getFirstVisibleRow(), 1, testid + "key down with accel and scroll");
385
386 tree.treeBoxObject.scrollToRow(4);
387 selection.select(4);
388 synthesizeKeyExpectEvent("VK_UP", { accelKey: true }, tree, "!select", "key up with accel");
389 testtag_tree_TreeSelection_State(tree, testid + "key up with accel", multiple ? 3 : 4, [4]);
390 if (!multiple)
391 is(tree.treeBoxObject.getFirstVisibleRow(), 3, testid + "key up with accel and scroll");
392
393 // do this three times, one for each state of pageUpOrDownMovesSelection,
394 // and then once with the accel key pressed
395 for (var t = 0; t < 3; t++) {
396 var testidmod = (t == 2) ? " with accel" : (t == 1) ? " rev" : "";
397 var keymod = (t == 2) ? { accelKey: true } : { };
398
399 var moveselection = tree.pageUpOrDownMovesSelection;
400 if (t == 2)
401 moveselection = !moveselection;
402
403 tree.treeBoxObject.scrollToRow(4);
404 selection.currentIndex = 6;
405 selection.select(6);
406 var expected = moveselection ? 4 : 6;
407 synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up");
408 testtag_tree_TreeSelection_State(tree, testid + "key page up" + testidmod,
409 expected, [expected], moveselection ? 4 : 0);
410
411 expected = moveselection ? 0 : 6;
412 synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up again");
413 testtag_tree_TreeSelection_State(tree, testid + "key page up again" + testidmod,
414 expected, [expected], 0);
415
416 expected = moveselection ? 0 : 6;
417 synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up at start");
418 testtag_tree_TreeSelection_State(tree, testid + "key page up at start" + testidmod,
419 expected, [expected], 0);
420
421 tree.treeBoxObject.scrollToRow(0);
422 selection.currentIndex = 1;
423 selection.select(1);
424 expected = moveselection ? 3 : 1;
425 synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down");
426 testtag_tree_TreeSelection_State(tree, testid + "key page down" + testidmod,
427 expected, [expected], moveselection ? 0 : 4);
428
429 expected = moveselection ? 7 : 1;
430 synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down again");
431 testtag_tree_TreeSelection_State(tree, testid + "key page down again" + testidmod,
432 expected, [expected], 4);
433
434 expected = moveselection ? 7 : 1;
435 synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down at start");
436 testtag_tree_TreeSelection_State(tree, testid + "key page down at start" + testidmod,
437 expected, [expected], 4);
438
439 if (t < 2)
440 tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
441 }
442
443 tree.treeBoxObject.scrollToRow(4);
444 selection.select(6);
445 synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home");
446 testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0);
447
448 tree.treeBoxObject.scrollToRow(0);
449 selection.select(1);
450 synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end");
451 testtag_tree_TreeSelection_State(tree, testid + "key end", 7, [7], 4);
452
453 // in single selection mode, the selection doesn't change in this case
454 tree.treeBoxObject.scrollToRow(4);
455 selection.select(6);
456 synthesizeKeyExpectEvent("VK_HOME", { accelKey: true }, tree, "!select", "key home with accel");
457 testtag_tree_TreeSelection_State(tree, testid + "key home with accel", multiple ? 0 : 6, [6], 0);
458
459 tree.treeBoxObject.scrollToRow(0);
460 selection.select(1);
461 synthesizeKeyExpectEvent("VK_END", { accelKey: true }, tree, "!select", "key end with accel");
462 testtag_tree_TreeSelection_State(tree, testid + "key end with accel", multiple ? 7 : 1, [1], 4);
463
464 // next, test cursor navigation with selection. Here the select event will be fired
465 selection.select(1);
466 var eventExpected = multiple ? "select" : "!select";
467 synthesizeKeyExpectEvent("VK_DOWN", { shiftKey: true }, tree, eventExpected, "key shift down to select");
468 testtag_tree_TreeSelection_State(tree, testid + "key shift down to select",
469 multiple ? 2 : 1, multiple ? [1, 2] : [1]);
470 is(selection.shiftSelectPivot, multiple ? 1 : -1,
471 testid + "key shift down to select shiftSelectPivot");
472 synthesizeKeyExpectEvent("VK_UP", { shiftKey: true }, tree, eventExpected, "key shift up to unselect");
473 testtag_tree_TreeSelection_State(tree, testid + "key shift up to unselect", 1, [1]);
474 is(selection.shiftSelectPivot, multiple ? 1 : -1,
475 testid + "key shift up to unselect shiftSelectPivot");
476 if (multiple) {
477 synthesizeKeyExpectEvent("VK_UP", { shiftKey: true }, tree, "select", "key shift up to select");
478 testtag_tree_TreeSelection_State(tree, testid + "key shift up to select", 0, [0, 1]);
479 is(selection.shiftSelectPivot, 1, testid + "key shift up to select shiftSelectPivot");
480 synthesizeKeyExpectEvent("VK_DOWN", { shiftKey: true }, tree, "select", "key shift down to unselect");
481 testtag_tree_TreeSelection_State(tree, testid + "key shift down to unselect", 1, [1]);
482 is(selection.shiftSelectPivot, 1, testid + "key shift down to unselect shiftSelectPivot");
483 }
484
485 // do this twice, one for each state of pageUpOrDownMovesSelection, however
486 // when selecting with the shift key, pageUpOrDownMovesSelection is ignored
487 // and the selection always changes
488 var lastidx = tree.view.rowCount - 1;
489 for (var t = 0; t < 2; t++) {
490 var testidmod = (t == 0) ? "" : " rev";
491
492 // If the top or bottom visible row is the current row, pressing shift and
493 // page down / page up selects one page up or one page down. Otherwise, the
494 // selection is made to the top or bottom of the visible area.
495 tree.treeBoxObject.scrollToRow(lastidx - 3);
496 selection.currentIndex = 6;
497 selection.select(6);
498 synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, eventExpected, "key shift page up");
499 testtag_tree_TreeSelection_State(tree, testid + "key shift page up" + testidmod,
500 multiple ? 4 : 6, multiple ? [4, 5, 6] : [6]);
501 if (multiple) {
502 synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "select", "key shift page up again");
503 testtag_tree_TreeSelection_State(tree, testid + "key shift page up again" + testidmod,
504 0, [0, 1, 2, 3, 4, 5, 6]);
505 // no change in the selection, so no select event should be fired
506 synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "!select", "key shift page up at start");
507 testtag_tree_TreeSelection_State(tree, testid + "key shift page up at start" + testidmod,
508 0, [0, 1, 2, 3, 4, 5, 6]);
509 // deselect by paging down again
510 synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "select", "key shift page down deselect");
511 testtag_tree_TreeSelection_State(tree, testid + "key shift page down deselect" + testidmod,
512 3, [3, 4, 5, 6]);
513 }
514
515 tree.treeBoxObject.scrollToRow(1);
516 selection.currentIndex = 2;
517 selection.select(2);
518 synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, eventExpected, "key shift page down");
519 testtag_tree_TreeSelection_State(tree, testid + "key shift page down" + testidmod,
520 multiple ? 4 : 2, multiple ? [2, 3, 4] : [2]);
521 if (multiple) {
522 synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "select", "key shift page down again");
523 testtag_tree_TreeSelection_State(tree, testid + "key shift page down again" + testidmod,
524 7, [2, 3, 4, 5, 6, 7]);
525 synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "!select", "key shift page down at start");
526 testtag_tree_TreeSelection_State(tree, testid + "key shift page down at start" + testidmod,
527 7, [2, 3, 4, 5, 6, 7]);
528 synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "select", "key shift page up deselect");
529 testtag_tree_TreeSelection_State(tree, testid + "key shift page up deselect" + testidmod,
530 4, [2, 3, 4]);
531 }
532
533 // test when page down / page up is pressed when the view is scrolled such
534 // that the selection is not visible
535 if (multiple) {
536 tree.treeBoxObject.scrollToRow(3);
537 selection.currentIndex = 1;
538 selection.select(1);
539 synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, eventExpected,
540 "key shift page down with view scrolled down");
541 testtag_tree_TreeSelection_State(tree, testid + "key shift page down with view scrolled down" + testidmod,
542 6, [1, 2, 3, 4, 5, 6], 3);
543
544 tree.treeBoxObject.scrollToRow(2);
545 selection.currentIndex = 6;
546 selection.select(6);
547 synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, eventExpected,
548 "key shift page up with view scrolled up");
549 testtag_tree_TreeSelection_State(tree, testid + "key shift page up with view scrolled up" + testidmod,
550 2, [2, 3, 4, 5, 6], 2);
551
552 tree.treeBoxObject.scrollToRow(2);
553 selection.currentIndex = 0;
554 selection.select(0);
555 // don't expect the select event, as the selection won't have changed
556 synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "!select",
557 "key shift page up at start with view scrolled down");
558 testtag_tree_TreeSelection_State(tree, testid + "key shift page up at start with view scrolled down" + testidmod,
559 0, [0], 0);
560
561 tree.treeBoxObject.scrollToRow(0);
562 selection.currentIndex = 7;
563 selection.select(7);
564 // don't expect the select event, as the selection won't have changed
565 synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "!select",
566 "key shift page down at end with view scrolled up");
567 testtag_tree_TreeSelection_State(tree, testid + "key shift page down at end with view scrolled up" + testidmod,
568 7, [7], 4);
569 }
570
571 tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
572 }
573
574 tree.treeBoxObject.scrollToRow(4);
575 selection.select(5);
576 synthesizeKeyExpectEvent("VK_HOME", { shiftKey: true }, tree, eventExpected, "key shift home");
577 testtag_tree_TreeSelection_State(tree, testid + "key shift home",
578 multiple ? 0 : 5, multiple ? [0, 1, 2, 3, 4, 5] : [5], multiple ? 0 : 4);
579
580 tree.treeBoxObject.scrollToRow(0);
581 selection.select(3);
582 synthesizeKeyExpectEvent("VK_END", { shiftKey: true }, tree, eventExpected, "key shift end");
583 testtag_tree_TreeSelection_State(tree, testid + "key shift end",
584 multiple ? 7 : 3, multiple ? [3, 4, 5, 6, 7] : [3], multiple ? 4 : 0);
585
586 // pressing space selects a row, pressing accel + space unselects a row
587 selection.select(2);
588 selection.currentIndex = 4;
589 synthesizeKeyExpectEvent(" ", {}, tree, "select", "key space on");
590 // in single selection mode, space shouldn't do anything
591 testtag_tree_TreeSelection_State(tree, testid + "key space on", 4, multiple ? [2, 4] : [2]);
592
593 if (multiple) {
594 synthesizeKeyExpectEvent(" ", { accelKey: true }, tree, "select", "key space off");
595 testtag_tree_TreeSelection_State(tree, testid + "key space off", 4, [2]);
596 }
597
598 // check that clicking on a row selects it
599 tree.treeBoxObject.scrollToRow(0);
600 selection.select(2);
601 selection.currentIndex = 2;
602 if (0) { // XXXndeakin disable these tests for now
603 mouseOnCell(tree, 1, tree.columns[1], "mouse on row");
604 testtag_tree_TreeSelection_State(tree, testid + "mouse on row", 1, [1], 0,
605 tree.selType == "cell" ? tree.columns[1] : null);
606 }
607
608 // restore the scroll position to the start of the page
609 sendKey("HOME");
610
611 window.removeEventListener("keydown", keydownListener, false);
612 window.removeEventListener("keypress", keypressListener, false);
613 is(keydownFired, multiple ? 63 : 40, "keydown event wasn't fired properly");
614 is(keypressFired, multiple ? 2 : 1, "keypress event wasn't fired properly");
615 }
616
617 function testtag_tree_UI_editing(tree, testid, rowInfo)
618 {
619 testid += " editing UI ";
620
621 // check editing UI
622 var ecolumn = tree.columns[0];
623 var rowIndex = 2;
624 var inputField = tree.inputField;
625
626 // temporary make the tree editable to test mouse double click
627 var wasEditable = tree.editable;
628 if (!wasEditable)
629 tree.editable = true;
630
631 // if this is a container save its current open status
632 var row = rowInfo.rows[rowIndex];
633 var wasOpen = null;
634 if (tree.view.isContainer(row))
635 wasOpen = tree.view.isContainerOpen(row);
636
637 // Test whether a keystroke can enter text entry, and another can exit.
638 if (tree.selType == "cell")
639 {
640 tree.stopEditing(false);
641 ok(!tree.editingColumn, "Should not be editing tree cell now");
642 tree.view.selection.currentColumn = ecolumn;
643 tree.currentIndex = rowIndex;
644
645 const isMac = (navigator.platform.indexOf("Mac") >= 0);
646 const StartEditingKey = isMac ? "RETURN" : "F2";
647 sendKey(StartEditingKey);
648 is(tree.editingColumn, ecolumn, "Should be editing tree cell now");
649 sendKey("ESCAPE");
650 ok(!tree.editingColumn, "Should not be editing tree cell now");
651 is(tree.currentIndex, rowIndex, "Current index should not have changed");
652 is(tree.view.selection.currentColumn, ecolumn, "Current column should not have changed");
653 }
654
655 mouseDblClickOnCell(tree, rowIndex, ecolumn, testid + "edit on double click");
656 is(tree.editingColumn, ecolumn, testid + "editing column");
657 is(tree.editingRow, rowIndex, testid + "editing row");
658
659 // ensure that we don't expand an expandable container on edit
660 if (wasOpen != null)
661 is(tree.view.isContainerOpen(row), wasOpen, testid + "opened container node on edit");
662
663 // ensure to restore editable attribute
664 if (!wasEditable)
665 tree.editable = false;
666
667 var ci = tree.currentIndex;
668
669 // cursor navigation should not change the selection while editing
670 var testKey = function(key) {
671 synthesizeKeyExpectEvent(key, {}, tree, "!select", "key " + key + " with editing");
672 is(tree.editingRow == rowIndex && tree.editingColumn == ecolumn && tree.currentIndex == ci,
673 true, testid + "key " + key + " while editing");
674 }
675
676 testKey("VK_DOWN");
677 testKey("VK_UP");
678 testKey("VK_PAGE_DOWN");
679 testKey("VK_PAGE_UP");
680 testKey("VK_HOME");
681 testKey("VK_END");
682
683 // XXXndeakin figure out how to send characters to the textbox
684 // inputField.inputField.focus()
685 // synthesizeKeyExpectEvent(inputField.inputField, "b", null, "");
686 // tree.stopEditing(true);
687 // is(tree.view.getCellText(0, ecolumn), "b", testid + "edit cell");
688
689 // Restore initial state.
690 tree.stopEditing(false);
691 }
692
693 function testtag_tree_TreeSelection_UI_cell(tree, testid, rowInfo)
694 {
695 testid += " selection UI cell ";
696
697 var columns = tree.columns;
698 var firstcolumn = columns[0];
699 var secondcolumn = columns[1];
700 var lastcolumn = columns[columns.length - 1];
701 var secondlastcolumn = columns[columns.length - 2];
702 var selection = tree.view.selection;
703
704 selection.clearSelection();
705 selection.currentIndex = -1;
706 selection.currentColumn = firstcolumn;
707 is(selection.currentColumn, firstcolumn, testid + " first currentColumn");
708
709 // no selection yet so nothing should happen when the left and right cursor keys are pressed
710 synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right no selection");
711 testtag_tree_TreeSelection_State(tree, testid + "key right no selection", -1, [], null, firstcolumn);
712
713 selection.currentColumn = secondcolumn;
714 synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left no selection");
715 testtag_tree_TreeSelection_State(tree, testid + "key left no selection", -1, [], null, secondcolumn);
716
717 selection.select(2);
718 selection.currentIndex = 2;
719 if (0) { // XXXndeakin disable these tests for now
720 mouseOnCell(tree, 1, secondlastcolumn, "mouse on cell");
721 testtag_tree_TreeSelection_State(tree, testid + "mouse on cell", 1, [1], null, secondlastcolumn);
722 }
723
724 tree.focus();
725
726 // selection is set, so it should move when the left and right cursor keys are pressed
727 tree.treeBoxObject.scrollToRow(0);
728 selection.select(1);
729 selection.currentIndex = 1;
730 selection.currentColumn = secondcolumn;
731 synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left in second column");
732 testtag_tree_TreeSelection_State(tree, testid + "key left in second column", 1, [1], 0, firstcolumn);
733
734 synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left in first column");
735 testtag_tree_TreeSelection_State(tree, testid + "key left in first column", 1, [1], 0, firstcolumn);
736
737 selection.currentColumn = secondlastcolumn;
738 synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right in second last column");
739 testtag_tree_TreeSelection_State(tree, testid + "key right in second last column", 1, [1], 0, lastcolumn);
740
741 synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right in last column");
742 testtag_tree_TreeSelection_State(tree, testid + "key right in last column", 1, [1], 0, lastcolumn);
743
744 synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up in second row");
745 testtag_tree_TreeSelection_State(tree, testid + "key up in second row", 0, [0], 0, lastcolumn);
746
747 synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up in first row");
748 testtag_tree_TreeSelection_State(tree, testid + "key up in first row", 0, [0], 0, lastcolumn);
749
750 synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down in first row");
751 testtag_tree_TreeSelection_State(tree, testid + "key down in first row", 1, [1], 0, lastcolumn);
752
753 var lastidx = tree.view.rowCount - 1;
754 tree.treeBoxObject.scrollToRow(lastidx - 3);
755 selection.select(lastidx);
756 selection.currentIndex = lastidx;
757 synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down in last row");
758 testtag_tree_TreeSelection_State(tree, testid + "key down in last row", lastidx, [lastidx], lastidx - 3, lastcolumn);
759
760 synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home");
761 testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0, lastcolumn);
762
763 synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end");
764 testtag_tree_TreeSelection_State(tree, testid + "key end", lastidx, [lastidx], lastidx - 3, lastcolumn);
765
766 for (var t = 0; t < 2; t++) {
767 var testidmod = (t == 0) ? "" : " rev";
768
769 // scroll to the end, subtract 3 because we want lastidx to appear
770 // at the end of view
771 tree.treeBoxObject.scrollToRow(lastidx - 3);
772 selection.select(lastidx);
773 selection.currentIndex = lastidx;
774 var expectedrow = tree.pageUpOrDownMovesSelection ? lastidx - 3 : lastidx;
775 synthesizeKeyExpectEvent("VK_PAGE_UP", {}, tree, "!select", "key page up");
776 testtag_tree_TreeSelection_State(tree, testid + "key page up" + testidmod,
777 expectedrow, [expectedrow],
778 tree.pageUpOrDownMovesSelection ? lastidx - 3 : 0, lastcolumn);
779
780 tree.treeBoxObject.scrollToRow(1);
781 selection.select(1);
782 selection.currentIndex = 1;
783 var expectedrow = tree.pageUpOrDownMovesSelection ? 4 : 1;
784 synthesizeKeyExpectEvent("VK_PAGE_DOWN", {}, tree, "!select", "key page down");
785 testtag_tree_TreeSelection_State(tree, testid + "key page down" + testidmod,
786 expectedrow, [expectedrow],
787 tree.pageUpOrDownMovesSelection ? 1 : lastidx - 3, lastcolumn);
788
789 tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
790 }
791
792 // now check navigation when there is unselctable column
793 secondcolumn.element.setAttribute("selectable", "false");
794 secondcolumn.invalidate();
795 is(secondcolumn.selectable, false, testid + "set selectable attribute");
796
797 if (columns.length >= 3) {
798 selection.select(3);
799 selection.currentIndex = 3;
800 // check whether unselectable columns are skipped over
801 selection.currentColumn = firstcolumn;
802 synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable column");
803 testtag_tree_TreeSelection_State(tree, testid + "key right unselectable column",
804 3, [3], null, secondcolumn.getNext());
805
806 synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable column");
807 testtag_tree_TreeSelection_State(tree, testid + "key left unselectable column",
808 3, [3], null, firstcolumn);
809 }
810
811 secondcolumn.element.removeAttribute("selectable");
812 secondcolumn.invalidate();
813 is(secondcolumn.selectable, true, testid + "clear selectable attribute");
814
815 // check to ensure that navigation isn't allowed if the first column is not selectable
816 selection.currentColumn = secondcolumn;
817 firstcolumn.element.setAttribute("selectable", "false");
818 firstcolumn.invalidate();
819 synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable first column");
820 testtag_tree_TreeSelection_State(tree, testid + "key left unselectable first column",
821 3, [3], null, secondcolumn);
822 firstcolumn.element.removeAttribute("selectable");
823 firstcolumn.invalidate();
824
825 // check to ensure that navigation isn't allowed if the last column is not selectable
826 selection.currentColumn = secondlastcolumn;
827 lastcolumn.element.setAttribute("selectable", "false");
828 lastcolumn.invalidate();
829 synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable last column");
830 testtag_tree_TreeSelection_State(tree, testid + "key right unselectable last column",
831 3, [3], null, secondlastcolumn);
832 lastcolumn.element.removeAttribute("selectable");
833 lastcolumn.invalidate();
834
835 // now check for cells with selectable false
836 if (!rowInfo.rows[4].cells[1].selectable && columns.length >= 3) {
837 // check whether unselectable cells are skipped over
838 selection.select(4);
839 selection.currentIndex = 4;
840
841 selection.currentColumn = firstcolumn;
842 synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable cell");
843 testtag_tree_TreeSelection_State(tree, testid + "key right unselectable cell",
844 4, [4], null, secondcolumn.getNext());
845
846 synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable cell");
847 testtag_tree_TreeSelection_State(tree, testid + "key left unselectable cell",
848 4, [4], null, firstcolumn);
849
850 tree.treeBoxObject.scrollToRow(1);
851 selection.select(3);
852 selection.currentIndex = 3;
853 selection.currentColumn = secondcolumn;
854
855 synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down unselectable cell");
856 testtag_tree_TreeSelection_State(tree, testid + "key down unselectable cell",
857 5, [5], 2, secondcolumn);
858
859 tree.treeBoxObject.scrollToRow(4);
860 synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up unselectable cell");
861 testtag_tree_TreeSelection_State(tree, testid + "key up unselectable cell",
862 3, [3], 3, secondcolumn);
863 }
864
865 // restore the scroll position to the start of the page
866 sendKey("HOME");
867 }
868
869 function testtag_tree_TreeView(tree, testid, rowInfo)
870 {
871 testid += " view ";
872
873 var columns = tree.columns;
874 var view = tree.view;
875
876 is(view instanceof Components.interfaces.nsITreeView, true, testid + "view is a TreeView");
877 is(view.rowCount, rowInfo.rows.length, testid + "rowCount");
878
879 testtag_tree_TreeView_rows(tree, testid, rowInfo, 0);
880
881 // note that this will only work for content trees currently
882 view.setCellText(0, columns[1], "Changed Value");
883 is(view.getCellText(0, columns[1]), "Changed Value", "setCellText");
884
885 view.setCellValue(1, columns[0], "Another Changed Value");
886 is(view.getCellValue(1, columns[0]), "Another Changed Value", "setCellText");
887 }
888
889 function testtag_tree_TreeView_rows(tree, testid, rowInfo, startRow)
890 {
891 var r;
892 var columns = tree.columns;
893 var view = tree.view;
894 var length = rowInfo.rows.length;
895
896 // methods to test along with the functions which determine the expected value
897 var checkRowMethods =
898 {
899 isContainer: function(row) { return row.container },
900 isContainerOpen: function(row) { return false },
901 isContainerEmpty: function(row) { return (row.children != null && row.children.rows.length == 0) },
902 isSeparator: function(row) { return row.separator },
903 getRowProperties: function(row) { return row.properties },
904 getLevel: function(row) { return row.level },
905 getParentIndex: function(row) { return row.parent },
906 hasNextSibling: function(row) { return r < startRow + length - 1; }
907 };
908
909 var checkCellMethods =
910 {
911 getCellText: function(row, cell) { return cell.label },
912 getCellValue: function(row, cell) { return cell.value },
913 getCellProperties: function(row, cell) { return cell.properties },
914 isEditable: function(row, cell) { return cell.editable },
915 isSelectable: function(row, cell) { return cell.selectable },
916 getImageSrc: function(row, cell) { return cell.image },
917 getProgressMode: function(row, cell) { return cell.mode }
918 };
919
920 var failedMethods = { };
921 var checkMethod, actual, expected;
922 var containerInfo = null;
923 var toggleOpenStateOK = true;
924
925 for (r = startRow; r < length; r++) {
926 var row = rowInfo.rows[r];
927 for (var c = 0; c < row.cells.length; c++) {
928 var cell = row.cells[c];
929
930 for (checkMethod in checkCellMethods) {
931 expected = checkCellMethods[checkMethod](row, cell);
932 actual = view[checkMethod](r, columns[c]);
933 if (actual !== expected) {
934 failedMethods[checkMethod] = true;
935 is(actual, expected, testid + "row " + r + " column " + c + " " + checkMethod + " is incorrect");
936 }
937 }
938 }
939
940 // compare row properties
941 for (checkMethod in checkRowMethods) {
942 expected = checkRowMethods[checkMethod](row, r);
943 if (checkMethod == "hasNextSibling") {
944 actual = view[checkMethod](r, r);
945 }
946 else {
947 actual = view[checkMethod](r);
948 }
949 if (actual !== expected) {
950 failedMethods[checkMethod] = true;
951 is(actual, expected, testid + "row " + r + " " + checkMethod + " is incorrect");
952 }
953 }
954 /*
955 // open and recurse into containers
956 if (row.container) {
957 view.toggleOpenState(r);
958 if (!view.isContainerOpen(r)) {
959 toggleOpenStateOK = false;
960 is(view.isContainerOpen(r), true, testid + "row " + r + " toggleOpenState open");
961 }
962 testtag_tree_TreeView_rows(tree, testid + "container " + r + " ", row.children, r + 1);
963 view.toggleOpenState(r);
964 if (view.isContainerOpen(r)) {
965 toggleOpenStateOK = false;
966 is(view.isContainerOpen(r), false, testid + "row " + r + " toggleOpenState close");
967 }
968 }
969 */
970 }
971
972 for (var failedMethod in failedMethods) {
973 if (failedMethod in checkRowMethods)
974 delete checkRowMethods[failedMethod];
975 if (failedMethod in checkCellMethods)
976 delete checkCellMethods[failedMethod];
977 }
978
979 for (checkMethod in checkRowMethods)
980 is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod);
981 for (checkMethod in checkCellMethods)
982 is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod);
983 if (toggleOpenStateOK)
984 is("toggleOpenState ok", "toggleOpenState ok", testid + "toggleOpenState");
985 }
986
987 function testtag_tree_TreeView_rows_sort(tree, testid, rowInfo)
988 {
989 // check if cycleHeader sorts the columns
990 var columnIndex = 0;
991 var view = tree.view;
992 var column = tree.columns[columnIndex];
993 var columnElement = column.element;
994 var sortkey = columnElement.getAttribute("sort");
995 if (sortkey) {
996 view.cycleHeader(column);
997 is(tree.getAttribute("sort"), sortkey, "cycleHeader sort");
998 is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending");
999 is(columnElement.getAttribute("sortDirection"), "ascending", "cycleHeader column sortDirection");
1000 is(columnElement.getAttribute("sortActive"), "true", "cycleHeader column sortActive");
1001 view.cycleHeader(column);
1002 is(tree.getAttribute("sortDirection"), "descending", "cycleHeader sortDirection descending");
1003 is(columnElement.getAttribute("sortDirection"), "descending", "cycleHeader column sortDirection descending");
1004 view.cycleHeader(column);
1005 is(tree.getAttribute("sortDirection"), "", "cycleHeader sortDirection natural");
1006 is(columnElement.getAttribute("sortDirection"), "", "cycleHeader column sortDirection natural");
1007 // XXXndeakin content view isSorted needs to be tested
1008 }
1009
1010 // Check that clicking on column header sorts the column.
1011 var columns = getSortedColumnArray(tree);
1012 is(columnElement.getAttribute("sortDirection"), "",
1013 "cycleHeader column sortDirection");
1014
1015 // Click once on column header and check sorting has cycled once.
1016 mouseClickOnColumnHeader(columns, columnIndex, 0, 1);
1017 is(columnElement.getAttribute("sortDirection"), "ascending",
1018 "single click cycleHeader column sortDirection ascending");
1019
1020 // Now simulate a double click.
1021 mouseClickOnColumnHeader(columns, columnIndex, 0, 2);
1022 if (navigator.platform.indexOf("Win") == 0) {
1023 // Windows cycles only once on double click.
1024 is(columnElement.getAttribute("sortDirection"), "descending",
1025 "double click cycleHeader column sortDirection descending");
1026 // 1 single clicks should restore natural sorting.
1027 mouseClickOnColumnHeader(columns, columnIndex, 0, 1);
1028 }
1029
1030 // Check we have gone back to natural sorting.
1031 is(columnElement.getAttribute("sortDirection"), "",
1032 "cycleHeader column sortDirection");
1033
1034 columnElement.setAttribute("sorthints", "twostate");
1035 view.cycleHeader(column);
1036 is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending twostate");
1037 view.cycleHeader(column);
1038 is(tree.getAttribute("sortDirection"), "descending", "cycleHeader sortDirection ascending twostate");
1039 view.cycleHeader(column);
1040 is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending twostate again");
1041 columnElement.removeAttribute("sorthints");
1042 view.cycleHeader(column);
1043 view.cycleHeader(column);
1044
1045 is(columnElement.getAttribute("sortDirection"), "",
1046 "cycleHeader column sortDirection reset");
1047 }
1048
1049 // checks if the current and selected rows are correct
1050 // current is the index of the current row
1051 // selected is an array of the indicies of the selected rows
1052 // column is the selected column
1053 // viewidx is the row that should be visible at the top of the tree
1054 function testtag_tree_TreeSelection_State(tree, testid, current, selected, viewidx, column)
1055 {
1056 var selection = tree.view.selection;
1057
1058 if (!column)
1059 column = (tree.selType == "cell") ? tree.columns[0] : null;
1060
1061 is(selection.count, selected.length, testid + " count");
1062 is(tree.currentIndex, current, testid + " currentIndex");
1063 is(selection.currentIndex, current, testid + " TreeSelection currentIndex");
1064 is(selection.currentColumn, column, testid + " currentColumn");
1065 if (viewidx !== null && viewidx !== undefined)
1066 is(tree.treeBoxObject.getFirstVisibleRow(), viewidx, testid + " first visible row");
1067
1068 var actualSelected = [];
1069 var count = tree.view.rowCount;
1070 for (var s = 0; s < count; s++) {
1071 if (selection.isSelected(s))
1072 actualSelected.push(s);
1073 }
1074
1075 is(compareArrays(selected, actualSelected), true, testid + " selection [" + selected + "]");
1076
1077 actualSelected = [];
1078 var rangecount = selection.getRangeCount();
1079 for (var r = 0; r < rangecount; r++) {
1080 var start = {}, end = {};
1081 selection.getRangeAt(r, start, end);
1082 for (var rs = start.value; rs <= end.value; rs++)
1083 actualSelected.push(rs);
1084 }
1085
1086 is(compareArrays(selected, actualSelected), true, testid + " range selection [" + selected + "]");
1087 }
1088
1089 function testtag_tree_column_reorder()
1090 {
1091 // Make sure the tree is scrolled into the view, otherwise the test will
1092 // fail
1093 var testframe = window.parent.document.getElementById("testframe");
1094 if (testframe) {
1095 testframe.scrollIntoView();
1096 }
1097
1098 var tree = document.getElementById("tree-column-reorder");
1099 var numColumns = tree.columns.count;
1100
1101 var reference = [];
1102 for (var i = 0; i < numColumns; i++) {
1103 reference.push("col_" + i);
1104 }
1105
1106 // Drag the first column to each position
1107 for (var i = 0; i < numColumns - 1; i++) {
1108 synthesizeColumnDrag(tree, i, i + 1, true);
1109 arrayMove(reference, i, i + 1, true);
1110 checkColumns(tree, reference, "drag first column right");
1111 }
1112
1113 // And back
1114 for (var i = numColumns - 1; i >= 1; i--) {
1115 synthesizeColumnDrag(tree, i, i - 1, false);
1116 arrayMove(reference, i, i - 1, false);
1117 checkColumns(tree, reference, "drag last column left");
1118 }
1119
1120 // Drag each column one column left
1121 for (var i = 1; i < numColumns; i++) {
1122 synthesizeColumnDrag(tree, i, i - 1, false);
1123 arrayMove(reference, i, i - 1, false);
1124 checkColumns(tree, reference, "drag each column left");
1125 }
1126
1127 // And back
1128 for (var i = numColumns - 2; i >= 0; i--) {
1129 synthesizeColumnDrag(tree, i, i + 1, true);
1130 arrayMove(reference, i, i + 1, true);
1131 checkColumns(tree, reference, "drag each column right");
1132 }
1133
1134 // Drag each column 5 to the right
1135 for (var i = 0; i < numColumns - 5; i++) {
1136 synthesizeColumnDrag(tree, i, i + 5, true);
1137 arrayMove(reference, i, i + 5, true);
1138 checkColumns(tree, reference, "drag each column 5 to the right");
1139 }
1140
1141 // And to the left
1142 for (var i = numColumns - 6; i >= 5; i--) {
1143 synthesizeColumnDrag(tree, i, i - 5, false);
1144 arrayMove(reference, i, i - 5, false);
1145 checkColumns(tree, reference, "drag each column 5 to the left");
1146 }
1147
1148 // Test that moving a column after itself does not move anything
1149 synthesizeColumnDrag(tree, 0, 0, true);
1150 checkColumns(tree, reference, "drag to itself");
1151 is(document.treecolDragging, null, "drag to itself completed");
1152
1153 // XXX roc should this be here???
1154 SimpleTest.finish();
1155 }
1156
1157 function testtag_tree_wheel(aTree)
1158 {
1159 const deltaModes = [
1160 WheelEvent.DOM_DELTA_PIXEL, // 0
1161 WheelEvent.DOM_DELTA_LINE, // 1
1162 WheelEvent.DOM_DELTA_PAGE // 2
1163 ];
1164 function helper(aStart, aDelta, aIntDelta, aDeltaMode)
1165 {
1166 aTree.treeBoxObject.scrollToRow(aStart);
1167 var expected = !aIntDelta ? aStart :
1168 aDeltaMode != WheelEvent.DOM_DELTA_PAGE ? aStart + aIntDelta :
1169 aIntDelta > 0 ? aStart + aTree.treeBoxObject.getPageLength() :
1170 aStart - aTree.treeBoxObject.getPageLength();
1171 if (expected < 0) {
1172 expected = 0;
1173 }
1174 if (expected > aTree.view.rowCount - aTree.treeBoxObject.getPageLength()) {
1175 expected = aTree.view.rowCount - aTree.treeBoxObject.getPageLength();
1176 }
1177 synthesizeWheel(aTree.body, 1, 1,
1178 { deltaMode: aDeltaMode, deltaY: aDelta,
1179 lineOrPageDeltaY: aIntDelta });
1180 is(aTree.treeBoxObject.getFirstVisibleRow(), expected,
1181 "testtag_tree_wheel: vertical, starting " + aStart +
1182 " delta " + aDelta + " lineOrPageDelta " + aIntDelta +
1183 " aDeltaMode " + aDeltaMode);
1184
1185 aTree.treeBoxObject.scrollToRow(aStart);
1186 // Check that horizontal scrolling has no effect
1187 synthesizeWheel(aTree.body, 1, 1,
1188 { deltaMode: aDeltaMode, deltaX: aDelta,
1189 lineOrPageDeltaX: aIntDelta });
1190 is(aTree.treeBoxObject.getFirstVisibleRow(), aStart,
1191 "testtag_tree_wheel: horizontal, starting " + aStart +
1192 " delta " + aDelta + " lineOrPageDelta " + aIntDelta +
1193 " aDeltaMode " + aDeltaMode);
1194 }
1195
1196 var defaultPrevented = 0;
1197
1198 function wheelListener(event) {
1199 defaultPrevented++;
1200 }
1201 window.addEventListener("wheel", wheelListener, false);
1202
1203 deltaModes.forEach(function(aDeltaMode) {
1204 var delta = (aDeltaMode == WheelEvent.DOM_DELTA_PIXEL) ? 5.0 : 0.3;
1205 helper(2, -delta, 0, aDeltaMode);
1206 helper(2, -delta, -1, aDeltaMode);
1207 helper(2, delta, 0, aDeltaMode);
1208 helper(2, delta, 1, aDeltaMode);
1209 helper(2, -2 * delta, 0, aDeltaMode);
1210 helper(2, -2 * delta, -1, aDeltaMode);
1211 helper(2, 2 * delta, 0, aDeltaMode);
1212 helper(2, 2 * delta, 1, aDeltaMode);
1213 });
1214
1215 window.removeEventListener("wheel", wheelListener, false);
1216 is(defaultPrevented, 48, "wheel event default prevented");
1217 }
1218
1219 function synthesizeColumnDrag(aTree, aMouseDownColumnNumber, aMouseUpColumnNumber, aAfter)
1220 {
1221 var columns = getSortedColumnArray(aTree);
1222
1223 var down = columns[aMouseDownColumnNumber].element;
1224 var up = columns[aMouseUpColumnNumber].element;
1225
1226 // Target the initial mousedown in the middle of the column header so we
1227 // avoid the extra hit test space given to the splitter
1228 var columnWidth = down.boxObject.width;
1229 var splitterHitWidth = columnWidth / 2;
1230 synthesizeMouse(down, splitterHitWidth, 3, { type: "mousedown"});
1231
1232 var offsetX = 0;
1233 if (aAfter) {
1234 offsetX = columnWidth;
1235 }
1236
1237 if (aMouseUpColumnNumber > aMouseDownColumnNumber) {
1238 for (var i = aMouseDownColumnNumber; i <= aMouseUpColumnNumber; i++) {
1239 var move = columns[i].element;
1240 synthesizeMouse(move, offsetX, 3, { type: "mousemove"});
1241 }
1242 }
1243 else {
1244 for (var i = aMouseDownColumnNumber; i >= aMouseUpColumnNumber; i--) {
1245 var move = columns[i].element;
1246 synthesizeMouse(move, offsetX, 3, { type: "mousemove"});
1247 }
1248 }
1249
1250 synthesizeMouse(up, offsetX, 3, { type: "mouseup"});
1251 }
1252
1253 function arrayMove(aArray, aFrom, aTo, aAfter)
1254 {
1255 var o = aArray.splice(aFrom, 1)[0];
1256 if (aTo > aFrom) {
1257 aTo--;
1258 }
1259
1260 if (aAfter) {
1261 aTo++;
1262 }
1263
1264 aArray.splice(aTo, 0, o);
1265 }
1266
1267 function getSortedColumnArray(aTree)
1268 {
1269 var columns = aTree.columns;
1270 var a = [];
1271 for (var i = 0; i < columns.length; i++) {
1272 a.push(columns.getColumnAt(i));
1273 }
1274
1275 a.sort(function(a, b) {
1276 var o1 = parseInt(a.element.getAttribute("ordinal"));
1277 var o2 = parseInt(b.element.getAttribute("ordinal"));
1278 return o1 - o2;
1279 });
1280 return a;
1281 }
1282
1283 function checkColumns(aTree, aReference, aMessage)
1284 {
1285 var columns = getSortedColumnArray(aTree);
1286 var ids = [];
1287 columns.forEach(function(e) {
1288 ids.push(e.element.id);
1289 });
1290 is(compareArrays(ids, aReference), true, aMessage);
1291 }
1292
1293 function mouseOnCell(tree, row, column, testname)
1294 {
1295 var x = {}, y = {}, width = {}, height = {};
1296 tree.boxObject.getCoordsForCellItem(row, column, "text", x, y, width, height);
1297
1298 synthesizeMouseExpectEvent(tree.body, x.value, y.value, {}, tree, "select", testname);
1299 }
1300
1301 function mouseClickOnColumnHeader(aColumns, aColumnIndex, aButton, aClickCount)
1302 {
1303 var columnHeader = aColumns[aColumnIndex].element;
1304 var columnHeaderRect = columnHeader.getBoundingClientRect();
1305 var columnWidth = columnHeaderRect.right - columnHeaderRect.left;
1306 // For multiple click we send separate click events, with increasing
1307 // clickCount. This simulates the common behavior of multiple clicks.
1308 for (var i = 1; i <= aClickCount; i++) {
1309 // Target the middle of the column header.
1310 synthesizeMouse(columnHeader, columnWidth / 2, 3,
1311 { button: aButton,
1312 clickCount: i }, null);
1313 }
1314 }
1315
1316 function mouseDblClickOnCell(tree, row, column, testname)
1317 {
1318 // select the row we will edit
1319 var selection = tree.view.selection;
1320 selection.select(row);
1321 tree.treeBoxObject.ensureRowIsVisible(row);
1322
1323 // get cell coordinates
1324 var x = {}, y = {}, width = {}, height = {};
1325 tree.treeBoxObject.getCoordsForCellItem(row, column, "text", x, y, width, height);
1326
1327 synthesizeMouse(tree.body, x.value, y.value, { clickCount: 2 }, null);
1328 }
1329
1330 function compareArrays(arr1, arr2)
1331 {
1332 if (arr1.length != arr2.length)
1333 return false;
1334
1335 for (var i = 0; i < arr1.length; i++) {
1336 if (arr1[i] != arr2[i])
1337 return false;
1338 }
1339
1340 return true;
1341 }
1342
1343 function convertProperties(arr)
1344 {
1345 var results = [];
1346 var count = arr.Count();
1347 for (var i = 0; i < count; i++)
1348 results.push(arr.GetElementAt(i).QueryInterface(Components.interfaces.nsIAtom).toString());
1349
1350 results.sort();
1351 return results.join(" ");
1352 }
1353
1354 function convertDOMtoTreeRowInfo(treechildren, level, rowidx)
1355 {
1356 var obj = { rows: [] };
1357
1358 var parentidx = rowidx.value;
1359
1360 treechildren = treechildren.childNodes;
1361 for (var r = 0; r < treechildren.length; r++) {
1362 rowidx.value++;
1363
1364 var treeitem = treechildren[r];
1365 if (treeitem.hasChildNodes()) {
1366 var treerow = treeitem.firstChild;
1367 var cellInfo = [];
1368 for (var c = 0; c < treerow.childNodes.length; c++) {
1369 var cell = treerow.childNodes[c];
1370 cellInfo.push({ label: "" + cell.getAttribute("label"),
1371 value: cell.getAttribute("value"),
1372 properties: cell.getAttribute("properties"),
1373 editable: cell.getAttribute("editable") != "false",
1374 selectable: cell.getAttribute("selectable") != "false",
1375 image: cell.getAttribute("src"),
1376 mode: cell.hasAttribute("mode") ? parseInt(cell.getAttribute("mode")) : 3 });
1377 }
1378
1379 var descendants = treeitem.lastChild;
1380 var children = (treerow == descendants) ? null :
1381 convertDOMtoTreeRowInfo(descendants, level + 1, rowidx);
1382 obj.rows.push({ cells: cellInfo,
1383 properties: treerow.getAttribute("properties"),
1384 container: treeitem.getAttribute("container") == "true",
1385 separator: treeitem.localName == "treeseparator",
1386 children: children,
1387 level: level,
1388 parent: parentidx });
1389 }
1390 }
1391
1392 return obj;
1393 }

mercurial