table.js (21624B)
1 /** 2 * This file provides set of helper functions to test nsIAccessibleTable 3 * interface. 4 * 5 * Required: 6 * common.js 7 * role.js 8 * states.js 9 */ 10 /* import-globals-from common.js */ 11 /* import-globals-from role.js */ 12 /* import-globals-from states.js */ 13 14 /** 15 * Constants used to describe cells array. 16 */ 17 const kDataCell = 1; // Indicates the cell is origin data cell 18 const kRowHeaderCell = 2; // Indicates the cell is row header cell 19 const kColHeaderCell = 4; // Indicated the cell is column header cell 20 const kOrigin = kDataCell | kRowHeaderCell | kColHeaderCell; 21 22 const kRowSpanned = 8; // Indicates the cell is not origin and row spanned 23 const kColSpanned = 16; // Indicates the cell is not origin and column spanned 24 const kSpanned = kRowSpanned | kColSpanned; 25 26 /** 27 * Constants to define column header type. 28 */ 29 const kNoColumnHeader = 0; 30 const kListboxColumnHeader = 1; 31 const kTreeColumnHeader = 2; 32 33 /** 34 * Constants to define table type. 35 */ 36 const kTable = 0; 37 const kTreeTable = 1; 38 const kMathTable = 2; 39 40 /** 41 * Test table structure and related methods. 42 * 43 * @param aIdentifier [in] table accessible identifier 44 * @param aCellsArray [in] two dimensional array (row X columns) of 45 * cell types (see constants defined above). 46 * @param aColHeaderType [in] specifies wether column header cells are 47 * arranged into the list. 48 * @param aCaption [in] caption text if any 49 * @param aSummary [in] summary text if any 50 * @param aTableType [in] specifies the table type. 51 * @param aRowRoles [in] array of row roles. 52 */ 53 function testTableStruct( 54 aIdentifier, 55 aCellsArray, 56 aColHeaderType, 57 aCaption, 58 aSummary, 59 aTableType, 60 aRowRoles 61 ) { 62 var tableNode = getNode(aIdentifier); 63 var isGrid = 64 tableNode.getAttribute("role") == "grid" || 65 tableNode.getAttribute("role") == "treegrid" || 66 tableNode.localName == "tree"; 67 68 var rowCount = aCellsArray.length; 69 var colsCount = aCellsArray[0] ? aCellsArray[0].length : 0; 70 71 // Test table accessible tree. 72 var tableObj = { 73 children: [], 74 }; 75 switch (aTableType) { 76 case kTable: 77 tableObj.role = ROLE_TABLE; 78 break; 79 case kTreeTable: 80 tableObj.role = ROLE_TREE_TABLE; 81 break; 82 case kMathTable: 83 tableObj.role = ROLE_MATHML_TABLE; 84 break; 85 } 86 87 // caption accessible handling 88 if (aCaption) { 89 var captionObj = { 90 role: ROLE_CAPTION, 91 children: [ 92 { 93 role: ROLE_TEXT_LEAF, 94 name: aCaption, 95 }, 96 ], 97 }; 98 99 tableObj.children.push(captionObj); 100 } 101 102 // special types of column headers handling 103 if (aColHeaderType) { 104 var headersObj = { 105 role: ROLE_LIST, 106 children: [], 107 }; 108 109 for (let idx = 0; idx < colsCount; idx++) { 110 var headerCellObj = { 111 role: ROLE_COLUMNHEADER, 112 }; 113 headersObj.children.push(headerCellObj); 114 } 115 116 if (aColHeaderType == kTreeColumnHeader) { 117 headersObj.children.push({ 118 role: ROLE_PUSHBUTTON, 119 }); 120 headersObj.children.push({ 121 role: ROLE_MENUPOPUP, 122 }); 123 } 124 125 tableObj.children.push(headersObj); 126 } 127 128 // rows and cells accessibles 129 for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { 130 let rowObj = { 131 role: aRowRoles ? aRowRoles[rowIdx] : ROLE_ROW, 132 children: [], 133 }; 134 135 for (let colIdx = 0; colIdx < colsCount; colIdx++) { 136 let celltype = aCellsArray[rowIdx][colIdx]; 137 138 var role = ROLE_NOTHING; 139 switch (celltype) { 140 case kDataCell: 141 role = 142 aTableType == kMathTable 143 ? ROLE_MATHML_CELL 144 : isGrid 145 ? ROLE_GRID_CELL 146 : ROLE_CELL; 147 break; 148 case kRowHeaderCell: 149 role = ROLE_ROWHEADER; 150 break; 151 case kColHeaderCell: 152 role = ROLE_COLUMNHEADER; 153 break; 154 } 155 156 if (role != ROLE_NOTHING) { 157 var cellObj = { role }; 158 rowObj.children.push(cellObj); 159 } 160 } 161 162 tableObj.children.push(rowObj); 163 } 164 165 testAccessibleTree(aIdentifier, tableObj); 166 167 // Test table table interface. 168 var table = getAccessible(aIdentifier, [nsIAccessibleTable]); 169 170 // summary 171 if (aSummary) { 172 is( 173 table.summary, 174 aSummary, 175 "Wrong summary of the table " + prettyName(aIdentifier) 176 ); 177 } 178 179 // rowCount and columnCount 180 is( 181 table.rowCount, 182 rowCount, 183 "Wrong rows count of " + prettyName(aIdentifier) 184 ); 185 is( 186 table.columnCount, 187 colsCount, 188 "Wrong columns count of " + prettyName(aIdentifier) 189 ); 190 191 // rows and columns extents 192 for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { 193 for (let colIdx = 0; colIdx < colsCount; colIdx++) { 194 let celltype = aCellsArray[rowIdx][colIdx]; 195 if (celltype & kOrigin) { 196 // table getRowExtentAt 197 var rowExtent = table.getRowExtentAt(rowIdx, colIdx); 198 let idx; 199 /* eslint-disable no-empty */ 200 for ( 201 idx = rowIdx + 1; 202 idx < rowCount && aCellsArray[idx][colIdx] & kRowSpanned; 203 idx++ 204 ) {} 205 /* eslint-enable no-empty */ 206 207 var expectedRowExtent = idx - rowIdx; 208 is( 209 rowExtent, 210 expectedRowExtent, 211 "getRowExtentAt: Wrong number of spanned rows at (" + 212 rowIdx + 213 ", " + 214 colIdx + 215 ") for " + 216 prettyName(aIdentifier) 217 ); 218 219 // table getColumnExtentAt 220 var colExtent = table.getColumnExtentAt(rowIdx, colIdx); 221 /* eslint-disable no-empty */ 222 for ( 223 idx = colIdx + 1; 224 idx < colsCount && aCellsArray[rowIdx][idx] & kColSpanned; 225 idx++ 226 ) {} 227 /* eslint-enable no-empty */ 228 229 var expectedColExtent = idx - colIdx; 230 is( 231 colExtent, 232 expectedColExtent, 233 "getColumnExtentAt: Wrong number of spanned columns at (" + 234 rowIdx + 235 ", " + 236 colIdx + 237 ") for " + 238 prettyName(aIdentifier) 239 ); 240 241 // cell rowExtent and columnExtent 242 var cell = getAccessible(table.getCellAt(rowIdx, colIdx), [ 243 nsIAccessibleTableCell, 244 ]); 245 246 is( 247 cell.rowExtent, 248 expectedRowExtent, 249 "rowExtent: Wrong number of spanned rows at (" + 250 rowIdx + 251 ", " + 252 colIdx + 253 ") for " + 254 prettyName(aIdentifier) 255 ); 256 257 is( 258 cell.columnExtent, 259 expectedColExtent, 260 "columnExtent: Wrong number of spanned column at (" + 261 rowIdx + 262 ", " + 263 colIdx + 264 ") for " + 265 prettyName(aIdentifier) 266 ); 267 } 268 } 269 } 270 } 271 272 /** 273 * Test table indexes. 274 * 275 * @param aIdentifier [in] table accessible identifier 276 * @param aIdxes [in] two dimensional array of cell indexes 277 */ 278 function testTableIndexes(aIdentifier, aIdxes) { 279 var tableAcc = getAccessible(aIdentifier, [nsIAccessibleTable]); 280 if (!tableAcc) { 281 return; 282 } 283 284 var obtainedRowIdx, obtainedColIdx, obtainedIdx; 285 var cellAcc; 286 287 var id = prettyName(aIdentifier); 288 289 var rowCount = aIdxes.length; 290 for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) { 291 var colCount = aIdxes[rowIdx].length; 292 for (var colIdx = 0; colIdx < colCount; colIdx++) { 293 var idx = aIdxes[rowIdx][colIdx]; 294 295 // getCellAt 296 try { 297 cellAcc = null; 298 cellAcc = tableAcc.getCellAt(rowIdx, colIdx); 299 } catch (e) {} 300 301 ok( 302 (idx != -1 && cellAcc) || (idx == -1 && !cellAcc), 303 id + 304 ": Can't get cell accessible at row = " + 305 rowIdx + 306 ", column = " + 307 colIdx 308 ); 309 310 if (idx != -1) { 311 // getRowIndexAt 312 var origRowIdx = rowIdx; 313 while ( 314 origRowIdx > 0 && 315 aIdxes[rowIdx][colIdx] == aIdxes[origRowIdx - 1][colIdx] 316 ) { 317 origRowIdx--; 318 } 319 320 try { 321 obtainedRowIdx = tableAcc.getRowIndexAt(idx); 322 } catch (e) { 323 ok( 324 false, 325 id + ": can't get row index for cell index " + idx + "," + e 326 ); 327 } 328 329 is( 330 obtainedRowIdx, 331 origRowIdx, 332 id + ": row for index " + idx + " is not correct (getRowIndexAt)" 333 ); 334 335 // getColumnIndexAt 336 var origColIdx = colIdx; 337 while ( 338 origColIdx > 0 && 339 aIdxes[rowIdx][colIdx] == aIdxes[rowIdx][origColIdx - 1] 340 ) { 341 origColIdx--; 342 } 343 344 try { 345 obtainedColIdx = tableAcc.getColumnIndexAt(idx); 346 } catch (e) { 347 ok( 348 false, 349 id + ": can't get column index for cell index " + idx + "," + e 350 ); 351 } 352 353 is( 354 obtainedColIdx, 355 origColIdx, 356 id + 357 ": column for index " + 358 idx + 359 " is not correct (getColumnIndexAt)" 360 ); 361 362 // getRowAndColumnIndicesAt 363 var obtainedRowIdxObj = {}, 364 obtainedColIdxObj = {}; 365 try { 366 tableAcc.getRowAndColumnIndicesAt( 367 idx, 368 obtainedRowIdxObj, 369 obtainedColIdxObj 370 ); 371 } catch (e) { 372 ok( 373 false, 374 id + 375 ": can't get row and column indices for cell index " + 376 idx + 377 "," + 378 e 379 ); 380 } 381 382 is( 383 obtainedRowIdxObj.value, 384 origRowIdx, 385 id + 386 ": row for index " + 387 idx + 388 " is not correct (getRowAndColumnIndicesAt)" 389 ); 390 is( 391 obtainedColIdxObj.value, 392 origColIdx, 393 id + 394 ": column for index " + 395 idx + 396 " is not correct (getRowAndColumnIndicesAt)" 397 ); 398 399 if (cellAcc) { 400 var cellId = prettyName(cellAcc); 401 cellAcc = getAccessible(cellAcc, [nsIAccessibleTableCell]); 402 403 // cell: 'table-cell-index' attribute 404 var attrs = cellAcc.attributes; 405 var strIdx = ""; 406 try { 407 strIdx = attrs.getStringProperty("table-cell-index"); 408 } catch (e) { 409 ok( 410 false, 411 cellId + 412 ": no cell index from object attributes on the cell accessible at index " + 413 idx + 414 "." 415 ); 416 } 417 418 if (strIdx) { 419 is( 420 parseInt(strIdx), 421 idx, 422 cellId + 423 ": cell index from object attributes of cell accessible isn't corrent." 424 ); 425 } 426 427 // cell: table 428 try { 429 is( 430 cellAcc.table, 431 tableAcc, 432 cellId + ": wrong table accessible for the cell." 433 ); 434 } catch (e) { 435 ok(false, cellId + ": can't get table accessible from the cell."); 436 } 437 438 // cell: getRowIndex 439 try { 440 obtainedRowIdx = cellAcc.rowIndex; 441 } catch (e) { 442 ok( 443 false, 444 cellId + 445 ": can't get row index of the cell at index " + 446 idx + 447 "," + 448 e 449 ); 450 } 451 452 is( 453 obtainedRowIdx, 454 origRowIdx, 455 cellId + ": row for the cell at index " + idx + " is not correct" 456 ); 457 458 // cell: getColumnIndex 459 try { 460 obtainedColIdx = cellAcc.columnIndex; 461 } catch (e) { 462 ok( 463 false, 464 cellId + 465 ": can't get column index of the cell at index " + 466 idx + 467 "," + 468 e 469 ); 470 } 471 472 is( 473 obtainedColIdx, 474 origColIdx, 475 id + ": column for the cell at index " + idx + " is not correct" 476 ); 477 } 478 } 479 480 // getCellIndexAt 481 try { 482 obtainedIdx = tableAcc.getCellIndexAt(rowIdx, colIdx); 483 } catch (e) { 484 obtainedIdx = -1; 485 } 486 487 is( 488 obtainedIdx, 489 idx, 490 id + 491 ": row " + 492 rowIdx + 493 " /column " + 494 colIdx + 495 " and index " + 496 obtainedIdx + 497 " aren't inconsistent." 498 ); 499 } 500 } 501 } 502 503 /** 504 * Test table getters selection methods. 505 * 506 * @param aIdentifier [in] table accessible identifier 507 * @param aCellsArray [in] two dimensional array (row X columns) of cells 508 * states (either boolean (selected/unselected) if cell is 509 * origin, otherwise kRowSpanned or kColSpanned constant). 510 * @param aMsg [in] text appended before every message 511 */ 512 function testTableSelection(aIdentifier, aCellsArray, aMsg) { 513 var msg = aMsg ? aMsg : ""; 514 var acc = getAccessible(aIdentifier, [nsIAccessibleTable]); 515 if (!acc) { 516 return; 517 } 518 519 var rowCount = aCellsArray.length; 520 var colsCount = aCellsArray[0].length; 521 522 // Columns selection tests. 523 var selCols = []; 524 525 // isColumnSelected test 526 for (let colIdx = 0; colIdx < colsCount; colIdx++) { 527 var isColSelected = true; 528 for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { 529 if ( 530 !aCellsArray[rowIdx][colIdx] || 531 aCellsArray[rowIdx][colIdx] == undefined 532 ) { 533 isColSelected = false; 534 break; 535 } 536 } 537 538 is( 539 acc.isColumnSelected(colIdx), 540 isColSelected, 541 msg + 542 "Wrong selection state of " + 543 colIdx + 544 " column for " + 545 prettyName(aIdentifier) 546 ); 547 548 if (isColSelected) { 549 selCols.push(colIdx); 550 } 551 } 552 553 // selectedColsCount test 554 is( 555 acc.selectedColumnCount, 556 selCols.length, 557 msg + "Wrong count of selected columns for " + prettyName(aIdentifier) 558 ); 559 560 // getSelectedColumns test 561 var actualSelCols = acc.getSelectedColumnIndices(); 562 563 var actualSelColsCount = actualSelCols.length; 564 is( 565 actualSelColsCount, 566 selCols.length, 567 msg + 568 "Wrong count of selected columns for " + 569 prettyName(aIdentifier) + 570 "from getSelectedColumns." 571 ); 572 573 for (let i = 0; i < actualSelColsCount; i++) { 574 is( 575 actualSelCols[i], 576 selCols[i], 577 msg + "Column at index " + selCols[i] + " should be selected." 578 ); 579 } 580 581 // Rows selection tests. 582 var selRows = []; 583 584 // isRowSelected test 585 for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { 586 var isRowSelected = true; 587 for (let colIdx = 0; colIdx < colsCount; colIdx++) { 588 if ( 589 !aCellsArray[rowIdx][colIdx] || 590 aCellsArray[rowIdx][colIdx] == undefined 591 ) { 592 isRowSelected = false; 593 break; 594 } 595 } 596 597 is( 598 acc.isRowSelected(rowIdx), 599 isRowSelected, 600 msg + 601 "Wrong selection state of " + 602 rowIdx + 603 " row for " + 604 prettyName(aIdentifier) 605 ); 606 607 if (isRowSelected) { 608 selRows.push(rowIdx); 609 } 610 } 611 612 // selectedRowCount test 613 is( 614 acc.selectedRowCount, 615 selRows.length, 616 msg + "Wrong count of selected rows for " + prettyName(aIdentifier) 617 ); 618 619 // getSelectedRows test 620 var actualSelRows = acc.getSelectedRowIndices(); 621 622 var actualSelrowCount = actualSelRows.length; 623 is( 624 actualSelrowCount, 625 selRows.length, 626 msg + 627 "Wrong count of selected rows for " + 628 prettyName(aIdentifier) + 629 "from getSelectedRows." 630 ); 631 632 for (let i = 0; i < actualSelrowCount; i++) { 633 is( 634 actualSelRows[i], 635 selRows[i], 636 msg + "Row at index " + selRows[i] + " should be selected." 637 ); 638 } 639 640 // Cells selection tests. 641 var selCells = []; 642 643 // isCellSelected test 644 for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { 645 for (let colIdx = 0; colIdx < colsCount; colIdx++) { 646 if (aCellsArray[rowIdx][colIdx] & kSpanned) { 647 continue; 648 } 649 650 var isSelected = !!aCellsArray[rowIdx][colIdx]; 651 is( 652 acc.isCellSelected(rowIdx, colIdx), 653 isSelected, 654 msg + 655 "Wrong selection state of cell at " + 656 rowIdx + 657 " row and " + 658 colIdx + 659 " column for " + 660 prettyName(aIdentifier) 661 ); 662 663 if (aCellsArray[rowIdx][colIdx]) { 664 selCells.push(acc.getCellIndexAt(rowIdx, colIdx)); 665 } 666 } 667 } 668 669 // selectedCellCount tests 670 is( 671 acc.selectedCellCount, 672 selCells.length, 673 msg + "Wrong count of selected cells for " + prettyName(aIdentifier) 674 ); 675 676 // getSelectedCellIndices test 677 var actualSelCells = acc.getSelectedCellIndices(); 678 679 var actualSelCellsCount = actualSelCells.length; 680 is( 681 actualSelCellsCount, 682 selCells.length, 683 msg + 684 "Wrong count of selected cells for " + 685 prettyName(aIdentifier) + 686 "from getSelectedCells." 687 ); 688 689 for (let i = 0; i < actualSelCellsCount; i++) { 690 is( 691 actualSelCells[i], 692 selCells[i], 693 msg + 694 "getSelectedCellIndices: Cell at index " + 695 selCells[i] + 696 " should be selected." 697 ); 698 } 699 700 // selectedCells and isSelected tests 701 var actualSelCellsArray = acc.selectedCells; 702 for (let i = 0; i < actualSelCellsCount; i++) { 703 var actualSelCellAccessible = actualSelCellsArray.queryElementAt( 704 i, 705 nsIAccessibleTableCell 706 ); 707 708 let colIdx = acc.getColumnIndexAt(selCells[i]); 709 let rowIdx = acc.getRowIndexAt(selCells[i]); 710 var expectedSelCellAccessible = acc.getCellAt(rowIdx, colIdx); 711 712 is( 713 actualSelCellAccessible, 714 expectedSelCellAccessible, 715 msg + 716 "getSelectedCells: Cell at index " + 717 selCells[i] + 718 " should be selected." 719 ); 720 721 ok( 722 actualSelCellAccessible.isSelected(), 723 "isSelected: Cell at index " + selCells[i] + " should be selected." 724 ); 725 } 726 727 // selected states tests 728 for (let rowIdx = 0; rowIdx < rowCount; rowIdx++) { 729 for (let colIdx = 0; colIdx < colsCount; colIdx++) { 730 if (aCellsArray[rowIdx][colIdx] & kSpanned) { 731 continue; 732 } 733 734 var cell = acc.getCellAt(rowIdx, colIdx); 735 var isSel = aCellsArray[rowIdx][colIdx]; 736 if (isSel == undefined) { 737 testStates(cell, 0, 0, STATE_SELECTABLE | STATE_SELECTED); 738 } else if (isSel) { 739 testStates(cell, STATE_SELECTED); 740 } else { 741 testStates(cell, STATE_SELECTABLE, 0, STATE_SELECTED); 742 } 743 } 744 } 745 } 746 747 /** 748 * Test columnHeaderCells and rowHeaderCells of accessible table. 749 */ 750 function testHeaderCells(aHeaderInfoMap) { 751 for (var testIdx = 0; testIdx < aHeaderInfoMap.length; testIdx++) { 752 var dataCellIdentifier = aHeaderInfoMap[testIdx].cell; 753 var dataCell = getAccessible(dataCellIdentifier, [nsIAccessibleTableCell]); 754 755 // row header cells 756 var rowHeaderCells = aHeaderInfoMap[testIdx].rowHeaderCells; 757 var rowHeaderCellsCount = rowHeaderCells.length; 758 var actualRowHeaderCells = dataCell.rowHeaderCells; 759 var actualRowHeaderCellsCount = actualRowHeaderCells.length; 760 761 is( 762 actualRowHeaderCellsCount, 763 rowHeaderCellsCount, 764 "Wrong number of row header cells for the cell " + 765 prettyName(dataCellIdentifier) 766 ); 767 768 if (actualRowHeaderCellsCount == rowHeaderCellsCount) { 769 for (let idx = 0; idx < rowHeaderCellsCount; idx++) { 770 var rowHeaderCell = getAccessible(rowHeaderCells[idx]); 771 var actualRowHeaderCell = actualRowHeaderCells.queryElementAt( 772 idx, 773 nsIAccessible 774 ); 775 isObject( 776 actualRowHeaderCell, 777 rowHeaderCell, 778 "Wrong row header cell at index " + 779 idx + 780 " for the cell " + 781 dataCellIdentifier 782 ); 783 } 784 } 785 786 // column header cells 787 var colHeaderCells = aHeaderInfoMap[testIdx].columnHeaderCells; 788 var colHeaderCellsCount = colHeaderCells.length; 789 var actualColHeaderCells = dataCell.columnHeaderCells; 790 var actualColHeaderCellsCount = actualColHeaderCells.length; 791 792 is( 793 actualColHeaderCellsCount, 794 colHeaderCellsCount, 795 "Wrong number of column header cells for the cell " + 796 prettyName(dataCellIdentifier) 797 ); 798 799 if (actualColHeaderCellsCount == colHeaderCellsCount) { 800 for (let idx = 0; idx < colHeaderCellsCount; idx++) { 801 var colHeaderCell = getAccessible(colHeaderCells[idx]); 802 var actualColHeaderCell = actualColHeaderCells.queryElementAt( 803 idx, 804 nsIAccessible 805 ); 806 isObject( 807 actualColHeaderCell, 808 colHeaderCell, 809 "Wrong column header cell at index " + 810 idx + 811 " for the cell " + 812 dataCellIdentifier 813 ); 814 } 815 } 816 } 817 } 818 819 // ////////////////////////////////////////////////////////////////////////////// 820 // private implementation 821 822 /** 823 * Return row and column of orig cell for the given spanned cell. 824 */ 825 function getOrigRowAndColumn(aCellsArray, aRowIdx, aColIdx) { 826 var cellState = aCellsArray[aRowIdx][aColIdx]; 827 828 var origRowIdx = aRowIdx, 829 origColIdx = aColIdx; 830 if (cellState & kRowSpanned) { 831 for (var prevRowIdx = aRowIdx - 1; prevRowIdx >= 0; prevRowIdx--) { 832 let prevCellState = aCellsArray[prevRowIdx][aColIdx]; 833 if (!(prevCellState & kRowSpanned)) { 834 origRowIdx = prevRowIdx; 835 break; 836 } 837 } 838 } 839 840 if (cellState & kColSpanned) { 841 for (var prevColIdx = aColIdx - 1; prevColIdx >= 0; prevColIdx--) { 842 let prevCellState = aCellsArray[aRowIdx][prevColIdx]; 843 if (!(prevCellState & kColSpanned)) { 844 origColIdx = prevColIdx; 845 break; 846 } 847 } 848 } 849 850 return [origRowIdx, origColIdx]; 851 }