2180 lines
58 KiB
JavaScript
2180 lines
58 KiB
JavaScript
// This file expects the following globals to be defined at various times.
|
|
/* globals getCustomTreeViewCellInfo */
|
|
|
|
// This files relies on these specific Chrome/XBL globals
|
|
/* globals TreeColumns, TreeColumn */
|
|
|
|
var columns_simpletree = [
|
|
{ name: "name", label: "Name", key: true, properties: "one two" },
|
|
{ name: "address", label: "Address" },
|
|
];
|
|
|
|
var columns_hiertree = [
|
|
{
|
|
name: "name",
|
|
label: "Name",
|
|
primary: true,
|
|
key: true,
|
|
properties: "one two",
|
|
},
|
|
{ name: "address", label: "Address" },
|
|
{ name: "planet", label: "Planet" },
|
|
{ name: "gender", label: "Gender", cycler: true },
|
|
];
|
|
|
|
// XXXndeakin still to add some tests for:
|
|
// cycler columns, checkbox cells
|
|
|
|
// this test function expects a tree to have 8 rows in it when it isn't
|
|
// expanded. The tree should only display four rows at a time. If editable,
|
|
// the cell at row 1 and column 0 must be editable, and the cell at row 2 and
|
|
// column 1 must not be editable.
|
|
async function testtag_tree(
|
|
treeid,
|
|
treerowinfoid,
|
|
seltype,
|
|
columnstype,
|
|
testid
|
|
) {
|
|
// Stop keystrokes that aren't handled by the tree from leaking out and
|
|
// scrolling the main Mochitests window!
|
|
function preventDefault(event) {
|
|
event.preventDefault();
|
|
}
|
|
document.addEventListener("keypress", preventDefault);
|
|
|
|
var multiple = seltype == "multiple";
|
|
|
|
var tree = document.getElementById(treeid);
|
|
var treerowinfo = document.getElementById(treerowinfoid);
|
|
var rowInfo;
|
|
if (testid == "tree view") {
|
|
rowInfo = getCustomTreeViewCellInfo();
|
|
} else {
|
|
rowInfo = convertDOMtoTreeRowInfo(treerowinfo, 0, { value: -1 });
|
|
}
|
|
var columnInfo =
|
|
columnstype == "simple" ? columns_simpletree : columns_hiertree;
|
|
|
|
is(tree.selType, seltype == "multiple" ? "" : seltype, testid + " seltype");
|
|
|
|
// note: the functions below should be in this order due to changes made in later tests
|
|
|
|
await testtag_tree_treecolpicker(tree, columnInfo, testid);
|
|
testtag_tree_columns(tree, columnInfo, testid);
|
|
testtag_tree_TreeSelection(tree, testid, multiple);
|
|
testtag_tree_TreeSelection_UI(tree, testid, multiple);
|
|
testtag_tree_TreeView(tree, testid, rowInfo);
|
|
|
|
is(tree.editable, false, "tree should not be editable");
|
|
// currently, the editable flag means that tree editing cannot be invoked
|
|
// by the user. However, editing can still be started with a script.
|
|
is(tree.editingRow, -1, testid + " initial editingRow");
|
|
is(tree.editingColumn, null, testid + " initial editingColumn");
|
|
|
|
testtag_tree_UI_editing(tree, testid, rowInfo);
|
|
|
|
is(
|
|
tree.editable,
|
|
false,
|
|
"tree should not be editable after testtag_tree_UI_editing"
|
|
);
|
|
// currently, the editable flag means that tree editing cannot be invoked
|
|
// by the user. However, editing can still be started with a script.
|
|
is(tree.editingRow, -1, testid + " initial editingRow (continued)");
|
|
is(tree.editingColumn, null, testid + " initial editingColumn (continued)");
|
|
|
|
var ecolumn = tree.columns[0];
|
|
ok(
|
|
!tree.startEditing(1, ecolumn),
|
|
"non-editable trees shouldn't start editing"
|
|
);
|
|
is(
|
|
tree.editingRow,
|
|
-1,
|
|
testid + " failed startEditing shouldn't set editingRow"
|
|
);
|
|
is(
|
|
tree.editingColumn,
|
|
null,
|
|
testid + " failed startEditing shouldn't set editingColumn"
|
|
);
|
|
|
|
tree.editable = true;
|
|
|
|
ok(tree.startEditing(1, ecolumn), "startEditing should have returned true");
|
|
is(tree.editingRow, 1, testid + " startEditing editingRow");
|
|
is(tree.editingColumn, ecolumn, testid + " startEditing editingColumn");
|
|
is(
|
|
tree.getAttribute("editing"),
|
|
"true",
|
|
testid + " startEditing editing attribute"
|
|
);
|
|
|
|
tree.stopEditing(true);
|
|
is(tree.editingRow, -1, testid + " stopEditing editingRow");
|
|
is(tree.editingColumn, null, testid + " stopEditing editingColumn");
|
|
is(
|
|
tree.hasAttribute("editing"),
|
|
false,
|
|
testid + " stopEditing editing attribute"
|
|
);
|
|
|
|
tree.startEditing(-1, ecolumn);
|
|
is(
|
|
tree.editingRow == -1 && tree.editingColumn == null,
|
|
true,
|
|
testid + " startEditing -1 editingRow"
|
|
);
|
|
tree.startEditing(15, ecolumn);
|
|
is(
|
|
tree.editingRow == -1 && tree.editingColumn == null,
|
|
true,
|
|
testid + " startEditing 15 editingRow"
|
|
);
|
|
tree.startEditing(1, null);
|
|
is(
|
|
tree.editingRow == -1 && tree.editingColumn == null,
|
|
true,
|
|
testid + " startEditing null column editingRow"
|
|
);
|
|
tree.startEditing(2, tree.columns[1]);
|
|
is(
|
|
tree.editingRow == -1 && tree.editingColumn == null,
|
|
true,
|
|
testid + " startEditing non editable cell editingRow"
|
|
);
|
|
|
|
tree.startEditing(1, ecolumn);
|
|
var inputField = tree.inputField;
|
|
is(inputField.localName, "input", testid + "inputField");
|
|
inputField.value = "Changed Value";
|
|
tree.stopEditing(true);
|
|
is(
|
|
tree.view.getCellText(1, ecolumn),
|
|
"Changed Value",
|
|
testid + "edit cell accept"
|
|
);
|
|
|
|
// this cell can be edited, but stopEditing(false) means don't accept the change.
|
|
tree.startEditing(1, ecolumn);
|
|
inputField.value = "Second Value";
|
|
tree.stopEditing(false);
|
|
is(
|
|
tree.view.getCellText(1, ecolumn),
|
|
"Changed Value",
|
|
testid + "edit cell no accept"
|
|
);
|
|
|
|
tree.editable = false;
|
|
|
|
// do the sorting tests last as it will cause the rows to rearrange
|
|
// skip them for the custom tree view
|
|
if (testid != "tree view") {
|
|
testtag_tree_TreeView_rows_sort(tree, testid, rowInfo);
|
|
}
|
|
|
|
testtag_tree_wheel(tree);
|
|
|
|
document.removeEventListener("keypress", preventDefault);
|
|
|
|
SimpleTest.finish();
|
|
}
|
|
|
|
async function testtag_tree_treecolpicker(tree, expectedColumns, testid) {
|
|
testid += " ";
|
|
|
|
async function showAndHideTreecolpicker() {
|
|
let treecolpicker = tree.querySelector("treecolpicker");
|
|
let treecolpickerMenupopup = treecolpicker.querySelector("menupopup");
|
|
await new Promise(resolve => {
|
|
treecolpickerMenupopup.addEventListener("popupshown", resolve, {
|
|
once: true,
|
|
});
|
|
treecolpicker.querySelector("button").click();
|
|
});
|
|
let menuitems = treecolpicker.querySelectorAll("menuitem");
|
|
// Ignore the last "Restore Column Order" menu in the count:
|
|
is(
|
|
menuitems.length - 1,
|
|
expectedColumns.length,
|
|
testid + "Same number of columns"
|
|
);
|
|
for (var c = 0; c < expectedColumns.length; c++) {
|
|
is(
|
|
menuitems[c].textContent,
|
|
expectedColumns[c].label,
|
|
testid + "treecolpicker menu matches"
|
|
);
|
|
ok(
|
|
!menuitems[c].querySelector("label").hidden,
|
|
testid + "label not hidden"
|
|
);
|
|
}
|
|
await new Promise(resolve => {
|
|
treecolpickerMenupopup.addEventListener("popuphidden", resolve, {
|
|
once: true,
|
|
});
|
|
treecolpickerMenupopup.hidePopup();
|
|
});
|
|
}
|
|
|
|
// Regression test for Bug 1549931 (menuitem content being hidden upon second open)
|
|
await showAndHideTreecolpicker();
|
|
await showAndHideTreecolpicker();
|
|
}
|
|
|
|
function testtag_tree_columns(tree, expectedColumns, testid) {
|
|
testid += " ";
|
|
|
|
var columns = tree.columns;
|
|
|
|
is(
|
|
TreeColumns.isInstance(columns),
|
|
true,
|
|
testid + "columns is a TreeColumns"
|
|
);
|
|
is(columns.count, expectedColumns.length, testid + "TreeColumns count");
|
|
is(columns.length, expectedColumns.length, testid + "TreeColumns length");
|
|
|
|
var treecols = tree.getElementsByTagName("treecols")[0];
|
|
var treecol = treecols.getElementsByTagName("treecol");
|
|
|
|
var x = 0;
|
|
var primary = null,
|
|
sorted = null,
|
|
key = null;
|
|
for (var c = 0; c < expectedColumns.length; c++) {
|
|
var adjtestid = testid + " column " + c + " ";
|
|
var column = columns[c];
|
|
var expectedColumn = expectedColumns[c];
|
|
is(columns.getColumnAt(c), column, adjtestid + "getColumnAt");
|
|
is(
|
|
columns.getNamedColumn(expectedColumn.name),
|
|
column,
|
|
adjtestid + "getNamedColumn"
|
|
);
|
|
is(columns.getColumnFor(treecol[c]), column, adjtestid + "getColumnFor");
|
|
if (expectedColumn.primary) {
|
|
primary = column;
|
|
}
|
|
if (expectedColumn.sorted) {
|
|
sorted = column;
|
|
}
|
|
if (expectedColumn.key) {
|
|
key = column;
|
|
}
|
|
|
|
// XXXndeakin on Windows and Linux, some columns are one pixel to the
|
|
// left of where they should be. Could just be a rounding issue.
|
|
var adj = 1;
|
|
is(
|
|
column.x + adj >= x,
|
|
true,
|
|
adjtestid +
|
|
"position is after last column " +
|
|
column.x +
|
|
"," +
|
|
column.width +
|
|
"," +
|
|
x
|
|
);
|
|
is(column.width > 0, true, adjtestid + "width is greater than 0");
|
|
x = column.x + column.width;
|
|
|
|
// now check the TreeColumn properties
|
|
is(TreeColumn.isInstance(column), true, adjtestid + "is a TreeColumn");
|
|
is(column.element, treecol[c], adjtestid + "element is treecol");
|
|
is(column.columns, columns, adjtestid + "columns is TreeColumns");
|
|
is(column.id, expectedColumn.name, adjtestid + "name");
|
|
is(column.index, c, adjtestid + "index");
|
|
is(column.primary, primary == column, adjtestid + "column is primary");
|
|
|
|
is(
|
|
column.cycler,
|
|
"cycler" in expectedColumn && expectedColumn.cycler,
|
|
adjtestid + "column is cycler"
|
|
);
|
|
is(
|
|
column.editable,
|
|
"editable" in expectedColumn && expectedColumn.editable,
|
|
adjtestid + "column is editable"
|
|
);
|
|
|
|
is(
|
|
column.type,
|
|
"type" in expectedColumn ? expectedColumn.type : 1,
|
|
adjtestid + "type"
|
|
);
|
|
|
|
is(
|
|
column.getPrevious(),
|
|
c > 0 ? columns[c - 1] : null,
|
|
adjtestid + "getPrevious"
|
|
);
|
|
is(
|
|
column.getNext(),
|
|
c < columns.length - 1 ? columns[c + 1] : null,
|
|
adjtestid + "getNext"
|
|
);
|
|
|
|
// check the view's getColumnProperties method
|
|
var properties = tree.view.getColumnProperties(column);
|
|
var expectedProperties = expectedColumn.properties;
|
|
is(properties, expectedProperties || "", adjtestid + "getColumnProperties");
|
|
}
|
|
|
|
is(columns.getFirstColumn(), columns[0], testid + "getFirstColumn");
|
|
is(
|
|
columns.getLastColumn(),
|
|
columns[columns.length - 1],
|
|
testid + "getLastColumn"
|
|
);
|
|
is(columns.getPrimaryColumn(), primary, testid + "getPrimaryColumn");
|
|
is(columns.getSortedColumn(), sorted, testid + "getSortedColumn");
|
|
is(columns.getKeyColumn(), key, testid + "getKeyColumn");
|
|
|
|
is(columns.getColumnAt(-1), null, testid + "getColumnAt under");
|
|
is(columns.getColumnAt(columns.length), null, testid + "getColumnAt over");
|
|
is(columns.getNamedColumn(""), null, testid + "getNamedColumn null");
|
|
is(
|
|
columns.getNamedColumn("unknown"),
|
|
null,
|
|
testid + "getNamedColumn unknown"
|
|
);
|
|
is(columns.getColumnFor(null), null, testid + "getColumnFor null");
|
|
is(columns.getColumnFor(tree), null, testid + "getColumnFor other");
|
|
}
|
|
|
|
function testtag_tree_TreeSelection(tree, testid, multiple) {
|
|
testid += " selection ";
|
|
|
|
var selection = tree.view.selection;
|
|
is(
|
|
selection instanceof Ci.nsITreeSelection,
|
|
true,
|
|
testid + "selection is a TreeSelection"
|
|
);
|
|
is(selection.single, !multiple, testid + "single");
|
|
|
|
testtag_tree_TreeSelection_State(tree, testid + "initial", -1, []);
|
|
is(selection.shiftSelectPivot, -1, testid + "initial shiftSelectPivot");
|
|
|
|
selection.currentIndex = 2;
|
|
testtag_tree_TreeSelection_State(tree, testid + "set currentIndex", 2, []);
|
|
tree.currentIndex = 3;
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "set tree.currentIndex",
|
|
3,
|
|
[]
|
|
);
|
|
|
|
// test the select() method, which should deselect all rows and select
|
|
// a single row
|
|
selection.select(1);
|
|
testtag_tree_TreeSelection_State(tree, testid + "select 1", 1, [1]);
|
|
selection.select(3);
|
|
testtag_tree_TreeSelection_State(tree, testid + "select 2", 3, [3]);
|
|
selection.select(3);
|
|
testtag_tree_TreeSelection_State(tree, testid + "select same", 3, [3]);
|
|
|
|
selection.currentIndex = 1;
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "set currentIndex with single selection",
|
|
1,
|
|
[3]
|
|
);
|
|
|
|
tree.currentIndex = 2;
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "set tree.currentIndex with single selection",
|
|
2,
|
|
[3]
|
|
);
|
|
|
|
// check the toggleSelect method. In single selection mode, it only toggles on when
|
|
// there isn't currently a selection.
|
|
selection.toggleSelect(2);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "toggleSelect 1",
|
|
2,
|
|
multiple ? [2, 3] : [3]
|
|
);
|
|
selection.toggleSelect(2);
|
|
selection.toggleSelect(3);
|
|
testtag_tree_TreeSelection_State(tree, testid + "toggleSelect 2", 3, []);
|
|
|
|
// the current index doesn't change after a selectAll, so it should still be set to 1
|
|
// selectAll has no effect on single selection trees
|
|
selection.currentIndex = 1;
|
|
selection.selectAll();
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "selectAll 1",
|
|
1,
|
|
multiple ? [0, 1, 2, 3, 4, 5, 6, 7] : []
|
|
);
|
|
selection.toggleSelect(2);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "toggleSelect after selectAll",
|
|
2,
|
|
multiple ? [0, 1, 3, 4, 5, 6, 7] : [2]
|
|
);
|
|
selection.clearSelection();
|
|
testtag_tree_TreeSelection_State(tree, testid + "clearSelection", 2, []);
|
|
selection.toggleSelect(3);
|
|
selection.toggleSelect(1);
|
|
if (multiple) {
|
|
selection.selectAll();
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "selectAll 2",
|
|
1,
|
|
[0, 1, 2, 3, 4, 5, 6, 7]
|
|
);
|
|
}
|
|
selection.currentIndex = 2;
|
|
selection.clearSelection();
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "clearSelection after selectAll",
|
|
2,
|
|
[]
|
|
);
|
|
|
|
is(selection.shiftSelectPivot, -1, testid + "shiftSelectPivot set to -1");
|
|
|
|
// rangedSelect and clearRange set the currentIndex to the endIndex. The
|
|
// shiftSelectPivot property will be set to startIndex.
|
|
selection.rangedSelect(1, 3, false);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "rangedSelect no augment",
|
|
multiple ? 3 : 2,
|
|
multiple ? [1, 2, 3] : []
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
multiple ? 1 : -1,
|
|
testid + "shiftSelectPivot after rangedSelect no augment"
|
|
);
|
|
if (multiple) {
|
|
selection.select(1);
|
|
selection.rangedSelect(0, 2, true);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "rangedSelect augment",
|
|
2,
|
|
[0, 1, 2]
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
0,
|
|
testid + "shiftSelectPivot after rangedSelect augment"
|
|
);
|
|
|
|
selection.clearRange(1, 3);
|
|
testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment", 3, [
|
|
0,
|
|
]);
|
|
|
|
// check that rangedSelect can take a start value higher than end
|
|
selection.rangedSelect(3, 1, false);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "rangedSelect reverse",
|
|
1,
|
|
[1, 2, 3]
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
3,
|
|
testid + "shiftSelectPivot after rangedSelect reverse"
|
|
);
|
|
|
|
// check that setting the current index doesn't change the selection
|
|
selection.currentIndex = 0;
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "currentIndex with range selection",
|
|
0,
|
|
[1, 2, 3]
|
|
);
|
|
}
|
|
|
|
// both values of rangedSelect may be the same
|
|
selection.rangedSelect(2, 2, false);
|
|
testtag_tree_TreeSelection_State(tree, testid + "rangedSelect one row", 2, [
|
|
2,
|
|
]);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
2,
|
|
testid + "shiftSelectPivot after selecting one row"
|
|
);
|
|
|
|
if (multiple) {
|
|
selection.rangedSelect(2, 3, true);
|
|
|
|
// a start index of -1 means from the last point
|
|
selection.rangedSelect(-1, 0, true);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "rangedSelect -1 existing selection",
|
|
0,
|
|
[0, 1, 2, 3]
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
2,
|
|
testid + "shiftSelectPivot after -1 existing selection"
|
|
);
|
|
|
|
selection.currentIndex = 2;
|
|
selection.rangedSelect(-1, 0, false);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "rangedSelect -1 from currentIndex",
|
|
0,
|
|
[0, 1, 2]
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
2,
|
|
testid + "shiftSelectPivot -1 from currentIndex"
|
|
);
|
|
}
|
|
|
|
// XXXndeakin need to test out of range values but these don't work properly
|
|
/*
|
|
selection.select(-1);
|
|
testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment -1", -1, []);
|
|
|
|
selection.select(8);
|
|
testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment 8", 3, [0]);
|
|
*/
|
|
}
|
|
|
|
function testtag_tree_TreeSelection_UI(tree, testid, multiple) {
|
|
testid += " selection UI ";
|
|
|
|
var selection = tree.view.selection;
|
|
selection.clearSelection();
|
|
selection.currentIndex = 0;
|
|
tree.focus();
|
|
|
|
var keydownFired = 0;
|
|
var keypressFired = 0;
|
|
function keydownListener() {
|
|
keydownFired++;
|
|
}
|
|
function keypressListener() {
|
|
keypressFired++;
|
|
}
|
|
|
|
// check that cursor up and down keys navigate up and down
|
|
// select event fires after a delay so don't expect it. The reason it fires after a delay
|
|
// is so that cursor navigation allows quicking skimming over a set of items without
|
|
// actually firing events in-between, improving performance. The select event will only
|
|
// be fired on the row where the cursor stops.
|
|
window.addEventListener("keydown", keydownListener);
|
|
window.addEventListener("keypress", keypressListener);
|
|
|
|
synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down");
|
|
testtag_tree_TreeSelection_State(tree, testid + "key down", 1, [1], 0);
|
|
|
|
synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up");
|
|
testtag_tree_TreeSelection_State(tree, testid + "key up", 0, [0], 0);
|
|
|
|
synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up at start");
|
|
testtag_tree_TreeSelection_State(tree, testid + "key up at start", 0, [0], 0);
|
|
|
|
// pressing down while the last row is selected should not fire a select event,
|
|
// as the selection won't have changed. Also the view is not scrolled in this case.
|
|
selection.select(7);
|
|
synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down at end");
|
|
testtag_tree_TreeSelection_State(tree, testid + "key down at end", 7, [7], 0);
|
|
|
|
// pressing keys while at the edge of the visible rows should scroll the list
|
|
tree.scrollToRow(4);
|
|
selection.select(4);
|
|
synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up with scroll");
|
|
is(tree.getFirstVisibleRow(), 3, testid + "key up with scroll");
|
|
|
|
tree.scrollToRow(0);
|
|
selection.select(3);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_DOWN",
|
|
{},
|
|
tree,
|
|
"!select",
|
|
"key down with scroll"
|
|
);
|
|
is(tree.getFirstVisibleRow(), 1, testid + "key down with scroll");
|
|
|
|
// accel key and cursor movement adjust currentIndex but should not change
|
|
// the selection. In single selection mode, the selection will not change,
|
|
// but instead will just scroll up or down a line
|
|
tree.scrollToRow(0);
|
|
selection.select(1);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_DOWN",
|
|
{ accelKey: true },
|
|
tree,
|
|
"!select",
|
|
"key down with accel"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key down with accel",
|
|
multiple ? 2 : 1,
|
|
[1]
|
|
);
|
|
if (!multiple) {
|
|
is(tree.getFirstVisibleRow(), 1, testid + "key down with accel and scroll");
|
|
}
|
|
|
|
tree.scrollToRow(4);
|
|
selection.select(4);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_UP",
|
|
{ accelKey: true },
|
|
tree,
|
|
"!select",
|
|
"key up with accel"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key up with accel",
|
|
multiple ? 3 : 4,
|
|
[4]
|
|
);
|
|
if (!multiple) {
|
|
is(tree.getFirstVisibleRow(), 3, testid + "key up with accel and scroll");
|
|
}
|
|
|
|
// do this three times, one for each state of pageUpOrDownMovesSelection,
|
|
// and then once with the accel key pressed
|
|
for (let t = 0; t < 3; t++) {
|
|
let testidmod = "";
|
|
if (t == 2) {
|
|
testidmod = " with accel";
|
|
} else if (t == 1) {
|
|
testidmod = " rev";
|
|
}
|
|
var keymod = t == 2 ? { accelKey: true } : {};
|
|
|
|
var moveselection = tree.pageUpOrDownMovesSelection;
|
|
if (t == 2) {
|
|
moveselection = !moveselection;
|
|
}
|
|
|
|
tree.scrollToRow(4);
|
|
selection.currentIndex = 6;
|
|
selection.select(6);
|
|
var expected = moveselection ? 4 : 6;
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
keymod,
|
|
tree,
|
|
"!select",
|
|
"key page up"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key page up" + testidmod,
|
|
expected,
|
|
[expected],
|
|
moveselection ? 4 : 0
|
|
);
|
|
|
|
expected = moveselection ? 0 : 6;
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
keymod,
|
|
tree,
|
|
"!select",
|
|
"key page up again"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key page up again" + testidmod,
|
|
expected,
|
|
[expected],
|
|
0
|
|
);
|
|
|
|
expected = moveselection ? 0 : 6;
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
keymod,
|
|
tree,
|
|
"!select",
|
|
"key page up at start"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key page up at start" + testidmod,
|
|
expected,
|
|
[expected],
|
|
0
|
|
);
|
|
|
|
tree.scrollToRow(0);
|
|
selection.currentIndex = 1;
|
|
selection.select(1);
|
|
expected = moveselection ? 3 : 1;
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
keymod,
|
|
tree,
|
|
"!select",
|
|
"key page down"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key page down" + testidmod,
|
|
expected,
|
|
[expected],
|
|
moveselection ? 0 : 4
|
|
);
|
|
|
|
expected = moveselection ? 7 : 1;
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
keymod,
|
|
tree,
|
|
"!select",
|
|
"key page down again"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key page down again" + testidmod,
|
|
expected,
|
|
[expected],
|
|
4
|
|
);
|
|
|
|
expected = moveselection ? 7 : 1;
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
keymod,
|
|
tree,
|
|
"!select",
|
|
"key page down at start"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key page down at start" + testidmod,
|
|
expected,
|
|
[expected],
|
|
4
|
|
);
|
|
|
|
if (t < 2) {
|
|
tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
|
|
}
|
|
}
|
|
|
|
tree.scrollToRow(4);
|
|
selection.select(6);
|
|
synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home");
|
|
testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0);
|
|
|
|
tree.scrollToRow(0);
|
|
selection.select(1);
|
|
synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end");
|
|
testtag_tree_TreeSelection_State(tree, testid + "key end", 7, [7], 4);
|
|
|
|
// in single selection mode, the selection doesn't change in this case
|
|
tree.scrollToRow(4);
|
|
selection.select(6);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_HOME",
|
|
{ accelKey: true },
|
|
tree,
|
|
"!select",
|
|
"key home with accel"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key home with accel",
|
|
multiple ? 0 : 6,
|
|
[6],
|
|
0
|
|
);
|
|
|
|
tree.scrollToRow(0);
|
|
selection.select(1);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_END",
|
|
{ accelKey: true },
|
|
tree,
|
|
"!select",
|
|
"key end with accel"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key end with accel",
|
|
multiple ? 7 : 1,
|
|
[1],
|
|
4
|
|
);
|
|
|
|
// next, test cursor navigation with selection. Here the select event will be fired
|
|
selection.select(1);
|
|
var eventExpected = multiple ? "select" : "!select";
|
|
synthesizeKeyExpectEvent(
|
|
"VK_DOWN",
|
|
{ shiftKey: true },
|
|
tree,
|
|
eventExpected,
|
|
"key shift down to select"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift down to select",
|
|
multiple ? 2 : 1,
|
|
multiple ? [1, 2] : [1]
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
multiple ? 1 : -1,
|
|
testid + "key shift down to select shiftSelectPivot"
|
|
);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_UP",
|
|
{ shiftKey: true },
|
|
tree,
|
|
eventExpected,
|
|
"key shift up to unselect"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift up to unselect",
|
|
1,
|
|
[1]
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
multiple ? 1 : -1,
|
|
testid + "key shift up to unselect shiftSelectPivot"
|
|
);
|
|
if (multiple) {
|
|
synthesizeKeyExpectEvent(
|
|
"VK_UP",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"select",
|
|
"key shift up to select"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift up to select",
|
|
0,
|
|
[0, 1]
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
1,
|
|
testid + "key shift up to select shiftSelectPivot"
|
|
);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_DOWN",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"select",
|
|
"key shift down to unselect"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift down to unselect",
|
|
1,
|
|
[1]
|
|
);
|
|
is(
|
|
selection.shiftSelectPivot,
|
|
1,
|
|
testid + "key shift down to unselect shiftSelectPivot"
|
|
);
|
|
}
|
|
|
|
// do this twice, one for each state of pageUpOrDownMovesSelection, however
|
|
// when selecting with the shift key, pageUpOrDownMovesSelection is ignored
|
|
// and the selection always changes
|
|
var lastidx = tree.view.rowCount - 1;
|
|
for (let t = 0; t < 2; t++) {
|
|
let testidmod = t == 0 ? "" : " rev";
|
|
|
|
// If the top or bottom visible row is the current row, pressing shift and
|
|
// page down / page up selects one page up or one page down. Otherwise, the
|
|
// selection is made to the top or bottom of the visible area.
|
|
tree.scrollToRow(lastidx - 3);
|
|
selection.currentIndex = 6;
|
|
selection.select(6);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
{ shiftKey: true },
|
|
tree,
|
|
eventExpected,
|
|
"key shift page up"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page up" + testidmod,
|
|
multiple ? 4 : 6,
|
|
multiple ? [4, 5, 6] : [6]
|
|
);
|
|
if (multiple) {
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"select",
|
|
"key shift page up again"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page up again" + testidmod,
|
|
0,
|
|
[0, 1, 2, 3, 4, 5, 6]
|
|
);
|
|
// no change in the selection, so no select event should be fired
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"!select",
|
|
"key shift page up at start"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page up at start" + testidmod,
|
|
0,
|
|
[0, 1, 2, 3, 4, 5, 6]
|
|
);
|
|
// deselect by paging down again
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"select",
|
|
"key shift page down deselect"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page down deselect" + testidmod,
|
|
3,
|
|
[3, 4, 5, 6]
|
|
);
|
|
}
|
|
|
|
tree.scrollToRow(1);
|
|
selection.currentIndex = 2;
|
|
selection.select(2);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
{ shiftKey: true },
|
|
tree,
|
|
eventExpected,
|
|
"key shift page down"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page down" + testidmod,
|
|
multiple ? 4 : 2,
|
|
multiple ? [2, 3, 4] : [2]
|
|
);
|
|
if (multiple) {
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"select",
|
|
"key shift page down again"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page down again" + testidmod,
|
|
7,
|
|
[2, 3, 4, 5, 6, 7]
|
|
);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"!select",
|
|
"key shift page down at start"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page down at start" + testidmod,
|
|
7,
|
|
[2, 3, 4, 5, 6, 7]
|
|
);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"select",
|
|
"key shift page up deselect"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page up deselect" + testidmod,
|
|
4,
|
|
[2, 3, 4]
|
|
);
|
|
}
|
|
|
|
// test when page down / page up is pressed when the view is scrolled such
|
|
// that the selection is not visible
|
|
if (multiple) {
|
|
tree.scrollToRow(3);
|
|
selection.currentIndex = 1;
|
|
selection.select(1);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
{ shiftKey: true },
|
|
tree,
|
|
eventExpected,
|
|
"key shift page down with view scrolled down"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page down with view scrolled down" + testidmod,
|
|
6,
|
|
[1, 2, 3, 4, 5, 6],
|
|
3
|
|
);
|
|
|
|
tree.scrollToRow(2);
|
|
selection.currentIndex = 6;
|
|
selection.select(6);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
{ shiftKey: true },
|
|
tree,
|
|
eventExpected,
|
|
"key shift page up with view scrolled up"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page up with view scrolled up" + testidmod,
|
|
2,
|
|
[2, 3, 4, 5, 6],
|
|
2
|
|
);
|
|
|
|
tree.scrollToRow(2);
|
|
selection.currentIndex = 0;
|
|
selection.select(0);
|
|
// don't expect the select event, as the selection won't have changed
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_UP",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"!select",
|
|
"key shift page up at start with view scrolled down"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid +
|
|
"key shift page up at start with view scrolled down" +
|
|
testidmod,
|
|
0,
|
|
[0],
|
|
0
|
|
);
|
|
|
|
tree.scrollToRow(0);
|
|
selection.currentIndex = 7;
|
|
selection.select(7);
|
|
// don't expect the select event, as the selection won't have changed
|
|
synthesizeKeyExpectEvent(
|
|
"VK_PAGE_DOWN",
|
|
{ shiftKey: true },
|
|
tree,
|
|
"!select",
|
|
"key shift page down at end with view scrolled up"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift page down at end with view scrolled up" + testidmod,
|
|
7,
|
|
[7],
|
|
4
|
|
);
|
|
}
|
|
|
|
tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
|
|
}
|
|
|
|
tree.scrollToRow(4);
|
|
selection.select(5);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_HOME",
|
|
{ shiftKey: true },
|
|
tree,
|
|
eventExpected,
|
|
"key shift home"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift home",
|
|
multiple ? 0 : 5,
|
|
multiple ? [0, 1, 2, 3, 4, 5] : [5],
|
|
multiple ? 0 : 4
|
|
);
|
|
|
|
tree.scrollToRow(0);
|
|
selection.select(3);
|
|
synthesizeKeyExpectEvent(
|
|
"VK_END",
|
|
{ shiftKey: true },
|
|
tree,
|
|
eventExpected,
|
|
"key shift end"
|
|
);
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key shift end",
|
|
multiple ? 7 : 3,
|
|
multiple ? [3, 4, 5, 6, 7] : [3],
|
|
multiple ? 4 : 0
|
|
);
|
|
|
|
// pressing space selects a row, pressing accel + space unselects a row
|
|
selection.select(2);
|
|
selection.currentIndex = 4;
|
|
synthesizeKeyExpectEvent(" ", {}, tree, "select", "key space on");
|
|
// in single selection mode, space shouldn't do anything
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "key space on",
|
|
4,
|
|
multiple ? [2, 4] : [2]
|
|
);
|
|
|
|
if (multiple) {
|
|
synthesizeKeyExpectEvent(
|
|
" ",
|
|
{ accelKey: true },
|
|
tree,
|
|
"select",
|
|
"key space off"
|
|
);
|
|
testtag_tree_TreeSelection_State(tree, testid + "key space off", 4, [2]);
|
|
}
|
|
|
|
// check that clicking on a row selects it
|
|
tree.scrollToRow(0);
|
|
selection.select(2);
|
|
selection.currentIndex = 2;
|
|
if (0) {
|
|
// XXXndeakin disable these tests for now
|
|
mouseOnCell(tree, 1, tree.columns[1], "mouse on row");
|
|
testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid + "mouse on row",
|
|
1,
|
|
[1],
|
|
0,
|
|
null
|
|
);
|
|
}
|
|
|
|
// restore the scroll position to the start of the page
|
|
sendKey("HOME");
|
|
|
|
window.removeEventListener("keydown", keydownListener);
|
|
window.removeEventListener("keypress", keypressListener);
|
|
is(keydownFired, multiple ? 63 : 40, "keydown event wasn't fired properly");
|
|
is(keypressFired, multiple ? 2 : 1, "keypress event wasn't fired properly");
|
|
}
|
|
|
|
function testtag_tree_UI_editing(tree, testid, rowInfo) {
|
|
testid += " editing UI ";
|
|
|
|
// check editing UI
|
|
var ecolumn = tree.columns[0];
|
|
var rowIndex = 2;
|
|
|
|
// temporary make the tree editable to test mouse double click
|
|
var wasEditable = tree.editable;
|
|
if (!wasEditable) {
|
|
tree.editable = true;
|
|
}
|
|
|
|
// if this is a container save its current open status
|
|
var row = rowInfo.rows[rowIndex];
|
|
var wasOpen = null;
|
|
if (tree.view.isContainer(row)) {
|
|
wasOpen = tree.view.isContainerOpen(row);
|
|
}
|
|
|
|
mouseDblClickOnCell(tree, rowIndex, ecolumn, testid + "edit on double click");
|
|
is(tree.editingColumn, ecolumn, testid + "editing column");
|
|
is(tree.editingRow, rowIndex, testid + "editing row");
|
|
|
|
// ensure that we don't expand an expandable container on edit
|
|
if (wasOpen != null) {
|
|
is(
|
|
tree.view.isContainerOpen(row),
|
|
wasOpen,
|
|
testid + "opened container node on edit"
|
|
);
|
|
}
|
|
|
|
// ensure to restore editable attribute
|
|
if (!wasEditable) {
|
|
tree.editable = false;
|
|
}
|
|
|
|
var ci = tree.currentIndex;
|
|
|
|
// cursor navigation should not change the selection while editing
|
|
var testKey = function (key) {
|
|
synthesizeKeyExpectEvent(
|
|
key,
|
|
{},
|
|
tree,
|
|
"!select",
|
|
"key " + key + " with editing"
|
|
);
|
|
is(
|
|
tree.editingRow == rowIndex &&
|
|
tree.editingColumn == ecolumn &&
|
|
tree.currentIndex == ci,
|
|
true,
|
|
testid + "key " + key + " while editing"
|
|
);
|
|
};
|
|
|
|
testKey("VK_DOWN");
|
|
testKey("VK_UP");
|
|
testKey("VK_PAGE_DOWN");
|
|
testKey("VK_PAGE_UP");
|
|
testKey("VK_HOME");
|
|
testKey("VK_END");
|
|
|
|
// XXXndeakin figure out how to send characters to the textbox
|
|
// inputField.inputField.focus()
|
|
// synthesizeKeyExpectEvent(inputField.inputField, "b", null, "");
|
|
// tree.stopEditing(true);
|
|
// is(tree.view.getCellText(0, ecolumn), "b", testid + "edit cell");
|
|
|
|
// Restore initial state.
|
|
tree.stopEditing(false);
|
|
}
|
|
|
|
function testtag_tree_TreeView(tree, testid, rowInfo) {
|
|
testid += " view ";
|
|
|
|
var columns = tree.columns;
|
|
var view = tree.view;
|
|
|
|
is(view instanceof Ci.nsITreeView, true, testid + "view is a TreeView");
|
|
is(view.rowCount, rowInfo.rows.length, testid + "rowCount");
|
|
|
|
testtag_tree_TreeView_rows(tree, testid, rowInfo, 0);
|
|
|
|
// note that this will only work for content trees currently
|
|
view.setCellText(0, columns[1], "Changed Value");
|
|
is(view.getCellText(0, columns[1]), "Changed Value", "setCellText");
|
|
|
|
view.setCellValue(1, columns[0], "Another Changed Value");
|
|
is(view.getCellValue(1, columns[0]), "Another Changed Value", "setCellText");
|
|
}
|
|
|
|
function testtag_tree_TreeView_rows(tree, testid, rowInfo, startRow) {
|
|
var r;
|
|
var columns = tree.columns;
|
|
var view = tree.view;
|
|
var length = rowInfo.rows.length;
|
|
|
|
// methods to test along with the functions which determine the expected value
|
|
var checkRowMethods = {
|
|
isContainer(row) {
|
|
return row.container;
|
|
},
|
|
isContainerOpen() {
|
|
return false;
|
|
},
|
|
isContainerEmpty(row) {
|
|
return row.children != null && !row.children.rows.length;
|
|
},
|
|
isSeparator(row) {
|
|
return row.separator;
|
|
},
|
|
getRowProperties(row) {
|
|
return row.properties;
|
|
},
|
|
getLevel(row) {
|
|
return row.level;
|
|
},
|
|
getParentIndex(row) {
|
|
return row.parent;
|
|
},
|
|
hasNextSibling() {
|
|
return r < startRow + length - 1;
|
|
},
|
|
};
|
|
|
|
var checkCellMethods = {
|
|
getCellText(row, cell) {
|
|
return cell.label;
|
|
},
|
|
getCellValue(row, cell) {
|
|
return cell.value;
|
|
},
|
|
getCellProperties(row, cell) {
|
|
return cell.properties;
|
|
},
|
|
isEditable(row, cell) {
|
|
return cell.editable;
|
|
},
|
|
getImageSrc(row, cell) {
|
|
return cell.image;
|
|
},
|
|
};
|
|
|
|
var failedMethods = {};
|
|
var checkMethod, actual, expected;
|
|
var toggleOpenStateOK = true;
|
|
|
|
for (r = startRow; r < length; r++) {
|
|
var row = rowInfo.rows[r];
|
|
for (var c = 0; c < row.cells.length; c++) {
|
|
var cell = row.cells[c];
|
|
|
|
for (checkMethod in checkCellMethods) {
|
|
expected = checkCellMethods[checkMethod](row, cell);
|
|
actual = view[checkMethod](r, columns[c]);
|
|
if (actual !== expected) {
|
|
failedMethods[checkMethod] = true;
|
|
is(
|
|
actual,
|
|
expected,
|
|
testid +
|
|
"row " +
|
|
r +
|
|
" column " +
|
|
c +
|
|
" " +
|
|
checkMethod +
|
|
" is incorrect"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// compare row properties
|
|
for (checkMethod in checkRowMethods) {
|
|
expected = checkRowMethods[checkMethod](row, r);
|
|
if (checkMethod == "hasNextSibling") {
|
|
actual = view[checkMethod](r, r);
|
|
} else {
|
|
actual = view[checkMethod](r);
|
|
}
|
|
if (actual !== expected) {
|
|
failedMethods[checkMethod] = true;
|
|
is(
|
|
actual,
|
|
expected,
|
|
testid + "row " + r + " " + checkMethod + " is incorrect"
|
|
);
|
|
}
|
|
}
|
|
/*
|
|
// open and recurse into containers
|
|
if (row.container) {
|
|
view.toggleOpenState(r);
|
|
if (!view.isContainerOpen(r)) {
|
|
toggleOpenStateOK = false;
|
|
is(view.isContainerOpen(r), true, testid + "row " + r + " toggleOpenState open");
|
|
}
|
|
testtag_tree_TreeView_rows(tree, testid + "container " + r + " ", row.children, r + 1);
|
|
view.toggleOpenState(r);
|
|
if (view.isContainerOpen(r)) {
|
|
toggleOpenStateOK = false;
|
|
is(view.isContainerOpen(r), false, testid + "row " + r + " toggleOpenState close");
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
for (var failedMethod in failedMethods) {
|
|
if (failedMethod in checkRowMethods) {
|
|
delete checkRowMethods[failedMethod];
|
|
}
|
|
if (failedMethod in checkCellMethods) {
|
|
delete checkCellMethods[failedMethod];
|
|
}
|
|
}
|
|
|
|
for (checkMethod in checkRowMethods) {
|
|
is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod);
|
|
}
|
|
for (checkMethod in checkCellMethods) {
|
|
is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod);
|
|
}
|
|
if (toggleOpenStateOK) {
|
|
is("toggleOpenState ok", "toggleOpenState ok", testid + "toggleOpenState");
|
|
}
|
|
}
|
|
|
|
function testtag_tree_TreeView_rows_sort(tree) {
|
|
// check if cycleHeader sorts the columns
|
|
var columnIndex = 0;
|
|
var view = tree.view;
|
|
var column = tree.columns[columnIndex];
|
|
var columnElement = column.element;
|
|
var sortkey = columnElement.getAttribute("sort");
|
|
if (sortkey) {
|
|
view.cycleHeader(column);
|
|
is(tree.getAttribute("sort"), sortkey, "cycleHeader sort");
|
|
is(
|
|
tree.getAttribute("sortDirection"),
|
|
"ascending",
|
|
"cycleHeader sortDirection ascending"
|
|
);
|
|
is(
|
|
columnElement.getAttribute("sortDirection"),
|
|
"ascending",
|
|
"cycleHeader column sortDirection"
|
|
);
|
|
is(
|
|
columnElement.getAttribute("sortActive"),
|
|
"true",
|
|
"cycleHeader column sortActive"
|
|
);
|
|
view.cycleHeader(column);
|
|
is(
|
|
tree.getAttribute("sortDirection"),
|
|
"descending",
|
|
"cycleHeader sortDirection descending"
|
|
);
|
|
is(
|
|
columnElement.getAttribute("sortDirection"),
|
|
"descending",
|
|
"cycleHeader column sortDirection descending"
|
|
);
|
|
view.cycleHeader(column);
|
|
is(
|
|
tree.getAttribute("sortDirection"),
|
|
"",
|
|
"cycleHeader sortDirection natural"
|
|
);
|
|
is(
|
|
columnElement.getAttribute("sortDirection"),
|
|
"",
|
|
"cycleHeader column sortDirection natural"
|
|
);
|
|
// XXXndeakin content view isSorted needs to be tested
|
|
}
|
|
|
|
// Check that clicking on column header sorts the column.
|
|
var columns = getSortedColumnArray(tree);
|
|
is(
|
|
columnElement.getAttribute("sortDirection"),
|
|
"",
|
|
"cycleHeader column sortDirection"
|
|
);
|
|
|
|
// Click once on column header and check sorting has cycled once.
|
|
mouseClickOnColumnHeader(columns, columnIndex, 0, 1);
|
|
is(
|
|
columnElement.getAttribute("sortDirection"),
|
|
"ascending",
|
|
"single click cycleHeader column sortDirection ascending"
|
|
);
|
|
|
|
// Now simulate a double click.
|
|
mouseClickOnColumnHeader(columns, columnIndex, 0, 2);
|
|
if (navigator.platform.indexOf("Win") == 0) {
|
|
// Windows cycles only once on double click.
|
|
is(
|
|
columnElement.getAttribute("sortDirection"),
|
|
"descending",
|
|
"double click cycleHeader column sortDirection descending"
|
|
);
|
|
// 1 single clicks should restore natural sorting.
|
|
mouseClickOnColumnHeader(columns, columnIndex, 0, 1);
|
|
}
|
|
|
|
// Check we have gone back to natural sorting.
|
|
is(
|
|
columnElement.getAttribute("sortDirection"),
|
|
"",
|
|
"cycleHeader column sortDirection"
|
|
);
|
|
|
|
columnElement.setAttribute("sorthints", "twostate");
|
|
view.cycleHeader(column);
|
|
is(
|
|
tree.getAttribute("sortDirection"),
|
|
"ascending",
|
|
"cycleHeader sortDirection ascending twostate"
|
|
);
|
|
view.cycleHeader(column);
|
|
is(
|
|
tree.getAttribute("sortDirection"),
|
|
"descending",
|
|
"cycleHeader sortDirection ascending twostate"
|
|
);
|
|
view.cycleHeader(column);
|
|
is(
|
|
tree.getAttribute("sortDirection"),
|
|
"ascending",
|
|
"cycleHeader sortDirection ascending twostate again"
|
|
);
|
|
columnElement.removeAttribute("sorthints");
|
|
view.cycleHeader(column);
|
|
view.cycleHeader(column);
|
|
|
|
is(
|
|
columnElement.getAttribute("sortDirection"),
|
|
"",
|
|
"cycleHeader column sortDirection reset"
|
|
);
|
|
}
|
|
|
|
// checks if the current and selected rows are correct
|
|
// current is the index of the current row
|
|
// selected is an array of the indicies of the selected rows
|
|
// viewidx is the row that should be visible at the top of the tree
|
|
function testtag_tree_TreeSelection_State(
|
|
tree,
|
|
testid,
|
|
current,
|
|
selected,
|
|
viewidx
|
|
) {
|
|
var selection = tree.view.selection;
|
|
|
|
is(selection.count, selected.length, testid + " count");
|
|
is(tree.currentIndex, current, testid + " currentIndex");
|
|
is(selection.currentIndex, current, testid + " TreeSelection currentIndex");
|
|
if (viewidx !== null && viewidx !== undefined) {
|
|
is(tree.getFirstVisibleRow(), viewidx, testid + " first visible row");
|
|
}
|
|
|
|
var actualSelected = [];
|
|
var count = tree.view.rowCount;
|
|
for (var s = 0; s < count; s++) {
|
|
if (selection.isSelected(s)) {
|
|
actualSelected.push(s);
|
|
}
|
|
}
|
|
|
|
is(
|
|
compareArrays(selected, actualSelected),
|
|
true,
|
|
testid + " selection [" + selected + "]"
|
|
);
|
|
|
|
actualSelected = [];
|
|
var rangecount = selection.getRangeCount();
|
|
for (var r = 0; r < rangecount; r++) {
|
|
var start = {},
|
|
end = {};
|
|
selection.getRangeAt(r, start, end);
|
|
for (var rs = start.value; rs <= end.value; rs++) {
|
|
actualSelected.push(rs);
|
|
}
|
|
}
|
|
|
|
is(
|
|
compareArrays(selected, actualSelected),
|
|
true,
|
|
testid + " range selection [" + selected + "]"
|
|
);
|
|
}
|
|
|
|
function testtag_tree_column_reorder() {
|
|
// Make sure the tree is scrolled into the view, otherwise the test will
|
|
// fail
|
|
var testframe = window.parent.document.getElementById("testframe");
|
|
if (testframe) {
|
|
testframe.scrollIntoView();
|
|
}
|
|
|
|
var tree = document.getElementById("tree-column-reorder");
|
|
var numColumns = tree.columns.count;
|
|
|
|
var reference = [];
|
|
for (let i = 0; i < numColumns; i++) {
|
|
reference.push("col_" + i);
|
|
}
|
|
|
|
// Drag the first column to each position
|
|
for (let i = 0; i < numColumns - 1; i++) {
|
|
synthesizeColumnDrag(tree, i, i + 1, true);
|
|
arrayMove(reference, i, i + 1, true);
|
|
checkColumns(tree, reference, "drag first column right");
|
|
}
|
|
|
|
// And back
|
|
for (let i = numColumns - 1; i >= 1; i--) {
|
|
synthesizeColumnDrag(tree, i, i - 1, false);
|
|
arrayMove(reference, i, i - 1, false);
|
|
checkColumns(tree, reference, "drag last column left");
|
|
}
|
|
|
|
// Drag each column one column left
|
|
for (let i = 1; i < numColumns; i++) {
|
|
synthesizeColumnDrag(tree, i, i - 1, false);
|
|
arrayMove(reference, i, i - 1, false);
|
|
checkColumns(tree, reference, "drag each column left");
|
|
}
|
|
|
|
// And back
|
|
for (let i = numColumns - 2; i >= 0; i--) {
|
|
synthesizeColumnDrag(tree, i, i + 1, true);
|
|
arrayMove(reference, i, i + 1, true);
|
|
checkColumns(tree, reference, "drag each column right");
|
|
}
|
|
|
|
// Drag each column 5 to the right
|
|
for (let i = 0; i < numColumns - 5; i++) {
|
|
synthesizeColumnDrag(tree, i, i + 5, true);
|
|
arrayMove(reference, i, i + 5, true);
|
|
checkColumns(tree, reference, "drag each column 5 to the right");
|
|
}
|
|
|
|
// And to the left
|
|
for (let i = numColumns - 6; i >= 5; i--) {
|
|
synthesizeColumnDrag(tree, i, i - 5, false);
|
|
arrayMove(reference, i, i - 5, false);
|
|
checkColumns(tree, reference, "drag each column 5 to the left");
|
|
}
|
|
|
|
// Test that moving a column after itself does not move anything
|
|
synthesizeColumnDrag(tree, 0, 0, true);
|
|
checkColumns(tree, reference, "drag to itself");
|
|
is(document.treecolDragging, null, "drag to itself completed");
|
|
|
|
// XXX roc should this be here???
|
|
SimpleTest.finish();
|
|
}
|
|
|
|
function testtag_tree_wheel(aTree) {
|
|
const deltaModes = [
|
|
WheelEvent.DOM_DELTA_PIXEL, // 0
|
|
WheelEvent.DOM_DELTA_LINE, // 1
|
|
WheelEvent.DOM_DELTA_PAGE, // 2
|
|
];
|
|
function helper(aStart, aDelta, aIntDelta, aDeltaMode) {
|
|
aTree.scrollToRow(aStart);
|
|
var expected;
|
|
if (!aIntDelta) {
|
|
expected = aStart;
|
|
} else if (aDeltaMode != WheelEvent.DOM_DELTA_PAGE) {
|
|
expected = aStart + aIntDelta;
|
|
} else if (aIntDelta > 0) {
|
|
expected = aStart + aTree.getPageLength();
|
|
} else {
|
|
expected = aStart - aTree.getPageLength();
|
|
}
|
|
|
|
if (expected < 0) {
|
|
expected = 0;
|
|
}
|
|
if (expected > aTree.view.rowCount - aTree.getPageLength()) {
|
|
expected = aTree.view.rowCount - aTree.getPageLength();
|
|
}
|
|
synthesizeWheel(aTree.body, 1, 1, {
|
|
deltaMode: aDeltaMode,
|
|
deltaY: aDelta,
|
|
lineOrPageDeltaY: aIntDelta,
|
|
});
|
|
is(
|
|
aTree.getFirstVisibleRow(),
|
|
expected,
|
|
"testtag_tree_wheel: vertical, starting " +
|
|
aStart +
|
|
" delta " +
|
|
aDelta +
|
|
" lineOrPageDelta " +
|
|
aIntDelta +
|
|
" aDeltaMode " +
|
|
aDeltaMode
|
|
);
|
|
|
|
aTree.scrollToRow(aStart);
|
|
// Check that horizontal scrolling has no effect
|
|
synthesizeWheel(aTree.body, 1, 1, {
|
|
deltaMode: aDeltaMode,
|
|
deltaX: aDelta,
|
|
lineOrPageDeltaX: aIntDelta,
|
|
});
|
|
is(
|
|
aTree.getFirstVisibleRow(),
|
|
aStart,
|
|
"testtag_tree_wheel: horizontal, starting " +
|
|
aStart +
|
|
" delta " +
|
|
aDelta +
|
|
" lineOrPageDelta " +
|
|
aIntDelta +
|
|
" aDeltaMode " +
|
|
aDeltaMode
|
|
);
|
|
}
|
|
|
|
var defaultPrevented = 0;
|
|
|
|
function wheelListener() {
|
|
defaultPrevented++;
|
|
}
|
|
window.addEventListener("wheel", wheelListener);
|
|
|
|
deltaModes.forEach(function (aDeltaMode) {
|
|
var delta = aDeltaMode == WheelEvent.DOM_DELTA_PIXEL ? 5.0 : 0.3;
|
|
helper(2, -delta, 0, aDeltaMode);
|
|
helper(2, -delta, -1, aDeltaMode);
|
|
helper(2, delta, 0, aDeltaMode);
|
|
helper(2, delta, 1, aDeltaMode);
|
|
helper(2, -2 * delta, 0, aDeltaMode);
|
|
helper(2, -2 * delta, -1, aDeltaMode);
|
|
helper(2, 2 * delta, 0, aDeltaMode);
|
|
helper(2, 2 * delta, 1, aDeltaMode);
|
|
});
|
|
|
|
window.removeEventListener("wheel", wheelListener);
|
|
is(defaultPrevented, 48, "wheel event default prevented");
|
|
}
|
|
|
|
async function testtag_tree_scroll() {
|
|
const tree = document.querySelector("tree");
|
|
|
|
info("Scroll down with the content scrollbar at the top");
|
|
await doScrollTest({
|
|
tree,
|
|
initialTreeScrollRow: 0,
|
|
initialContainerScrollTop: 0,
|
|
scrollDelta: 10,
|
|
isTreeScrollExpected: true,
|
|
});
|
|
|
|
info("Scroll down with the content scrollbar at the middle");
|
|
await doScrollTest({
|
|
tree,
|
|
initialTreeScrollRow: 3,
|
|
initialContainerScrollTop: 0,
|
|
scrollDelta: 10,
|
|
isTreeScrollExpected: true,
|
|
});
|
|
|
|
info("Scroll down with the content scrollbar at the bottom");
|
|
await doScrollTest({
|
|
tree,
|
|
initialTreeScrollRow: 9,
|
|
initialContainerScrollTop: 0,
|
|
scrollDelta: 10,
|
|
isTreeScrollExpected: false,
|
|
});
|
|
|
|
info("Scroll up with the content scrollbar at the bottom");
|
|
await doScrollTest({
|
|
tree,
|
|
initialTreeScrollRow: 9,
|
|
initialContainerScrollTop: 50,
|
|
scrollDelta: -10,
|
|
isTreeScrollExpected: true,
|
|
});
|
|
|
|
info("Scroll up with the content scrollbar at the middle");
|
|
await doScrollTest({
|
|
tree,
|
|
initialTreeScrollRow: 5,
|
|
initialContainerScrollTop: 50,
|
|
scrollDelta: -10,
|
|
isTreeScrollExpected: true,
|
|
});
|
|
|
|
info("Scroll up with the content scrollbar at the top");
|
|
await doScrollTest({
|
|
tree,
|
|
initialTreeScrollRow: 0,
|
|
initialContainerScrollTop: 50,
|
|
scrollDelta: -10,
|
|
isTreeScrollExpected: false,
|
|
});
|
|
|
|
info("Check whether the tree is not scrolled when the parent is scrolling");
|
|
await doScrollWhileScrollingParent(tree);
|
|
|
|
info(
|
|
"Check whether the tree component consumes wheel events even if the scroll is located at edge as long as the events are handled as the same series"
|
|
);
|
|
await doScrollInSameSeries({
|
|
tree,
|
|
initialTreeScrollRow: 0,
|
|
initialContainerScrollTop: 0,
|
|
scrollDelta: 10,
|
|
});
|
|
await doScrollInSameSeries({
|
|
tree,
|
|
initialTreeScrollRow: 9,
|
|
initialContainerScrollTop: 50,
|
|
scrollDelta: -10,
|
|
});
|
|
|
|
SimpleTest.finish();
|
|
}
|
|
|
|
async function doScrollInSameSeries({
|
|
tree,
|
|
initialTreeScrollRow,
|
|
initialContainerScrollTop,
|
|
scrollDelta,
|
|
}) {
|
|
// Set enough value to mousewheel.scroll_series_timeout pref to ensure the wheel
|
|
// event fired as the same series.
|
|
Services.prefs.setIntPref("mousewheel.scroll_series_timeout", 1000);
|
|
|
|
const scrollbar = tree.shadowRoot.querySelector(
|
|
"scrollbar[orient='vertical']"
|
|
);
|
|
const parent = tree.parentElement;
|
|
|
|
tree.scrollToRow(initialTreeScrollRow);
|
|
parent.scrollTop = initialContainerScrollTop;
|
|
|
|
// Scroll until the scrollbar was moved to the specified amount.
|
|
await SimpleTest.promiseWaitForCondition(async () => {
|
|
await nativeScroll(tree, 10, 10, scrollDelta);
|
|
const curpos = scrollbar.getAttribute("curpos");
|
|
return (
|
|
(scrollDelta < 0 && curpos == 0) ||
|
|
(scrollDelta > 0 && curpos == scrollbar.getAttribute("maxpos"))
|
|
);
|
|
});
|
|
|
|
// More scroll as the same series.
|
|
for (let i = 0; i < 10; i++) {
|
|
await nativeScroll(tree, 10, 10, scrollDelta);
|
|
}
|
|
|
|
is(
|
|
parent.scrollTop,
|
|
initialContainerScrollTop,
|
|
"The wheel events are condumed in tree component"
|
|
);
|
|
const utils = SpecialPowers.getDOMWindowUtils(window);
|
|
ok(!utils.getWheelScrollTarget(), "The parent should not handle the event");
|
|
|
|
Services.prefs.clearUserPref("mousewheel.scroll_series_timeout");
|
|
}
|
|
|
|
async function doScrollWhileScrollingParent(tree) {
|
|
// Set enough value to mousewheel.scroll_series_timeout pref to ensure the wheel
|
|
// event fired as the same series.
|
|
Services.prefs.setIntPref("mousewheel.scroll_series_timeout", 1000);
|
|
|
|
const scrollbar = tree.shadowRoot.querySelector(
|
|
"scrollbar[orient='vertical']"
|
|
);
|
|
const parent = tree.parentElement;
|
|
|
|
// Set initial scroll amount.
|
|
tree.scrollToRow(0);
|
|
parent.scrollTop = 0;
|
|
|
|
const scrollAmount = scrollbar.getAttribute("curpos");
|
|
|
|
// Scroll parent from top to bottom.
|
|
await SimpleTest.promiseWaitForCondition(async () => {
|
|
await nativeScroll(parent, 10, 10, 10);
|
|
return parent.scrollTop === parent.scrollTopMax;
|
|
});
|
|
|
|
is(
|
|
scrollAmount,
|
|
scrollbar.getAttribute("curpos"),
|
|
"The tree should not be scrolled"
|
|
);
|
|
|
|
const utils = SpecialPowers.getDOMWindowUtils(window);
|
|
await SimpleTest.promiseWaitForCondition(() => !utils.getWheelScrollTarget());
|
|
Services.prefs.clearUserPref("mousewheel.scroll_series_timeout");
|
|
}
|
|
|
|
async function doScrollTest({
|
|
tree,
|
|
initialTreeScrollRow,
|
|
initialContainerScrollTop,
|
|
scrollDelta,
|
|
isTreeScrollExpected,
|
|
}) {
|
|
const scrollbar = tree.shadowRoot.querySelector(
|
|
"scrollbar[orient='vertical']"
|
|
);
|
|
const container = tree.parentElement;
|
|
|
|
// Set initial scroll amount.
|
|
tree.scrollToRow(initialTreeScrollRow);
|
|
container.scrollTop = initialContainerScrollTop;
|
|
|
|
const treeScrollAmount = scrollbar.getAttribute("curpos");
|
|
const containerScrollAmount = container.scrollTop;
|
|
|
|
// Wait until changing either scroll.
|
|
await SimpleTest.promiseWaitForCondition(async () => {
|
|
await nativeScroll(tree, 10, 10, scrollDelta);
|
|
return (
|
|
treeScrollAmount !== scrollbar.getAttribute("curpos") ||
|
|
containerScrollAmount !== container.scrollTop
|
|
);
|
|
});
|
|
|
|
is(
|
|
treeScrollAmount !== scrollbar.getAttribute("curpos"),
|
|
isTreeScrollExpected,
|
|
"Scroll of tree is expected"
|
|
);
|
|
is(
|
|
containerScrollAmount !== container.scrollTop,
|
|
!isTreeScrollExpected,
|
|
"Scroll of container is expected"
|
|
);
|
|
|
|
// Wait until finishing wheel scroll transaction.
|
|
const utils = SpecialPowers.getDOMWindowUtils(window);
|
|
await SimpleTest.promiseWaitForCondition(() => !utils.getWheelScrollTarget());
|
|
}
|
|
|
|
async function nativeScroll(component, offsetX, offsetY, scrollDelta) {
|
|
const utils = SpecialPowers.getDOMWindowUtils(window);
|
|
const x = component.screenX + offsetX;
|
|
const y = component.screenY + offsetY;
|
|
|
|
// Mouse move event.
|
|
await new Promise(resolve => {
|
|
info("waiting for mousemove");
|
|
window.addEventListener("mousemove", resolve, { once: true });
|
|
utils.sendNativeMouseEvent(
|
|
x * window.devicePixelRatio,
|
|
y * window.devicePixelRatio,
|
|
utils.NATIVE_MOUSE_MESSAGE_MOVE,
|
|
0,
|
|
{},
|
|
component
|
|
);
|
|
});
|
|
|
|
// Wheel event.
|
|
await new Promise(resolve => {
|
|
info("waiting for wheel");
|
|
window.addEventListener("wheel", resolve, { once: true });
|
|
utils.sendNativeMouseScrollEvent(
|
|
x * window.devicePixelRatio,
|
|
y * window.devicePixelRatio,
|
|
// nativeVerticalWheelEventMsg is defined in apz_test_native_event_utils.js
|
|
// eslint-disable-next-line no-undef
|
|
nativeVerticalWheelEventMsg(),
|
|
0,
|
|
// nativeScrollUnits is defined in apz_test_native_event_utils.js
|
|
// eslint-disable-next-line no-undef
|
|
-nativeScrollUnits(component, scrollDelta),
|
|
0,
|
|
0,
|
|
0,
|
|
component
|
|
);
|
|
});
|
|
|
|
info("waiting for apz");
|
|
// promiseApzFlushedRepaints is defined in apz_test_utils.js
|
|
// eslint-disable-next-line no-undef
|
|
await promiseApzFlushedRepaints();
|
|
}
|
|
|
|
function synthesizeColumnDrag(
|
|
aTree,
|
|
aMouseDownColumnNumber,
|
|
aMouseUpColumnNumber,
|
|
aAfter
|
|
) {
|
|
var columns = getSortedColumnArray(aTree);
|
|
|
|
var down = columns[aMouseDownColumnNumber].element;
|
|
var up = columns[aMouseUpColumnNumber].element;
|
|
|
|
// Target the initial mousedown in the middle of the column header so we
|
|
// avoid the extra hit test space given to the splitter
|
|
var columnWidth = down.getBoundingClientRect().width;
|
|
var splitterHitWidth = columnWidth / 2;
|
|
synthesizeMouse(down, splitterHitWidth, 3, { type: "mousedown" });
|
|
|
|
var offsetX = 0;
|
|
if (aAfter) {
|
|
offsetX = columnWidth;
|
|
}
|
|
|
|
if (aMouseUpColumnNumber > aMouseDownColumnNumber) {
|
|
for (let i = aMouseDownColumnNumber; i <= aMouseUpColumnNumber; i++) {
|
|
let move = columns[i].element;
|
|
synthesizeMouse(move, offsetX, 3, { type: "mousemove" });
|
|
}
|
|
} else {
|
|
for (let i = aMouseDownColumnNumber; i >= aMouseUpColumnNumber; i--) {
|
|
let move = columns[i].element;
|
|
synthesizeMouse(move, offsetX, 3, { type: "mousemove" });
|
|
}
|
|
}
|
|
|
|
synthesizeMouse(up, offsetX, 3, { type: "mouseup" });
|
|
}
|
|
|
|
function arrayMove(aArray, aFrom, aTo, aAfter) {
|
|
var o = aArray.splice(aFrom, 1)[0];
|
|
if (aTo > aFrom) {
|
|
aTo--;
|
|
}
|
|
|
|
if (aAfter) {
|
|
aTo++;
|
|
}
|
|
|
|
aArray.splice(aTo, 0, o);
|
|
}
|
|
|
|
function getSortedColumnArray(aTree) {
|
|
var columns = aTree.columns;
|
|
var array = [];
|
|
for (let i = 0; i < columns.length; i++) {
|
|
array.push(columns.getColumnAt(i));
|
|
}
|
|
|
|
array.sort(function (a, b) {
|
|
var o1 = parseInt(a.element.style.order);
|
|
var o2 = parseInt(b.element.style.order);
|
|
return o1 - o2;
|
|
});
|
|
return array;
|
|
}
|
|
|
|
function checkColumns(aTree, aReference, aMessage) {
|
|
var columns = getSortedColumnArray(aTree);
|
|
var ids = [];
|
|
columns.forEach(function (e) {
|
|
ids.push(e.element.id);
|
|
});
|
|
is(compareArrays(ids, aReference), true, aMessage);
|
|
}
|
|
|
|
function mouseOnCell(tree, row, column, testname) {
|
|
var rect = tree.getCoordsForCellItem(row, column, "text");
|
|
|
|
synthesizeMouseExpectEvent(
|
|
tree.body,
|
|
rect.x,
|
|
rect.y,
|
|
{},
|
|
tree,
|
|
"select",
|
|
testname
|
|
);
|
|
}
|
|
|
|
function mouseClickOnColumnHeader(
|
|
aColumns,
|
|
aColumnIndex,
|
|
aButton,
|
|
aClickCount
|
|
) {
|
|
var columnHeader = aColumns[aColumnIndex].element;
|
|
var columnHeaderRect = columnHeader.getBoundingClientRect();
|
|
var columnWidth = columnHeaderRect.right - columnHeaderRect.left;
|
|
// For multiple click we send separate click events, with increasing
|
|
// clickCount. This simulates the common behavior of multiple clicks.
|
|
for (let i = 1; i <= aClickCount; i++) {
|
|
// Target the middle of the column header.
|
|
synthesizeMouse(columnHeader, columnWidth / 2, 3, {
|
|
button: aButton,
|
|
clickCount: i,
|
|
});
|
|
}
|
|
}
|
|
|
|
function mouseDblClickOnCell(tree, row, column) {
|
|
// select the row we will edit
|
|
var selection = tree.view.selection;
|
|
selection.select(row);
|
|
tree.ensureRowIsVisible(row);
|
|
|
|
// get cell coordinates
|
|
var rect = tree.getCoordsForCellItem(row, column, "text");
|
|
|
|
synthesizeMouse(tree.body, rect.x, rect.y, { clickCount: 2 });
|
|
}
|
|
|
|
function compareArrays(arr1, arr2) {
|
|
if (arr1.length != arr2.length) {
|
|
return false;
|
|
}
|
|
|
|
for (let i = 0; i < arr1.length; i++) {
|
|
if (arr1[i] != arr2[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function convertDOMtoTreeRowInfo(treechildren, level, rowidx) {
|
|
var obj = { rows: [] };
|
|
|
|
var parentidx = rowidx.value;
|
|
|
|
treechildren = treechildren.childNodes;
|
|
for (var r = 0; r < treechildren.length; r++) {
|
|
rowidx.value++;
|
|
|
|
var treeitem = treechildren[r];
|
|
if (treeitem.hasChildNodes()) {
|
|
var treerow = treeitem.firstChild;
|
|
var cellInfo = [];
|
|
for (var c = 0; c < treerow.childNodes.length; c++) {
|
|
var cell = treerow.childNodes[c];
|
|
cellInfo.push({
|
|
label: cell.getAttribute("label") || "",
|
|
value: cell.getAttribute("value") || "",
|
|
properties: cell.getAttribute("properties") || "",
|
|
editable: cell.getAttribute("editable") != "false",
|
|
selectable: cell.getAttribute("selectable") != "false",
|
|
image: cell.getAttribute("src") || "",
|
|
mode: cell.hasAttribute("mode")
|
|
? parseInt(cell.getAttribute("mode"))
|
|
: 3,
|
|
});
|
|
}
|
|
|
|
var descendants = treeitem.lastChild;
|
|
var children =
|
|
treerow == descendants
|
|
? null
|
|
: convertDOMtoTreeRowInfo(descendants, level + 1, rowidx);
|
|
obj.rows.push({
|
|
cells: cellInfo,
|
|
properties: treerow.getAttribute("properties") || "",
|
|
container: treeitem.getAttribute("container") == "true",
|
|
separator: treeitem.localName == "treeseparator",
|
|
children,
|
|
level,
|
|
parent: parentidx,
|
|
});
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|