diff options
Diffstat (limited to '')
-rw-r--r-- | accessible/tests/mochitest/table.js | 851 |
1 files changed, 851 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/table.js b/accessible/tests/mochitest/table.js new file mode 100644 index 0000000000..9e4989cced --- /dev/null +++ b/accessible/tests/mochitest/table.js @@ -0,0 +1,851 @@ +/** + * This file provides set of helper functions to test nsIAccessibleTable + * interface. + * + * Required: + * common.js + * role.js + * states.js + */ +/* import-globals-from common.js */ +/* import-globals-from role.js */ +/* import-globals-from states.js */ + +/** + * Constants used to describe cells array. + */ +const kDataCell = 1; // Indicates the cell is origin data cell +const kRowHeaderCell = 2; // Indicates the cell is row header cell +const kColHeaderCell = 4; // Indicated the cell is column header cell +const kOrigin = kDataCell | kRowHeaderCell | kColHeaderCell; + +const kRowSpanned = 8; // Indicates the cell is not origin and row spanned +const kColSpanned = 16; // Indicates the cell is not origin and column spanned +const kSpanned = kRowSpanned | kColSpanned; + +/** + * Constants to define column header type. + */ +const kNoColumnHeader = 0; +const kListboxColumnHeader = 1; +const kTreeColumnHeader = 2; + +/** + * Constants to define table type. + */ +const kTable = 0; +const kTreeTable = 1; +const kMathTable = 2; + +/** + * Test table structure and related methods. + * + * @param aIdentifier [in] table accessible identifier + * @param aCellsArray [in] two dimensional array (row X columns) of + * cell types (see constants defined above). + * @param aColHeaderType [in] specifies wether column header cells are + * arranged into the list. + * @param aCaption [in] caption text if any + * @param aSummary [in] summary text if any + * @param aTableType [in] specifies the table type. + * @param aRowRoles [in] array of row roles. + */ +function testTableStruct( + aIdentifier, + aCellsArray, + aColHeaderType, + aCaption, + aSummary, + aTableType, + aRowRoles +) { + var tableNode = getNode(aIdentifier); + var isGrid = + tableNode.getAttribute("role") == "grid" || + tableNode.getAttribute("role") == "treegrid" || + tableNode.localName == "tree"; + + var rowCount = aCellsArray.length; + var colsCount = aCellsArray[0] ? aCellsArray[0].length : 0; + + // Test table accessible tree. + var tableObj = { + children: [], + }; + switch (aTableType) { + case kTable: + tableObj.role = ROLE_TABLE; + break; + case kTreeTable: + tableObj.role = ROLE_TREE_TABLE; + break; + case kMathTable: + tableObj.role = ROLE_MATHML_TABLE; + break; + } + + // caption accessible handling + if (aCaption) { + var captionObj = { + role: ROLE_CAPTION, + children: [ + { + role: ROLE_TEXT_LEAF, + name: aCaption, + }, + ], + }; + + tableObj.children.push(captionObj); + } + + // special types of column headers handling + if (aColHeaderType) { + var headersObj = { + role: ROLE_LIST, + children: [], + }; + + for (let idx = 0; idx < colsCount; idx++) { + var headerCellObj = { + role: ROLE_COLUMNHEADER, + }; + headersObj.children.push(headerCellObj); + } + + if (aColHeaderType == kTreeColumnHeader) { + headersObj.children.push({ + role: ROLE_PUSHBUTTON, + }); + headersObj.children.push({ + role: ROLE_MENUPOPUP, + }); + } + + tableObj.children.push(headersObj); + } + + // rows and cells accessibles + for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { + let rowObj = { + role: aRowRoles ? aRowRoles[rowIdx] : ROLE_ROW, + children: [], + }; + + for (let colIdx = 0; colIdx < colsCount; colIdx++) { + let celltype = aCellsArray[rowIdx][colIdx]; + + var role = ROLE_NOTHING; + switch (celltype) { + case kDataCell: + role = + aTableType == kMathTable + ? ROLE_MATHML_CELL + : isGrid + ? ROLE_GRID_CELL + : ROLE_CELL; + break; + case kRowHeaderCell: + role = ROLE_ROWHEADER; + break; + case kColHeaderCell: + role = ROLE_COLUMNHEADER; + break; + } + + if (role != ROLE_NOTHING) { + var cellObj = { role }; + rowObj.children.push(cellObj); + } + } + + tableObj.children.push(rowObj); + } + + testAccessibleTree(aIdentifier, tableObj); + + // Test table table interface. + var table = getAccessible(aIdentifier, [nsIAccessibleTable]); + + // summary + if (aSummary) { + is( + table.summary, + aSummary, + "Wrong summary of the table " + prettyName(aIdentifier) + ); + } + + // rowCount and columnCount + is( + table.rowCount, + rowCount, + "Wrong rows count of " + prettyName(aIdentifier) + ); + is( + table.columnCount, + colsCount, + "Wrong columns count of " + prettyName(aIdentifier) + ); + + // rows and columns extents + for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { + for (let colIdx = 0; colIdx < colsCount; colIdx++) { + let celltype = aCellsArray[rowIdx][colIdx]; + if (celltype & kOrigin) { + // table getRowExtentAt + var rowExtent = table.getRowExtentAt(rowIdx, colIdx); + let idx; + /* eslint-disable no-empty */ + for ( + idx = rowIdx + 1; + idx < rowCount && aCellsArray[idx][colIdx] & kRowSpanned; + idx++ + ) {} + /* eslint-enable no-empty */ + + var expectedRowExtent = idx - rowIdx; + is( + rowExtent, + expectedRowExtent, + "getRowExtentAt: Wrong number of spanned rows at (" + + rowIdx + + ", " + + colIdx + + ") for " + + prettyName(aIdentifier) + ); + + // table getColumnExtentAt + var colExtent = table.getColumnExtentAt(rowIdx, colIdx); + /* eslint-disable no-empty */ + for ( + idx = colIdx + 1; + idx < colsCount && aCellsArray[rowIdx][idx] & kColSpanned; + idx++ + ) {} + /* eslint-enable no-empty */ + + var expectedColExtent = idx - colIdx; + is( + colExtent, + expectedColExtent, + "getColumnExtentAt: Wrong number of spanned columns at (" + + rowIdx + + ", " + + colIdx + + ") for " + + prettyName(aIdentifier) + ); + + // cell rowExtent and columnExtent + var cell = getAccessible(table.getCellAt(rowIdx, colIdx), [ + nsIAccessibleTableCell, + ]); + + is( + cell.rowExtent, + expectedRowExtent, + "rowExtent: Wrong number of spanned rows at (" + + rowIdx + + ", " + + colIdx + + ") for " + + prettyName(aIdentifier) + ); + + is( + cell.columnExtent, + expectedColExtent, + "columnExtent: Wrong number of spanned column at (" + + rowIdx + + ", " + + colIdx + + ") for " + + prettyName(aIdentifier) + ); + } + } + } +} + +/** + * Test table indexes. + * + * @param aIdentifier [in] table accessible identifier + * @param aIdxes [in] two dimensional array of cell indexes + */ +function testTableIndexes(aIdentifier, aIdxes) { + var tableAcc = getAccessible(aIdentifier, [nsIAccessibleTable]); + if (!tableAcc) { + return; + } + + var obtainedRowIdx, obtainedColIdx, obtainedIdx; + var cellAcc; + + var id = prettyName(aIdentifier); + + var rowCount = aIdxes.length; + for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) { + var colCount = aIdxes[rowIdx].length; + for (var colIdx = 0; colIdx < colCount; colIdx++) { + var idx = aIdxes[rowIdx][colIdx]; + + // getCellAt + try { + cellAcc = null; + cellAcc = tableAcc.getCellAt(rowIdx, colIdx); + } catch (e) {} + + ok( + (idx != -1 && cellAcc) || (idx == -1 && !cellAcc), + id + + ": Can't get cell accessible at row = " + + rowIdx + + ", column = " + + colIdx + ); + + if (idx != -1) { + // getRowIndexAt + var origRowIdx = rowIdx; + while ( + origRowIdx > 0 && + aIdxes[rowIdx][colIdx] == aIdxes[origRowIdx - 1][colIdx] + ) { + origRowIdx--; + } + + try { + obtainedRowIdx = tableAcc.getRowIndexAt(idx); + } catch (e) { + ok( + false, + id + ": can't get row index for cell index " + idx + "," + e + ); + } + + is( + obtainedRowIdx, + origRowIdx, + id + ": row for index " + idx + " is not correct (getRowIndexAt)" + ); + + // getColumnIndexAt + var origColIdx = colIdx; + while ( + origColIdx > 0 && + aIdxes[rowIdx][colIdx] == aIdxes[rowIdx][origColIdx - 1] + ) { + origColIdx--; + } + + try { + obtainedColIdx = tableAcc.getColumnIndexAt(idx); + } catch (e) { + ok( + false, + id + ": can't get column index for cell index " + idx + "," + e + ); + } + + is( + obtainedColIdx, + origColIdx, + id + + ": column for index " + + idx + + " is not correct (getColumnIndexAt)" + ); + + // getRowAndColumnIndicesAt + var obtainedRowIdxObj = {}, + obtainedColIdxObj = {}; + try { + tableAcc.getRowAndColumnIndicesAt( + idx, + obtainedRowIdxObj, + obtainedColIdxObj + ); + } catch (e) { + ok( + false, + id + + ": can't get row and column indices for cell index " + + idx + + "," + + e + ); + } + + is( + obtainedRowIdxObj.value, + origRowIdx, + id + + ": row for index " + + idx + + " is not correct (getRowAndColumnIndicesAt)" + ); + is( + obtainedColIdxObj.value, + origColIdx, + id + + ": column for index " + + idx + + " is not correct (getRowAndColumnIndicesAt)" + ); + + if (cellAcc) { + var cellId = prettyName(cellAcc); + cellAcc = getAccessible(cellAcc, [nsIAccessibleTableCell]); + + // cell: 'table-cell-index' attribute + var attrs = cellAcc.attributes; + var strIdx = ""; + try { + strIdx = attrs.getStringProperty("table-cell-index"); + } catch (e) { + ok( + false, + cellId + + ": no cell index from object attributes on the cell accessible at index " + + idx + + "." + ); + } + + if (strIdx) { + is( + parseInt(strIdx), + idx, + cellId + + ": cell index from object attributes of cell accessible isn't corrent." + ); + } + + // cell: table + try { + is( + cellAcc.table, + tableAcc, + cellId + ": wrong table accessible for the cell." + ); + } catch (e) { + ok(false, cellId + ": can't get table accessible from the cell."); + } + + // cell: getRowIndex + try { + obtainedRowIdx = cellAcc.rowIndex; + } catch (e) { + ok( + false, + cellId + + ": can't get row index of the cell at index " + + idx + + "," + + e + ); + } + + is( + obtainedRowIdx, + origRowIdx, + cellId + ": row for the cell at index " + idx + " is not correct" + ); + + // cell: getColumnIndex + try { + obtainedColIdx = cellAcc.columnIndex; + } catch (e) { + ok( + false, + cellId + + ": can't get column index of the cell at index " + + idx + + "," + + e + ); + } + + is( + obtainedColIdx, + origColIdx, + id + ": column for the cell at index " + idx + " is not correct" + ); + } + } + + // getCellIndexAt + try { + obtainedIdx = tableAcc.getCellIndexAt(rowIdx, colIdx); + } catch (e) { + obtainedIdx = -1; + } + + is( + obtainedIdx, + idx, + id + + ": row " + + rowIdx + + " /column " + + colIdx + + " and index " + + obtainedIdx + + " aren't inconsistent." + ); + } + } +} + +/** + * Test table getters selection methods. + * + * @param aIdentifier [in] table accessible identifier + * @param aCellsArray [in] two dimensional array (row X columns) of cells + * states (either boolean (selected/unselected) if cell is + * origin, otherwise kRowSpanned or kColSpanned constant). + * @param aMsg [in] text appended before every message + */ +function testTableSelection(aIdentifier, aCellsArray, aMsg) { + var msg = aMsg ? aMsg : ""; + var acc = getAccessible(aIdentifier, [nsIAccessibleTable]); + if (!acc) { + return; + } + + var rowCount = aCellsArray.length; + var colsCount = aCellsArray[0].length; + + // Columns selection tests. + var selCols = []; + + // isColumnSelected test + for (let colIdx = 0; colIdx < colsCount; colIdx++) { + var isColSelected = true; + for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { + if ( + !aCellsArray[rowIdx][colIdx] || + aCellsArray[rowIdx][colIdx] == undefined + ) { + isColSelected = false; + break; + } + } + + is( + acc.isColumnSelected(colIdx), + isColSelected, + msg + + "Wrong selection state of " + + colIdx + + " column for " + + prettyName(aIdentifier) + ); + + if (isColSelected) { + selCols.push(colIdx); + } + } + + // selectedColsCount test + is( + acc.selectedColumnCount, + selCols.length, + msg + "Wrong count of selected columns for " + prettyName(aIdentifier) + ); + + // getSelectedColumns test + var actualSelCols = acc.getSelectedColumnIndices(); + + var actualSelColsCount = actualSelCols.length; + is( + actualSelColsCount, + selCols.length, + msg + + "Wrong count of selected columns for " + + prettyName(aIdentifier) + + "from getSelectedColumns." + ); + + for (let i = 0; i < actualSelColsCount; i++) { + is( + actualSelCols[i], + selCols[i], + msg + "Column at index " + selCols[i] + " should be selected." + ); + } + + // Rows selection tests. + var selRows = []; + + // isRowSelected test + for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { + var isRowSelected = true; + for (let colIdx = 0; colIdx < colsCount; colIdx++) { + if ( + !aCellsArray[rowIdx][colIdx] || + aCellsArray[rowIdx][colIdx] == undefined + ) { + isRowSelected = false; + break; + } + } + + is( + acc.isRowSelected(rowIdx), + isRowSelected, + msg + + "Wrong selection state of " + + rowIdx + + " row for " + + prettyName(aIdentifier) + ); + + if (isRowSelected) { + selRows.push(rowIdx); + } + } + + // selectedRowCount test + is( + acc.selectedRowCount, + selRows.length, + msg + "Wrong count of selected rows for " + prettyName(aIdentifier) + ); + + // getSelectedRows test + var actualSelRows = acc.getSelectedRowIndices(); + + var actualSelrowCount = actualSelRows.length; + is( + actualSelrowCount, + selRows.length, + msg + + "Wrong count of selected rows for " + + prettyName(aIdentifier) + + "from getSelectedRows." + ); + + for (let i = 0; i < actualSelrowCount; i++) { + is( + actualSelRows[i], + selRows[i], + msg + "Row at index " + selRows[i] + " should be selected." + ); + } + + // Cells selection tests. + var selCells = []; + + // isCellSelected test + for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { + for (let colIdx = 0; colIdx < colsCount; colIdx++) { + if (aCellsArray[rowIdx][colIdx] & kSpanned) { + continue; + } + + var isSelected = !!aCellsArray[rowIdx][colIdx]; + is( + acc.isCellSelected(rowIdx, colIdx), + isSelected, + msg + + "Wrong selection state of cell at " + + rowIdx + + " row and " + + colIdx + + " column for " + + prettyName(aIdentifier) + ); + + if (aCellsArray[rowIdx][colIdx]) { + selCells.push(acc.getCellIndexAt(rowIdx, colIdx)); + } + } + } + + // selectedCellCount tests + is( + acc.selectedCellCount, + selCells.length, + msg + "Wrong count of selected cells for " + prettyName(aIdentifier) + ); + + // getSelectedCellIndices test + var actualSelCells = acc.getSelectedCellIndices(); + + var actualSelCellsCount = actualSelCells.length; + is( + actualSelCellsCount, + selCells.length, + msg + + "Wrong count of selected cells for " + + prettyName(aIdentifier) + + "from getSelectedCells." + ); + + for (let i = 0; i < actualSelCellsCount; i++) { + is( + actualSelCells[i], + selCells[i], + msg + + "getSelectedCellIndices: Cell at index " + + selCells[i] + + " should be selected." + ); + } + + // selectedCells and isSelected tests + var actualSelCellsArray = acc.selectedCells; + for (let i = 0; i < actualSelCellsCount; i++) { + var actualSelCellAccessible = actualSelCellsArray.queryElementAt( + i, + nsIAccessibleTableCell + ); + + let colIdx = acc.getColumnIndexAt(selCells[i]); + let rowIdx = acc.getRowIndexAt(selCells[i]); + var expectedSelCellAccessible = acc.getCellAt(rowIdx, colIdx); + + is( + actualSelCellAccessible, + expectedSelCellAccessible, + msg + + "getSelectedCells: Cell at index " + + selCells[i] + + " should be selected." + ); + + ok( + actualSelCellAccessible.isSelected(), + "isSelected: Cell at index " + selCells[i] + " should be selected." + ); + } + + // selected states tests + for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { + for (let colIdx = 0; colIdx < colsCount; colIdx++) { + if (aCellsArray[rowIdx][colIdx] & kSpanned) { + continue; + } + + var cell = acc.getCellAt(rowIdx, colIdx); + var isSel = aCellsArray[rowIdx][colIdx]; + if (isSel == undefined) { + testStates(cell, 0, 0, STATE_SELECTABLE | STATE_SELECTED); + } else if (isSel) { + testStates(cell, STATE_SELECTED); + } else { + testStates(cell, STATE_SELECTABLE, 0, STATE_SELECTED); + } + } + } +} + +/** + * Test columnHeaderCells and rowHeaderCells of accessible table. + */ +function testHeaderCells(aHeaderInfoMap) { + for (var testIdx = 0; testIdx < aHeaderInfoMap.length; testIdx++) { + var dataCellIdentifier = aHeaderInfoMap[testIdx].cell; + var dataCell = getAccessible(dataCellIdentifier, [nsIAccessibleTableCell]); + + // row header cells + var rowHeaderCells = aHeaderInfoMap[testIdx].rowHeaderCells; + var rowHeaderCellsCount = rowHeaderCells.length; + var actualRowHeaderCells = dataCell.rowHeaderCells; + var actualRowHeaderCellsCount = actualRowHeaderCells.length; + + is( + actualRowHeaderCellsCount, + rowHeaderCellsCount, + "Wrong number of row header cells for the cell " + + prettyName(dataCellIdentifier) + ); + + if (actualRowHeaderCellsCount == rowHeaderCellsCount) { + for (let idx = 0; idx < rowHeaderCellsCount; idx++) { + var rowHeaderCell = getAccessible(rowHeaderCells[idx]); + var actualRowHeaderCell = actualRowHeaderCells.queryElementAt( + idx, + nsIAccessible + ); + isObject( + actualRowHeaderCell, + rowHeaderCell, + "Wrong row header cell at index " + + idx + + " for the cell " + + dataCellIdentifier + ); + } + } + + // column header cells + var colHeaderCells = aHeaderInfoMap[testIdx].columnHeaderCells; + var colHeaderCellsCount = colHeaderCells.length; + var actualColHeaderCells = dataCell.columnHeaderCells; + var actualColHeaderCellsCount = actualColHeaderCells.length; + + is( + actualColHeaderCellsCount, + colHeaderCellsCount, + "Wrong number of column header cells for the cell " + + prettyName(dataCellIdentifier) + ); + + if (actualColHeaderCellsCount == colHeaderCellsCount) { + for (let idx = 0; idx < colHeaderCellsCount; idx++) { + var colHeaderCell = getAccessible(colHeaderCells[idx]); + var actualColHeaderCell = actualColHeaderCells.queryElementAt( + idx, + nsIAccessible + ); + isObject( + actualColHeaderCell, + colHeaderCell, + "Wrong column header cell at index " + + idx + + " for the cell " + + dataCellIdentifier + ); + } + } + } +} + +// ////////////////////////////////////////////////////////////////////////////// +// private implementation + +/** + * Return row and column of orig cell for the given spanned cell. + */ +function getOrigRowAndColumn(aCellsArray, aRowIdx, aColIdx) { + var cellState = aCellsArray[aRowIdx][aColIdx]; + + var origRowIdx = aRowIdx, + origColIdx = aColIdx; + if (cellState & kRowSpanned) { + for (var prevRowIdx = aRowIdx - 1; prevRowIdx >= 0; prevRowIdx--) { + let prevCellState = aCellsArray[prevRowIdx][aColIdx]; + if (!(prevCellState & kRowSpanned)) { + origRowIdx = prevRowIdx; + break; + } + } + } + + if (cellState & kColSpanned) { + for (var prevColIdx = aColIdx - 1; prevColIdx >= 0; prevColIdx--) { + let prevCellState = aCellsArray[aRowIdx][prevColIdx]; + if (!(prevCellState & kColSpanned)) { + origColIdx = prevColIdx; + break; + } + } + } + + return [origRowIdx, origColIdx]; +} |