diff --git a/src/org/labkey/test/components/ui/grids/EditableGrid.java b/src/org/labkey/test/components/ui/grids/EditableGrid.java index aa4c8633d8..5f8dea57da 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGrid.java +++ b/src/org/labkey/test/components/ui/grids/EditableGrid.java @@ -18,6 +18,7 @@ import org.labkey.test.components.ui.grids.FieldReferenceManager.FieldReference; import org.labkey.test.params.FieldDefinition; import org.labkey.test.params.FieldKey; +import org.labkey.test.util.CachingSupplier; import org.labkey.test.util.selenium.ScrollUtils; import org.labkey.test.util.selenium.WebElementUtils; import org.openqa.selenium.By; @@ -151,55 +152,29 @@ public EditableGrid removeColumn(CharSequence columnIdentifier) private boolean hasSelectColumn() { - return elementCache().selectColumn.isDisplayed(); + return elementCache().hasSelectColumn.get(); } public EditableGrid selectRow(int index, boolean checked) { - if (hasSelectColumn()) - { - WebElement checkBox = Locator.css("td > input[type=checkbox]").findElement(getRow(index)); - getWrapper().setCheckbox(checkBox, checked); - } - else - { - throw new NoSuchElementException("There is no select checkbox for row " + index); - } + elementCache().getCheckbox(index).set(checked); return this; } public boolean isRowSelected(int index) { - if (hasSelectColumn()) - { - WebElement checkBox = Locator.css("td > input[type=checkbox]").findElement(getRow(index)); - return checkBox.isSelected(); - } - else - { - throw new NoSuchElementException("There is no select checkbox for row " + index); - } + return elementCache().getCheckbox(index).isSelected(); } public EditableGrid selectAll(boolean checked) { - if (hasSelectColumn()) - { - getWrapper().setCheckbox(elementCache().selectColumn, checked); - } - else - { - throw new NoSuchElementException("There is no select checkbox for all rows."); - } + elementCache().selectAllCheckbox.set(checked); return this; } public boolean areAllRowsSelected() { - if (hasSelectColumn()) - return new Checkbox(elementCache().selectColumn).isSelected(); - else - throw new NoSuchElementException("There is no select checkbox for all rows."); + return elementCache().selectAllCheckbox.isSelected(); } /** @@ -212,14 +187,14 @@ public boolean areAllRowsSelected() public EditableGrid shiftSelectRange(int start, int end) { if (!hasSelectColumn()) - throw new NoSuchElementException("there is no select checkbox for all rows"); + throw new NoSuchElementException("there is no selection column for grid"); var checkBoxes = Locator.tag("tr").child("td") .child(Locator.tagWithAttribute("input", "type", "checkbox")) .findElements(elementCache().table); - getWrapper().scrollIntoView(checkBoxes.get(0), true); // bring as much of the grid into view as possible + checkBoxes.get(start).click(); + getWrapper().scrollIntoView(checkBoxes.get(end), true); // Actions.click() doesn't scroll new Actions(getDriver()) - .click(checkBoxes.get(start)) .keyDown(Keys.SHIFT) .click(checkBoxes.get(end)) .keyUp(Keys.SHIFT) @@ -227,11 +202,6 @@ public EditableGrid shiftSelectRange(int start, int end) return this; } - private List getRows() - { - return Locators.rows.findElements(elementCache().table); - } - /** * @param columnIdentifiers fieldKeys, names, or labels of columns * @return grid data for the specified columns, keyed by column label @@ -291,7 +261,7 @@ private List> getGridData(Function cells = row.findElements(By.tagName("td")); Map rowMap = new LinkedHashMap<>(includedColHeaders.size()); @@ -337,7 +307,7 @@ public List getColumnData(CharSequence columnIdentifier) private WebElement getRow(int index) { - return getRows().get(index); + return elementCache().getRows().get(index); } /** @@ -387,7 +357,7 @@ public boolean isCellReadOnly(int row, CharSequence columnIdentifier) public int getRowCount() { - return getRows().size(); + return elementCache().getRows().size(); } /** @@ -657,6 +627,7 @@ public WebElement activateCellUsingDoubleClick(int row, CharSequence columnIdent // Account for the cell already being active. if(!textArea.isDisplayed()) { + getWrapper().scrollIntoView(gridCell); getWrapper().doubleClick(gridCell); waitFor(textArea::isDisplayed, String.format("Table cell for row %d and column '%s' was not activated.", row, columnIdentifier), 1_000); @@ -1086,7 +1057,7 @@ private boolean areAllInSelection() List columns = getColumnLabels(); int selectIndexOffset = hasSelectColumn() ? 1 : 0; WebElement indexCell = getCell(0, columns.get(1 + selectIndexOffset)); - WebElement endCell = getCell(getRows().size()-1, columns.get(columns.size()-1)); + WebElement endCell = getCell(elementCache().getRows().size()-1, columns.get(columns.size()-1)); return (isInSelection(indexCell) && isInSelection(endCell)); } @@ -1219,7 +1190,13 @@ protected class ElementCache extends Component.ElementCache final WebElement deleteRowsBtn = Locator.byClass("bulk-remove-button").findWhenNeeded(topControls); final ExportMenu exportMenu = ExportMenu.finder(getDriver()).findWhenNeeded(topControls); final WebElement table = Locator.byClass("table-cellular").findWhenNeeded(this); - private final WebElement selectColumn = Locator.xpath("//th/input[@type='checkbox']").findWhenNeeded(table); + private final Checkbox selectAllCheckbox = new Checkbox(Locator.xpath("//th/input[@type='checkbox']").findWhenNeeded(table)); + private final CachingSupplier hasSelectColumn = new CachingSupplier<>(selectAllCheckbox::isDisplayed); + + Checkbox getCheckbox(int rowIndex) + { + return new Checkbox(Locator.css("td > input[type=checkbox]").findElement(getRow(rowIndex))); + } protected WebElement getColumnHeaderCell(CharSequence columnIdentifier) { @@ -1292,6 +1269,11 @@ public ReactDateTimePicker datePicker() final WebElement addRowsPanel = Locator.byClass("editable-grid__controls").findWhenNeeded(this); final Input addCountInput = Input.Input(Locator.name("addCount"), getDriver()).findWhenNeeded(addRowsPanel); final WebElement addRowsButton = Locator.byClass("btn-primary").findWhenNeeded(addRowsPanel); + + List getRows() + { + return Locators.rows.findElements(table); + } } protected abstract static class Locators diff --git a/src/org/labkey/test/tests/component/EditableGridTest.java b/src/org/labkey/test/tests/component/EditableGridTest.java index ac04dc7f9e..391ef10d67 100644 --- a/src/org/labkey/test/tests/component/EditableGridTest.java +++ b/src/org/labkey/test/tests/component/EditableGridTest.java @@ -36,6 +36,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.everyItem; @@ -1102,7 +1103,7 @@ public void testShiftArrowSelectHorizontal() checker().verifyTrue(String.format("The expected cell on row %d and column %s is not selected after hitting .", gridRow, PASTE_2), - endCell.getAttribute("class").toLowerCase().contains("cell-selected")); + Objects.requireNonNullElse(endCell.getAttribute("class"), "").toLowerCase().contains("cell-selected")); checker().screenShotIfNewError("TAB_ERROR"); } @@ -1252,8 +1253,8 @@ private void checkSelectedStyle(EditableGrid editableGrid, for(int rowIndex = startRow; rowIndex <= endRow; rowIndex++) { WebElement gridCell = Locator.tag("div").findElement(editableGrid.getCell(rowIndex, columnNames.get(colIndex))); - checker().verifyTrue(String.format("Cell (%s, %d) is not selected.",columnNames.get(colIndex), rowIndex), - gridCell.getAttribute("class").toLowerCase().contains("cell-selection")); + checker().verifyTrue(String.format("Cell (%s, %d) is not selected.", columnNames.get(colIndex), rowIndex), + Objects.requireNonNullElse(gridCell.getAttribute("class"), "").toLowerCase().contains("cell-selection")); } } @@ -1398,9 +1399,8 @@ public void testPasteCellValidation() testGrid.addRows(3); log("Pasting invalid values"); - testGrid.selectCell(0, STR_FIELD); + testGrid.pasteFromCell(0, STR_FIELD, rowsToString(clipRows), false); - actionPaste(null, rowsToString(clipRows)); List> expectedCellWarnings = List.of( Arrays.asList(null, null, null, null, null, null, null, null, null, null, null, null, null, null), Arrays.asList(null, REQ_STR_FIELD.getLabel() + " is required.", null, REQ_INT_FIELD.getLabel() + " is required.", null, REQ_DATETIME_FIELD.getLabel() + " is required.", null, REQ_TIME_FIELD.getLabel() + " is required.", null, null, null, REQ_TEXTCHOICE_FIELD.getLabel() + " is required.", null, REQ_LOOKUP_FIELD.getLabel() + " is required."), @@ -1511,8 +1511,7 @@ public void testFillCellValidation() testGrid.addRows(3); log("Start with pasting invalid values, so we can fill down invalid values for dropdowns and data/time inputs"); - testGrid.selectCell(0, STR_FIELD); - actionPaste(null, rowsToString(clipRows)); + testGrid.pasteFromCell(0, STR_FIELD, rowsToString(clipRows), false); // Scroll one column to the right into view, this will help ensure the REQ_LOOKUP_FIELD is within the viewport. var index = testGrid.getColumnLabels().indexOf(REQ_LOOKUP_FIELD.getLabel() + " *") + 1;