diff --git a/src/org/labkey/test/components/ui/grids/FieldReferenceManager.java b/src/org/labkey/test/components/ui/grids/FieldReferenceManager.java index 2f5558fb11..cb2c4de07c 100644 --- a/src/org/labkey/test/components/ui/grids/FieldReferenceManager.java +++ b/src/org/labkey/test/components/ui/grids/FieldReferenceManager.java @@ -1,29 +1,33 @@ package org.labkey.test.components.ui.grids; -import org.apache.commons.lang3.mutable.Mutable; -import org.apache.commons.lang3.mutable.MutableObject; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.labkey.test.params.FieldKey; import org.labkey.test.params.WrapsFieldKey; +import org.labkey.test.util.CachingSupplier; import org.labkey.test.util.selenium.WebElementUtils; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; public class FieldReferenceManager { private final List _fieldReferences; - private final Map fieldKeys = new LinkedHashMap<>(); - private final Map fieldLabels = new LinkedHashMap<>(); + private final Map _fieldsByIndex; + private final Map _fieldKeys = new LinkedHashMap<>(); + private final Map _fieldLabels = new LinkedHashMap<>(); public FieldReferenceManager(List columnHeaders) { - this._fieldReferences = Collections.unmodifiableList(columnHeaders); + _fieldReferences = List.copyOf(columnHeaders); + _fieldsByIndex = columnHeaders.stream().collect(Collectors.toMap(FieldReference::getDomIndex, Function.identity())); } public List getColumnHeaders() @@ -31,6 +35,11 @@ public List getColumnHeaders() return _fieldReferences; } + public FieldReference getColumnHeader(int index) + { + return _fieldsByIndex.get(index); + } + /** * Find field by uncertain field identifier in order of precedence: *
    @@ -40,7 +49,7 @@ public List getColumnHeaders() *
  1. Field Label
  2. *
*/ - public final FieldReference findFieldReference(CharSequence fieldIdentifier) + public final @NotNull FieldReference findFieldReference(CharSequence fieldIdentifier) { List> options; @@ -64,20 +73,32 @@ public final FieldReference findFieldReference(CharSequence fieldIdentifier) .orElseThrow(() -> new NoSuchElementException("Unable to locate field: " + fieldIdentifier)); } + public final @Nullable FieldReference findFieldReferenceOrNull(CharSequence fieldIdentifier) + { + try + { + return findFieldReference(fieldIdentifier); + } + catch (NoSuchElementException e) + { + return null; + } + } + private FieldReference findColumnHeaderByFieldKey(FieldKey fieldIdentifier) { - if (fieldKeys.containsKey(fieldIdentifier)) + if (_fieldKeys.containsKey(fieldIdentifier)) { - return fieldKeys.get(fieldIdentifier); + return _fieldKeys.get(fieldIdentifier); } - else if (fieldKeys.size() < _fieldReferences.size()) + else if (_fieldKeys.size() < _fieldReferences.size()) { for (FieldReference header : _fieldReferences) { - if (!fieldKeys.containsValue(header)) + if (!_fieldKeys.containsValue(header)) { FieldKey fieldKey = header.getFieldKey(); - fieldKeys.put(fieldKey, header); + _fieldKeys.put(fieldKey, header); if (fieldKey.equals(fieldIdentifier)) { return header; @@ -91,18 +112,18 @@ else if (fieldKeys.size() < _fieldReferences.size()) private FieldReference findColumnHeaderByLabel(String label) { - if (fieldLabels.containsKey(label)) + if (_fieldLabels.containsKey(label)) { - return fieldLabels.get(label); + return _fieldLabels.get(label); } - else if (fieldLabels.size() < _fieldReferences.size()) + else if (_fieldLabels.size() < _fieldReferences.size()) { for (FieldReference header : _fieldReferences) { - if (!fieldLabels.containsValue(header)) + if (!_fieldLabels.containsValue(header)) { String columnLabel = header.getLabel(); - fieldLabels.put(columnLabel, header); + _fieldLabels.put(columnLabel, header); if (columnLabel.equals(label)) { return header; @@ -118,8 +139,8 @@ public static class FieldReference { private final WebElement _element; private final int _domIndex; - private final Mutable _fieldLabel = new MutableObject<>(); - private final Mutable _fieldKey = new MutableObject<>(); + private final CachingSupplier _fieldLabel = new CachingSupplier<>(this::labelSupplier); + private final CachingSupplier _fieldKey = new CachingSupplier<>(this::fieldKeySupplier); public FieldReference(WebElement element, int domIndex) { @@ -134,34 +155,12 @@ public WebElement getElement() public FieldKey getFieldKey() { - if (_fieldKey.getValue() == null) - { - String path = _element.getDomAttribute("data-fieldkey"); - if (path == null) - { - // Some grids don't have a field key, but have a similar value in the ID attribute - path = _element.getDomAttribute("id"); - } - - if (path != null) - { - _fieldKey.setValue(FieldKey.fromFieldKey(path)); - } - else - { - _fieldKey.setValue(FieldKey.EMPTY); - } - } - return _fieldKey.getValue(); + return _fieldKey.get(); } public String getLabel() { - if (_fieldLabel.getValue() == null) - { - _fieldLabel.setValue(WebElementUtils.getTextContent(getElement()).trim()); - } - return _fieldLabel.getValue(); + return _fieldLabel.get(); } public String getName() @@ -173,5 +172,29 @@ public int getDomIndex() { return _domIndex; } + + protected String labelSupplier() + { + return WebElementUtils.getTextContent(getElement()).trim(); + } + + protected FieldKey fieldKeySupplier() + { + String path = getElement().getDomAttribute("data-fieldkey"); + if (path == null) + { + // Some grids don't have a field key, but have a similar value in the ID attribute + path = getElement().getDomAttribute("id"); + } + + if (path != null) + { + return FieldKey.fromFieldKey(path); + } + else + { + return FieldKey.EMPTY; + } + } } } diff --git a/src/org/labkey/test/pages/query/UpdateQueryRowPage.java b/src/org/labkey/test/pages/query/UpdateQueryRowPage.java index 79160c9fb0..11c5512490 100644 --- a/src/org/labkey/test/pages/query/UpdateQueryRowPage.java +++ b/src/org/labkey/test/pages/query/UpdateQueryRowPage.java @@ -59,21 +59,21 @@ public void setFields(Map fields) for (Map.Entry entry : fields.entrySet()) { Object value = entry.getValue(); - if (value instanceof String) + if (value instanceof String s) { - setField(entry.getKey(), (String) value); + setField(entry.getKey(), s); } - else if (value instanceof Boolean) + else if (value instanceof Boolean b) { - setField(entry.getKey(), (Boolean) value); + setField(entry.getKey(), b); } - else if (value instanceof Integer) + else if (value instanceof Integer i) { - setField(entry.getKey(), (Integer) value); + setField(entry.getKey(), i); } - else if (value instanceof File) + else if (value instanceof File f) { - setField(entry.getKey(), (File) value); + setField(entry.getKey(), f); } else { diff --git a/src/org/labkey/test/pages/study/OverviewPage.java b/src/org/labkey/test/pages/study/OverviewPage.java index 448a0f7a72..6591a8a882 100644 --- a/src/org/labkey/test/pages/study/OverviewPage.java +++ b/src/org/labkey/test/pages/study/OverviewPage.java @@ -16,12 +16,11 @@ package org.labkey.test.pages.study; import org.jetbrains.annotations.Nullable; -import org.labkey.test.CachingLocator; import org.labkey.test.Locator; import org.labkey.test.WebDriverWrapper; import org.labkey.test.WebTestHelper; import org.labkey.test.pages.LabKeyPage; -import org.labkey.test.selenium.LazyWebElement; +import org.labkey.test.util.CachingSupplier; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; @@ -133,7 +132,7 @@ public Map> getDatasetRowData() public Map> getDatasetRowData(@Nullable Integer leftVisitIndex, @Nullable Integer endVisitIndex) { Map> datasetRowData = new HashMap<>(); - List overviewRows = elementCache().getStudyOverviewRows(); + List overviewRows = elementCache().studyOverviewRows.get(); overviewRows = overviewRows.subList(1, overviewRows.size()); for (WebElement row : overviewRows) @@ -178,7 +177,7 @@ else if (!countStr.isEmpty()) public List getVisits() { - WebElement headerRow = elementCache().getStudyOverviewRows().get(0); + WebElement headerRow = elementCache().studyOverviewRows.get().get(0); List visitCells = Locator.css("td").findElements(headerRow); visitCells = visitCells.subList(1, visitCells.size() - 1); @@ -193,16 +192,13 @@ protected Elements newElementCache() protected class Elements extends LabKeyPage.ElementCache { - public final WebElement participantCountCheckbox = new LazyWebElement(Locator.checkboxByNameAndValue("visitStatistic", "ParticipantCount"), this); - public final WebElement rowCountCheckbox = new LazyWebElement(Locator.checkboxByNameAndValue("visitStatistic", "RowCount"), this); - public final WebElement studyOverview = new LazyWebElement(Locator.tagWithId("table", "studyOverview"), this); + public final WebElement participantCountCheckbox = Locator.checkboxByNameAndValue("visitStatistic", "ParticipantCount").findWhenNeeded(this); + public final WebElement rowCountCheckbox = Locator.checkboxByNameAndValue("visitStatistic", "RowCount").findWhenNeeded(this); + public final WebElement studyOverview = Locator.tagWithId("table", "studyOverview").findWhenNeeded(this); - private final Locator studyOverviewRow = new CachingLocator(Locator.CssLocator.union(Locator.css(".labkey-row"), Locator.css(".labkey-alternate-row"))); + private final Locator studyOverviewRow = Locator.CssLocator.union(Locator.css(".labkey-row"), Locator.css(".labkey-alternate-row")); - public List getStudyOverviewRows() - { - return studyOverviewRow.findElements(studyOverview); - } + public CachingSupplier> studyOverviewRows = new CachingSupplier<>(() -> studyOverviewRow.findElements(studyOverview)); } public static class CountPair diff --git a/src/org/labkey/test/params/FieldKey.java b/src/org/labkey/test/params/FieldKey.java index 56de9f3b4c..1469033737 100644 --- a/src/org/labkey/test/params/FieldKey.java +++ b/src/org/labkey/test/params/FieldKey.java @@ -2,7 +2,6 @@ import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; @@ -66,7 +65,7 @@ public static FieldKey fromParts(String... parts) * @param fieldKey String or FieldKey * @return FieldKey representation of the String, or the identity if a FieldKey was provided */ - public static @Nullable FieldKey fromFieldKey(CharSequence fieldKey) + public static FieldKey fromFieldKey(CharSequence fieldKey) { if (fieldKey instanceof WrapsFieldKey fk) { @@ -74,14 +73,7 @@ public static FieldKey fromParts(String... parts) } else { - try - { - return fromParts(Arrays.stream(fieldKey.toString().split(SEPARATOR)).map(FieldKey::decodePart).toList()); - } - catch (IllegalArgumentException iae) - { - return null; - } + return fromParts(Arrays.stream(fieldKey.toString().split(SEPARATOR)).map(FieldKey::decodePart).toList()); } } @@ -149,6 +141,10 @@ public String getName() return _name; } + /** + * Inverse of {@link #fromParts(String...)} + * @return decoded parts of the field key + */ public String[] getNameArray() { return Arrays.stream(_fieldKey.split(SEPARATOR)).map(FieldKey::decodePart).toArray(String[]::new); diff --git a/src/org/labkey/test/tests/AbstractExportTest.java b/src/org/labkey/test/tests/AbstractExportTest.java index 0c2a1ccde6..76c1a854ff 100644 --- a/src/org/labkey/test/tests/AbstractExportTest.java +++ b/src/org/labkey/test/tests/AbstractExportTest.java @@ -35,8 +35,8 @@ import java.util.HashSet; import java.util.List; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -103,7 +103,7 @@ public void preTest() exportHelper = new DataRegionExportHelper(dataRegion); if (hasSelectors()) - dataRegion.uncheckAll(); + dataRegion.uncheckAllOnPage(); } @Test diff --git a/src/org/labkey/test/tests/ButtonCustomizationTest.java b/src/org/labkey/test/tests/ButtonCustomizationTest.java index cffb23a76a..49eca0c4c5 100644 --- a/src/org/labkey/test/tests/ButtonCustomizationTest.java +++ b/src/org/labkey/test/tests/ButtonCustomizationTest.java @@ -25,11 +25,13 @@ import org.labkey.test.params.FieldDefinition; import org.labkey.test.params.FieldDefinition.ColumnType; import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.DataRegionTable.DataRegionFinder; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.WikiHelper; import java.util.Arrays; import java.util.List; +import java.util.Map; import static org.junit.Assert.assertTrue; @@ -101,12 +103,10 @@ public void testSteps() goToManageLists(); clickAndWait(Locator.linkWithText(LIST_NAME)); assertButtonNotPresent(METADATA_OVERRIDE_BUTTON); - DataRegionTable.findDataRegion(this).clickInsertNewRow(); - setFormElement(Locator.name("quf_name"), "Seattle"); - clickButton("Submit"); - DataRegionTable.findDataRegion(this).clickInsertNewRow(); - setFormElement(Locator.name("quf_name"), "Portland"); - clickButton("Submit"); + new DataRegionFinder(getDriver()).find().clickInsertNewRow() + .update(Map.of("name", "Seattle")); + new DataRegionFinder(getDriver()).find().clickInsertNewRow() + .update(Map.of("name", "Portland")); // assert custom buttons can be added to the standard set: beginAt("/query/" + PROJECT_NAME + "/schema.view?schemaName=lists"); @@ -188,7 +188,7 @@ public void testSteps() Locator.lkButton(METADATA_LINK_BUTTON).waitForElement(getDriver(), WAIT_FOR_JAVASCRIPT) .getAttribute("class").contains("labkey-disabled-button")); - buttonRegion.checkCheckbox(buttonRegion.getIndexWhereDataAppears("Portland", "Name")); + buttonRegion.checkCheckbox(buttonRegion.getRowIndex("Portland", "Name")); // wait for the button to enable: waitForElement(Locator.lkButton(METADATA_LINK_BUTTON), 10000); diff --git a/src/org/labkey/test/tests/DataRegionTest.java b/src/org/labkey/test/tests/DataRegionTest.java index 5b4556597a..add7f0d02e 100644 --- a/src/org/labkey/test/tests/DataRegionTest.java +++ b/src/org/labkey/test/tests/DataRegionTest.java @@ -277,7 +277,7 @@ private void dataRegionTest(URL url, String dataRegionName) throws MalformedURLE clickAndWait(selectionPart.append(Locator.tagWithClass("span", "show-selected"))); assertEquals(5, table.getDataRowCount()); - table.showAll(); + table.rowSelector().showAll(); assertEquals(15, table.getDataRowCount()); } diff --git a/src/org/labkey/test/tests/DatasetExportTest.java b/src/org/labkey/test/tests/DatasetExportTest.java index b5739b9dc3..58fee97724 100644 --- a/src/org/labkey/test/tests/DatasetExportTest.java +++ b/src/org/labkey/test/tests/DatasetExportTest.java @@ -108,7 +108,7 @@ public void setupDataset() throws Exception clickAndWait(Locator.linkWithText(ASSAY_RUN_FILE.getName())); DataRegionTable assayResults = new DataRegionTable(super.getDataRegionId(), this); - assayResults.checkAll(); + assayResults.checkAllOnPage(); clickButton("Link to Study"); selectOptionByText(Locator.name("targetStudy"), "/" + getProjectName() + "/" + getFolderName() + " (" + getFolderName() + " Study)"); clickButton("Next"); diff --git a/src/org/labkey/test/tests/DatasetPublishTest.java b/src/org/labkey/test/tests/DatasetPublishTest.java index d0e7869e48..1c7263412a 100644 --- a/src/org/labkey/test/tests/DatasetPublishTest.java +++ b/src/org/labkey/test/tests/DatasetPublishTest.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; @Category({Daily.class}) @BaseWebDriverTest.ClassTimeout(minutes = 6) @@ -90,12 +91,12 @@ private void addRowToSourceDataset() { goToProjectHome(); goToDataset("Demographics"); - DataRegionTable.findDataRegion(this).clickInsertNewRow(); - waitForElement(Locator.name("quf_ParticipantId")); - setFormElement(Locator.name("quf_ParticipantId"), "addedParticipant67676"); - setFormElement(Locator.name("quf_date"), "1/1/2001"); - setFormElement(Locator.name("quf_Comments"), "Comment on added participant"); - clickButton("Submit"); + new DataRegionTable.DataRegionFinder(getDriver()).find() + .clickInsertNewRow() + .update(Map.of( + "ParticipantId", "addedParticipant67676", + "date", "1/1/2001", + "Comments", "Comment on added participant")); } @LogMethod diff --git a/src/org/labkey/test/tests/FlagColumnTest.java b/src/org/labkey/test/tests/FlagColumnTest.java index 4a976ba04e..70a0e274e8 100644 --- a/src/org/labkey/test/tests/FlagColumnTest.java +++ b/src/org/labkey/test/tests/FlagColumnTest.java @@ -20,8 +20,8 @@ import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @Category({Assays.class, Daily.class}) @@ -124,7 +124,7 @@ private void doInit() // verify expected rows and columns are present DataRegionTable grid = new DataRegionTable("Data", getDriver()); - assertThat(grid.getColumnNames(), hasItems(RESULT_SOME_DATA, RESULT_FLAG_A, RESULT_FLAG_B, "Run/AnotherRunFlag", RESULT_RUN_FLAG)); + assertThat(grid.getFieldKeyStrings(), hasItems(RESULT_SOME_DATA, RESULT_FLAG_A, RESULT_FLAG_B, "Run/AnotherRunFlag", RESULT_RUN_FLAG)); assertEquals("run1-data1", grid.getDataAsText(0, RESULT_SOME_DATA)); assertEquals("run1-data2", grid.getDataAsText(1, RESULT_SOME_DATA)); assertEquals("run2-data1", grid.getDataAsText(2, RESULT_SOME_DATA)); diff --git a/src/org/labkey/test/tests/LinkedSchemaTest.java b/src/org/labkey/test/tests/LinkedSchemaTest.java index 428ff5156c..88b4b2e43b 100644 --- a/src/org/labkey/test/tests/LinkedSchemaTest.java +++ b/src/org/labkey/test/tests/LinkedSchemaTest.java @@ -581,7 +581,7 @@ private void checkLinkedSchema(String updatedMetaData, String studyName, int exp if((expectedUsersCount > 0) && (null != studyName)) { - if (!table.getColumnNames().contains("ParticipantId/Study")) + if (!table.getFieldKeyStrings().contains("ParticipantId/Study")) { CustomizeView cv = table.openCustomizeGrid(); cv.addColumn("ParticipantId/Study"); diff --git a/src/org/labkey/test/tests/MessagesLongTest.java b/src/org/labkey/test/tests/MessagesLongTest.java index 8579d2c3b1..8d8cbc5785 100644 --- a/src/org/labkey/test/tests/MessagesLongTest.java +++ b/src/org/labkey/test/tests/MessagesLongTest.java @@ -775,7 +775,7 @@ private void basicMessageTests() log("test filtering of messages grid"); clickAndWait(Locator.linkWithText("view list")); DataRegionTable region = new DataRegionTable("Announcements", getDriver()); - region.setFilter("Title", "Equals", "foo", WAIT_FOR_PAGE); + region.setFilter("Title", "Equals", "foo"); assertTextNotPresent(RESP1_TITLE); } diff --git a/src/org/labkey/test/tests/SampleTypeLineageTest.java b/src/org/labkey/test/tests/SampleTypeLineageTest.java index de8f2c97db..11ccc0725c 100644 --- a/src/org/labkey/test/tests/SampleTypeLineageTest.java +++ b/src/org/labkey/test/tests/SampleTypeLineageTest.java @@ -383,7 +383,7 @@ public void testUpdateLineageUsingFileImport() log("Check that the imported data is as expected."); DataRegionTable dataRegionTable = new DataRegionTable("Material", this); - int row = dataRegionTable.getIndexWhereDataAppears(testSample, "Name"); + int row = dataRegionTable.getRowIndex(testSample, "Name"); String data = dataRegionTable.getDataAsText(row, columnName); checker().verifyEquals("Something doesn't look right. Value for column not as expected.", testData, data); @@ -406,7 +406,7 @@ public void testUpdateLineageUsingFileImport() log("Check that the updated data is shown."); dataRegionTable = new DataRegionTable("Material", this); - row = dataRegionTable.getIndexWhereDataAppears(testSample, "Name"); + row = dataRegionTable.getRowIndex(testSample, "Name"); data = dataRegionTable.getDataAsText(row, columnName); checker().verifyEquals("Value for column not updated as expected.", updatedTestData, data); @@ -652,7 +652,7 @@ public void testDeriveSampleByUI() throws CommandException, IOException for(String parent : parents) { - int index = drt.getIndexWhereDataAppears(parent, "Name"); + int index = drt.getRowIndex(parent, "Name"); drt.checkCheckbox(index); } @@ -1129,7 +1129,7 @@ public void testDeleteSamplesSomeWithDerivedSamples() sampleHelper.createSampleType(new SampleTypeDefinition(SAMPLE_TYPE_NAME), sampleData); DataRegionTable drtSamples = sampleHelper.getSamplesDataRegionTable(); log("Derive one sample from another"); - drtSamples.checkCheckbox(drtSamples.getIndexWhereDataAppears(parentSampleNames.get(0), "Name")); + drtSamples.checkCheckbox(drtSamples.getRowIndex(parentSampleNames.get(0), "Name")); clickButton("Derive Samples"); waitAndClickAndWait(Locator.lkButton("Next")); String childName = parentSampleNames.get(0) + ".1"; @@ -1145,8 +1145,8 @@ public void testDeleteSamplesSomeWithDerivedSamples() log("Derive a sample with two parents"); clickAndWait(Locator.linkContainingText(SAMPLE_TYPE_NAME)); - drtSamples.checkCheckbox(drtSamples.getIndexWhereDataAppears(parentSampleNames.get(1), "Name")); - drtSamples.checkCheckbox(drtSamples.getIndexWhereDataAppears(childName, "Name")); + drtSamples.checkCheckbox(drtSamples.getRowIndex(parentSampleNames.get(1), "Name")); + drtSamples.checkCheckbox(drtSamples.getRowIndex(childName, "Name")); clickButton("Derive Samples"); waitAndClickAndWait(Locator.lkButton("Next")); String twoParentChildName = parentSampleNames.get(1) + "+" + childName + ".1"; @@ -1156,14 +1156,14 @@ public void testDeleteSamplesSomeWithDerivedSamples() clickAndWait(Locator.linkContainingText(SAMPLE_TYPE_NAME)); log("Try to delete parent sample"); - drtSamples.checkCheckbox(drtSamples.getIndexWhereDataAppears(parentSampleNames.get(0), "Name")); + drtSamples.checkCheckbox(drtSamples.getRowIndex(parentSampleNames.get(0), "Name")); drtSamples.clickHeaderButton("Delete"); Window.Window(getDriver()).withTitle("No samples can be deleted").waitFor() .clickButton("Dismiss", true); log("Try to delete multiple parent samples"); - drtSamples.checkCheckbox(drtSamples.getIndexWhereDataAppears(parentSampleNames.get(1), "Name")); - drtSamples.checkCheckbox(drtSamples.getIndexWhereDataAppears(childName, "Name")); + drtSamples.checkCheckbox(drtSamples.getRowIndex(parentSampleNames.get(1), "Name")); + drtSamples.checkCheckbox(drtSamples.getRowIndex(childName, "Name")); drtSamples.clickHeaderButton("Delete"); Window.Window(getDriver()).withTitle("No samples can be deleted").waitFor() .clickButton("Dismiss", true); @@ -1172,21 +1172,21 @@ public void testDeleteSamplesSomeWithDerivedSamples() assertEquals("No selection should remain", 0, drtSamples.getSelectedCount()); log("Try to delete parent and child"); - drtSamples.checkCheckbox(drtSamples.getIndexWhereDataAppears(parentSampleNames.get(1), "Name")); - drtSamples.checkCheckbox(drtSamples.getIndexWhereDataAppears(twoParentChildName, "Name")); + drtSamples.checkCheckbox(drtSamples.getRowIndex(parentSampleNames.get(1), "Name")); + drtSamples.checkCheckbox(drtSamples.getRowIndex(twoParentChildName, "Name")); assertEquals("Parent and child should be checked", 2, drtSamples.getCheckedCount()); assertEquals("Parent and child should be checked", 2, drtSamples.getSelectedCount()); sampleHelper.deleteSamples(drtSamples, "Permanently delete 1 sample"); - assertEquals("Deleted sample " + twoParentChildName + " still appears in grid", -1, drtSamples.getIndexWhereDataAppears(twoParentChildName, "Name")); - assertTrue("Parent sample " + parentSampleNames.get(1) + " does not appears in grid", drtSamples.getIndexWhereDataAppears(parentSampleNames.get(1), "Name") > -1); + assertEquals("Deleted sample " + twoParentChildName + " still appears in grid", -1, drtSamples.getRowIndex(twoParentChildName, "Name")); + assertTrue("Parent sample " + parentSampleNames.get(1) + " does not appears in grid", drtSamples.getRowIndex(parentSampleNames.get(1), "Name") > -1); assertEquals("Only parent sample should be checked", 1, drtSamples.getCheckedCount()); assertEquals("Only parent sample should be checked", 1, drtSamples.getSelectedCount()); log("Now that the child is gone, try to delete the parent"); sampleHelper.deleteSamples(drtSamples, "Permanently delete 1 sample"); - assertEquals("Deleted sample " + parentSampleNames.get(1) + " still appears in grid", -1, drtSamples.getIndexWhereDataAppears(parentSampleNames.get(1), "Name")); + assertEquals("Deleted sample " + parentSampleNames.get(1) + " still appears in grid", -1, drtSamples.getRowIndex(parentSampleNames.get(1), "Name")); assertEquals("No selection should remain", 0, drtSamples.getCheckedCount()); log("Now try to delete what's left, in several hitches"); diff --git a/src/org/labkey/test/tests/TimeChartAPITest.java b/src/org/labkey/test/tests/TimeChartAPITest.java index 74b786f209..08a3bf01d0 100644 --- a/src/org/labkey/test/tests/TimeChartAPITest.java +++ b/src/org/labkey/test/tests/TimeChartAPITest.java @@ -25,6 +25,7 @@ import org.labkey.test.categories.Daily; import org.labkey.test.categories.Reports; import org.labkey.test.tests.visualization.TimeChartTest; +import org.labkey.test.util.CachingSupplier; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LogMethod; import org.labkey.test.util.PortalHelper; @@ -311,11 +312,11 @@ private void testVisApi(File htmlPage, String[] testTitles, @Nullable int[] test waitForElement(Locator.paginationText(testRowCounts[testIndex]), WAIT_FOR_JAVASCRIPT); } - DataRegionTable table = null; + DataRegionTable table = new DataRegionTable.DataRegionFinder(getDriver()) + .withName("apiTestDataRegion").findWhenNeeded(); if (testColumnNames != null) { - if (null == table) table = new DataRegionTable("apiTestDataRegion", this); List expectedColumnNames = Arrays.asList(testColumnNames[testIndex]); List columnNames = new ArrayList<>(table.getColumnNames()); @@ -328,33 +329,28 @@ private void testVisApi(File htmlPage, String[] testTitles, @Nullable int[] test } } - if (colsForAllTests.length > 0) + for (List>> expectedColForAllTests : colsForAllTests) { - if (null == table) table = new DataRegionTable("apiTestDataRegion", this); + Pair> expectedColumn = expectedColForAllTests.get(testIndex); + String columnName = expectedColumn.getKey(); + int columnIndex = table.getColumnIndex(columnName); + List expectedValues = expectedColumn.getValue(); + List actualValues = new ArrayList<>(); + boolean isNumberCol = expectedValues.get(0) instanceof Number; + + for (int i = 0; i < table.getDataRowCount() && actualValues.size() < expectedValues.size() && columnIndex >= 0; i++) + { + String value = table.getDataAsText(i, columnIndex).trim(); + if (!value.isEmpty()) + actualValues.add(isNumberCol ? Double.parseDouble(value) : value); + } - for (List>> expectedColForAllTests : colsForAllTests) + if (!expectedValues.equals(actualValues)) { - Pair> expectedColumn = expectedColForAllTests.get(testIndex); - String columnName = expectedColumn.getKey(); - int columnIndex = table.getColumnIndex(columnName); - List expectedValues = expectedColumn.getValue(); - List actualValues = new ArrayList<>(); - boolean isNumberCol = expectedValues.get(0) instanceof Number; - - for (int i = 0; i < table.getDataRowCount() && actualValues.size() < expectedValues.size() && columnIndex >= 0; i++) - { - String value = table.getDataAsText(i, columnIndex).trim(); - if (!value.isEmpty()) - actualValues.add(isNumberCol ? Double.parseDouble(value) : value); - } - - if (!expectedValues.equals(actualValues)) - { - TestLogger.log("expected values for " + columnName + " -- " + String.join(", ", expectedValues.toString())); - TestLogger.log("actual values for " + columnName + " -- " + String.join(", ", actualValues.toString())); - } - assertEquals("Wrong values for column " + columnName, expectedValues, actualValues); + TestLogger.log("expected values for " + columnName + " -- " + String.join(", ", expectedValues.toString())); + TestLogger.log("actual values for " + columnName + " -- " + String.join(", ", actualValues.toString())); } + assertEquals("Wrong values for column " + columnName, expectedValues, actualValues); } if (testOutputTexts != null) diff --git a/src/org/labkey/test/tests/UniprotAnnotationTest.java b/src/org/labkey/test/tests/UniprotAnnotationTest.java index 2d63dfd3a2..cb7c99a489 100644 --- a/src/org/labkey/test/tests/UniprotAnnotationTest.java +++ b/src/org/labkey/test/tests/UniprotAnnotationTest.java @@ -68,10 +68,10 @@ public void testSteps() if (alreadyLoaded) { annotInsertions.setFilter("FileName", "Contains", UNIPROT_FILENAME); - annotInsertions.checkAll(); + annotInsertions.checkAllOnPage(); doAndWaitForPageToLoad(() -> { - annotInsertions.clickHeaderButtonByText("Delete"); + annotInsertions.clickHeaderButton("Delete"); acceptAlert(); }); } diff --git a/src/org/labkey/test/tests/UserDetailsPermissionTest.java b/src/org/labkey/test/tests/UserDetailsPermissionTest.java index dbcc072494..01d33a4121 100644 --- a/src/org/labkey/test/tests/UserDetailsPermissionTest.java +++ b/src/org/labkey/test/tests/UserDetailsPermissionTest.java @@ -31,7 +31,7 @@ import org.labkey.test.pages.query.ExecuteQueryPage; import org.labkey.test.params.FieldDefinition; import org.labkey.test.util.ApiPermissionsHelper; -import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.DataRegionTable.DataRegionFinder; import org.labkey.test.util.LogMethod; import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.PortalHelper; @@ -145,7 +145,7 @@ public void testUserVisibilityViaLookup() log("Verify that emails cannot be seen in list via lookup"); clickAndWait(Locator.linkWithText(EMAIL_TEST_LIST)); - DataRegionTable.findDataRegion(this).goToView(HIDDEN_COL_VIEW); + new DataRegionFinder(getDriver()).find().goToView(HIDDEN_COL_VIEW); assertTextPresent(displayName); // This user does not have permission to see user details, so no link assertElementNotPresent(Locator.linkWithText(displayName)); @@ -157,7 +157,7 @@ public void testUserVisibilityViaLookup() log("Verify that user table info can be seen with permission"); clickAndWait(Locator.linkWithText(EMAIL_TEST_LIST)); - DataRegionTable.findDataRegion(this).goToView(HIDDEN_COL_VIEW); + new DataRegionFinder(getDriver()).find().goToView(HIDDEN_COL_VIEW); assertTextPresent(CHECKED_USER, ADMIN_USER, HIDDEN_STRING); } @@ -171,7 +171,7 @@ public void testUserVisibilityViaQuery() ExecuteQueryPage.beginAt(this, "core", "Users"); log("Verify that emails cannot be seen in query webpart"); - DataRegionTable.findDataRegion(this).goToView(HIDDEN_COL_VIEW); + new DataRegionFinder(getDriver()).find().goToView(HIDDEN_COL_VIEW); assertElementPresent(Locator.linkWithText(displayName)); assertTextNotPresent(CHECKED_USER, ADMIN_USER, HIDDEN_STRING); @@ -180,7 +180,7 @@ public void testUserVisibilityViaQuery() ExecuteQueryPage.beginAt(this, "core", "Users"); log("Verify that user table info can be seen with permission"); - DataRegionTable.findDataRegion(this).goToView(HIDDEN_COL_VIEW); + new DataRegionFinder(getDriver()).find().goToView(HIDDEN_COL_VIEW); assertTextPresent(CHECKED_USER, ADMIN_USER, HIDDEN_STRING); } @@ -249,10 +249,10 @@ private void createHiddenEmailList() _listHelper.createList(getProjectName(), EMAIL_TEST_LIST, "Key", userColumn); goToManageLists(); clickAndWait(Locator.linkWithText(EMAIL_TEST_LIST)); - DataRegionTable.findDataRegion(this).clickInsertNewRow(); + new DataRegionFinder(getDriver()).find().clickInsertNewRow(); selectOptionByText(Locator.name("quf_user"), _userHelper.getDisplayNameForEmail(CHECKED_USER)); clickButton("Submit"); - DataRegionTable.findDataRegion(this).clickInsertNewRow(); + new DataRegionFinder(getDriver()).find().clickInsertNewRow(); selectOptionByText(Locator.name("quf_user"), _userHelper.getDisplayNameForEmail(ADMIN_USER)); clickButton("Submit"); _customizeViewsHelper.openCustomizeViewPanel(); diff --git a/src/org/labkey/test/tests/issues/IssuesTest.java b/src/org/labkey/test/tests/issues/IssuesTest.java index 6c749c14af..d12e2c3666 100644 --- a/src/org/labkey/test/tests/issues/IssuesTest.java +++ b/src/org/labkey/test/tests/issues/IssuesTest.java @@ -199,7 +199,7 @@ public void returnToProject() DataRegionTable issuesTable = new DataRegionTable(ISSUE_LIST_REGION_NAME, getDriver()); // clear region selection and filters - issuesTable.uncheckAll(); + issuesTable.uncheckAllOnPage(); issuesTable.clearAllFilters(); // reset folder filter @@ -599,7 +599,7 @@ public void viewSelectedDetailsTest() DataRegionTable issuesTable = new DataRegionTable(ISSUE_LIST_REGION_NAME, getDriver()); issuesTable.setFilter("Status", "Has Any Value", null); - issuesTable.checkAll(); + issuesTable.checkAllOnPage(); clickButton("View Details"); assertTextPresent( ISSUE_0.get("comment"), diff --git a/src/org/labkey/test/tests/list/ListTest.java b/src/org/labkey/test/tests/list/ListTest.java index 77fad8cb47..085c45b799 100644 --- a/src/org/labkey/test/tests/list/ListTest.java +++ b/src/org/labkey/test/tests/list/ListTest.java @@ -753,7 +753,7 @@ public void testCustomViews() assertTextPresent(TEST_DATA[TD_DESC][1], 2); log("Test deleting rows"); - dataRegionTable.checkAll(); + dataRegionTable.checkAllOnPage(); doAndWaitForPageToLoad(() -> { dt.clickHeaderButton("Delete"); diff --git a/src/org/labkey/test/tests/query/QueryLookupTest.java b/src/org/labkey/test/tests/query/QueryLookupTest.java index 980acf70c8..2bb9f73069 100644 --- a/src/org/labkey/test/tests/query/QueryLookupTest.java +++ b/src/org/labkey/test/tests/query/QueryLookupTest.java @@ -11,7 +11,6 @@ import org.labkey.test.params.FieldDefinition; import org.labkey.test.params.FieldInfo; import org.labkey.test.params.list.IntListDefinition; - import org.labkey.test.params.list.VarListDefinition; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.EscapeUtil; diff --git a/src/org/labkey/test/tests/remoteapi/BulkUpdateGroupApiTest.java b/src/org/labkey/test/tests/remoteapi/BulkUpdateGroupApiTest.java index 2a23a82af9..bdef4aabf0 100644 --- a/src/org/labkey/test/tests/remoteapi/BulkUpdateGroupApiTest.java +++ b/src/org/labkey/test/tests/remoteapi/BulkUpdateGroupApiTest.java @@ -80,7 +80,7 @@ private void deleteTestUsers(String suffix) if (usersTable.getDataRowCount() > 0) { - usersTable.checkAll(); + usersTable.checkAllOnPage(); clickButton("Delete"); clickButton("Permanently Delete"); } diff --git a/src/org/labkey/test/util/CachingSupplier.java b/src/org/labkey/test/util/CachingSupplier.java new file mode 100644 index 0000000000..a5d0464453 --- /dev/null +++ b/src/org/labkey/test/util/CachingSupplier.java @@ -0,0 +1,48 @@ +package org.labkey.test.util; + +import java.util.function.Supplier; + +/** + * Wraps another Supplier, invoking it lazily and caching its results for subsequent calls to get().
+ * Intended to be used as an alternative to null-checking and populating a member variable.
+ * Similar to {@link org.labkey.test.selenium.LazyWebElement} but for other types. + *
{@code
+ * final CachingSupplier item = new CachingSupplier<>(this::computeItem);
+ * T getItem() {
+ *     return item.get();
+ * }
+ * }
+ *
{@code
+ * // Old pattern
+ * T item;
+ * T getItem() {
+ *     if (item == null)
+ *     {
+ *         item = computeItem();
+ *     }
+ *     return item;
+ * }
+ * }
+ */ +public class CachingSupplier implements Supplier +{ + private final Supplier _factory; + private boolean _invoked = false; + private T _value; + + public CachingSupplier(Supplier factory) + { + _factory = factory; + } + + @Override + public T get() + { + if (!_invoked) + { + _value = _factory.get(); + _invoked = true; + } + return _value; + } +} diff --git a/src/org/labkey/test/util/DataRegionTable.java b/src/org/labkey/test/util/DataRegionTable.java index b82e57a43e..d0f00e599f 100644 --- a/src/org/labkey/test/util/DataRegionTable.java +++ b/src/org/labkey/test/util/DataRegionTable.java @@ -15,6 +15,7 @@ */ package org.labkey.test.util; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.remoteapi.collections.CaseInsensitiveHashMap; @@ -34,9 +35,12 @@ import org.labkey.test.components.labkey.LabKeyAlert; import org.labkey.test.components.study.DatasetFacetPanel; import org.labkey.test.components.study.ViewPreferencesPage; +import org.labkey.test.components.ui.grids.FieldReferenceManager; +import org.labkey.test.components.ui.grids.FieldReferenceManager.FieldReference; import org.labkey.test.pages.ImportDataPage; import org.labkey.test.pages.TimeChartWizard; import org.labkey.test.pages.query.UpdateQueryRowPage; +import org.labkey.test.params.FieldKey; import org.labkey.test.selenium.LazyWebElement; import org.labkey.test.selenium.RefindingWebElement; import org.labkey.test.selenium.WebElementDecorator; @@ -49,9 +53,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; @@ -74,11 +78,8 @@ public class DataRegionTable extends DataRegion private CustomizeView _customizeView; private DataRegionExportHelper _exportHelper; private PagingWidget _pagingWidget; - private final List _columnLabels = new ArrayList<>(); - private final List _columnNames = new ArrayList<>(); - private final Map _columnIndexMap = new CaseInsensitiveHashMap<>(); private final Map _mapRows = new HashMap<>(); - private final Map> _dataCache = new TreeMap<>(); + private final Map> _dataCache = new LinkedHashMap<>(); /** * @param regionName 'data-region-name' of the table @@ -118,9 +119,6 @@ protected void clearCache() _pagingWidget = null; _customizeView = null; _exportHelper = null; - _columnLabels.clear(); - _columnNames.clear(); - _columnIndexMap.clear(); _mapRows.clear(); _dataCache.clear(); } @@ -241,7 +239,7 @@ public static DataRegionTable findDataRegionWithinWebpart(WebDriverWrapper test, public int getColumnCount() { - return elementCache().getColumnHeaders().size() - (hasSelectors() ? 1 : 0); + return elementCache().getColumnHeaders().size(); } public boolean hasSummaryStatisticRow() @@ -274,7 +272,7 @@ public void assertPaginationText(int firstRow, int lastRow, int totalRows) } /** - * @deprecated Renamed. Use {@link #getRowIndex(String, String)} + * @deprecated Renamed. Use {@link #getRowIndex(CharSequence, String)} */ @Deprecated public int getRow(String columnLabel, String value) @@ -282,9 +280,9 @@ public int getRow(String columnLabel, String value) return getRowIndex(columnLabel, value); } - public int getRowIndex(String columnLabel, String value) + public int getRowIndex(CharSequence columnIdentifier, String value) { - return getRowIndex(getColumnIndexStrict(columnLabel), value); + return getRowIndex(getColumnIndexStrict(columnIdentifier), value); } public int getRowIndex(int columnIndex, String value) @@ -298,11 +296,11 @@ public int getRowIndex(int columnIndex, String value) return -1; } - public int getRowIndexStrict(String columnLabel, String value) + public int getRowIndexStrict(CharSequence columnIdentifier, String value) { - int rowIndex = getRowIndex(columnLabel, value); + int rowIndex = getRowIndex(columnIdentifier, value); if (rowIndex < 0) - throw new NoSuchElementException("No row where: " + columnLabel + "=" + value); + throw new NoSuchElementException("No row where: " + columnIdentifier + "=" + value); return rowIndex; } @@ -310,49 +308,50 @@ public int getRowIndexStrict(int columnIndex, String value) { int rowIndex = getRowIndex(columnIndex, value); if (rowIndex < 0) - throw new NoSuchElementException("No row where: " + elementCache().getColumnHeaders().get(columnIndex).getText() + " = " + value); + throw new NoSuchElementException("No row where: " + elementCache().getColumnHeaderManager().getColumnHeader(columnIndex).getLabel() + " = " + value); return rowIndex; } - public String getSummaryStatFooterText(String columnLabel) + public String getSummaryStatFooterText(CharSequence columnIdentifier) { - return getSummaryStatFooterText(getColumnIndexStrict(columnLabel)); + return getSummaryStatFooterText(getColumnIndexStrict(columnIdentifier)); } public String getSummaryStatFooterText(int columnIndex) { columnIndex += hasSelectors() ? 1 : 0; String footerText = elementCache().getSummaryStatisticCells().get(columnIndex).getText(); - return footerText != null ? footerText.replaceAll("\\?", "") : null; + return footerText.replaceAll("\\?", ""); } /** * do nothing if column is already present, add it if it is not - * @param columnName name of column to add, if necessary + * @param fieldKey fieldKey of column to add, if necessary */ - public void ensureColumnPresent(String columnName) + public void ensureColumnPresent(CharSequence fieldKey) { - ensureColumnsPresent(columnName); + ensureColumnsPresent(fieldKey); } /** * check for presence of columns, add them if they are not already present * requires columns actually exist - * @param names names of columns to add + * @param fieldKeys fieldKeys of columns to add */ - public void ensureColumnsPresent(String... names) + public void ensureColumnsPresent(CharSequence... fieldKeys) { boolean opened = false; - for (String name: names) + for (CharSequence fieldKey: fieldKeys) { - if (getColumnIndex(name) == -1) + fieldKey = FieldKey.fromFieldKey(fieldKey); + if (getColumnIndex(fieldKey) == -1) { if (!opened) { getCustomizeView().openCustomizeViewPanel(); opened = true; } - getCustomizeView().addColumn(name); + getCustomizeView().addColumn(fieldKey.toString()); } } if (opened) @@ -362,12 +361,12 @@ public void ensureColumnsPresent(String... names) /** * returns index of the row of the first appearance of the specified data, in the specified column * - * @deprecated Use {@link #getRowIndex(String, String)} or {@link #getRowIndexStrict(String, String)} + * @deprecated Use {@link #getRowIndex(CharSequence, String)} or {@link #getRowIndexStrict(CharSequence, String)} */ @Deprecated - public int getIndexWhereDataAppears(String data, String column) + public int getIndexWhereDataAppears(String data, CharSequence columnIdentifier) { - return getRowIndex(column, data); + return getRowIndex(columnIdentifier, data); } public static Locator.XPathLocator detailsLinkLocator() @@ -412,99 +411,66 @@ public void click() }; } - public WebElement link(int row, String columnName) + public WebElement link(int row, CharSequence columnIdentifier) { - return link(row, getColumnIndexStrict(columnName)); + return link(row, getColumnIndexStrict(columnIdentifier)); } - protected int getColumnIndexStrict(String name) + protected int getColumnIndexStrict(CharSequence columnIdentifier) { - int columnIndex = getColumnIndex(name); - if (columnIndex < 0) - throw new NoSuchElementException("No column: " + name); - return columnIndex; + return elementCache().getColumnIndex(columnIdentifier).getDomIndex(); } /** * Get column index for - * @param name or label for desired column + * @param columnIdentifier fieldKey, name, or label for desired column * @return column index or -1 if column isn't present */ - public int getColumnIndex(String name) + public int getColumnIndex(CharSequence columnIdentifier) { - if (_columnIndexMap.isEmpty()) - { - final var columnNames = getColumnNames(); - for (int j = 0; j < columnNames.size(); j++) - { - _columnIndexMap.put(columnNames.get(j), j); - } - } - int i = _columnIndexMap.getOrDefault(name, -1); - - // Try removing spaces from column name - if (i < 0) + try { - i = _columnIndexMap.getOrDefault(name.replace(" ", ""), -1); - if (i >= 0) - { - TestLogger.warn(String.format("Unnecessary space in requested column name. " + - "Requested: \"%s\" Found: \"%s\"", name, getColumnNames().get(i))); - } + return getColumnIndexStrict(columnIdentifier); } - - // Try matching column label - if (i < 0) + catch (NoSuchElementException e1) { - List columnLabelsWithoutSpaces = new ArrayList<>(getColumnLabels().size()); - getColumnLabels().forEach(s -> columnLabelsWithoutSpaces.add(s.replace(" ", ""))); - i = columnLabelsWithoutSpaces.indexOf(name.replace(" ", "")); - if (i >= 0) - { - TestLogger.warn(String.format("Please reference columns by name instead of label. " + - "Requested column with label: \"%s\" Found column with name: \"%s\"", name, getColumnNames().get(i))); - } + return -1; } - - return i; } public List getColumnLabels() { getComponentElement().isEnabled(); // validate cached element - if (_columnLabels.isEmpty()) - { - _columnLabels.addAll(getWrapper().getTexts(elementCache().getColumnHeaders())); - if (hasSelectors()) - _columnLabels.remove(0); - } - - return new ArrayList<>(_columnLabels); + return elementCache().getColumnHeaderManager().getColumnHeaders().stream() + .map(FieldReference::getLabel).toList(); } public List getColumnNames() { getComponentElement().isEnabled(); // validate cached element - if (_columnNames.isEmpty()) - { - List columnHeaders = elementCache().getColumnHeaders(); - for (int i = hasSelectors() ? 1 : 0; i < columnHeaders.size(); i++) - { - String columnName = columnHeaders.get(i).getAttribute("data-column-name"); - if (columnName.startsWith(getDataRegionName() + ":")) - columnName = columnName.substring(getDataRegionName().length() + 1); - _columnNames.add(columnName); - } - } + return elementCache().getColumnHeaderManager().getColumnHeaders().stream() + .map(FieldReference::getName).toList(); + } - return new ArrayList<>(_columnNames); + public List getFieldKeyStrings() + { + return getFieldKeys().stream() + .map(FieldKey::toString).toList(); } - public String getColumnTitle(String columnName) + public List getFieldKeys() { - WebElement columnHeader = elementCache().getColumnHeader(columnName); + getComponentElement().isEnabled(); // validate cached element + + return elementCache().getColumnHeaderManager().getColumnHeaders().stream() + .map(FieldReference::getFieldKey).toList(); + } + + public String getColumnTitle(CharSequence columnIdentifier) + { + WebElement columnHeader = elementCache().getColumnHeader(columnIdentifier).getElement(); return columnHeader.getAttribute("title"); } @@ -523,22 +489,22 @@ public List getColumnDataAsText(int col) return columnText; } - public List getColumnDataAsText(String name) + public List getColumnDataAsText(CharSequence columnIdentifier) { - return getColumnDataAsText(getColumnIndexStrict(name)); + return getColumnDataAsText(getColumnIndexStrict(columnIdentifier)); } - public Map getRowDataAsMap(String colName, String value) + public Map getRowDataAsMap(CharSequence columnIdentifier, String value) { - return getRowDataAsMap(getRowIndexStrict(colName, value)); + return getRowDataAsMap(getRowIndexStrict(columnIdentifier, value)); } public Map getRowDataAsMap(int row) { Map rowMap = new CaseInsensitiveHashMap<>(); - for (String colName : getColumnNames()) + for (FieldKey fieldKey : getFieldKeys()) { - rowMap.put(colName, getDataAsText(row, colName)); + rowMap.put(fieldKey.toString(), getDataAsText(row, fieldKey)); } return rowMap; } @@ -566,10 +532,10 @@ public List getRowDataAsText(int row) return rowText; } - public List getRowDataAsText(int row, String... columns) + public List getRowDataAsText(int row, CharSequence... columnIdentifiers) { List rowData = new ArrayList<>(); - for (String column : columns) + for (CharSequence column : columnIdentifiers) { rowData.add(getDataAsText(row, column)); } @@ -581,19 +547,19 @@ public List getRowDataAsText(int row, String... columns) * preconditions: must be on start page of table * postconditions: at start of table */ - public List> getFullColumnValues(String... columnNames) + public List> getFullColumnValues(CharSequence... columnIdentifiers) { boolean moreThanOnePage = getWrapper().isElementPresent(Locator.linkWithText("Next")); if (moreThanOnePage) { - showAll(); + rowSelector().showAll(); } List> columns = new ArrayList<>(); - for (String columnName : columnNames) + for (CharSequence columnIdentifier : columnIdentifiers) { - columns.add(getColumnDataAsText(columnName)); + columns.add(getColumnDataAsText(columnIdentifier)); } if (moreThanOnePage) @@ -604,14 +570,14 @@ public List> getFullColumnValues(String... columnNames) return columns; } - public List> getRows(String...columnNames) + public List> getRows(CharSequence... columnIdentifiers) { - List> fullColumnValues = getFullColumnValues(columnNames); + List> fullColumnValues = getFullColumnValues(columnIdentifiers); return collateColumnsIntoRows(fullColumnValues); } @SafeVarargs - public static List> collateColumnsIntoRows(List...columns) + public static List> collateColumnsIntoRows(List... columns) { return collateColumnsIntoRows(Arrays.asList(columns)); } @@ -656,7 +622,7 @@ public int getRowIndex(String pk) if (cached != null) return cached.intValue(); - int row = 0; + int row = _mapRows.size(); try { while (true) @@ -684,9 +650,9 @@ protected int getRowIndexStrict(String pk) return rowIndex; } - public WebElement findCell(int row, String column) + public WebElement findCell(int row, CharSequence columnIdentifier) { - return findCell(row, getColumnIndexStrict(column)); + return findCell(row, getColumnIndexStrict(columnIdentifier)); } public WebElement findCell(int row, int column) @@ -699,22 +665,19 @@ public String getDataAsText(int row, int column) { WebElement cell = findCell(row, column); // Will clear cache if needed - if (_dataCache.get(row) == null) - _dataCache.put(row, new TreeMap<>()); - if (_dataCache.get(row).get(column) == null) - _dataCache.get(row).put(column, cell.getText()); - - return _dataCache.get(row).get(column); + return _dataCache + .computeIfAbsent(row, k -> new LinkedHashMap<>()) + .computeIfAbsent(column, k -> cell.getText()); } - public String getDataAsText(int row, String columnName) + public String getDataAsText(int row, CharSequence columnIdentifier) { - return getDataAsText(row, getColumnIndexStrict(columnName)); + return getDataAsText(row, getColumnIndexStrict(columnIdentifier)); } - public String getDataAsText(String pk, String columnName) + public String getDataAsText(String pk, CharSequence columnIdentifier) { - return getDataAsText(getRowIndexStrict(pk), getColumnIndexStrict(columnName)); + return getDataAsText(getRowIndexStrict(pk), getColumnIndexStrict(columnIdentifier)); } /* Key is the PK on the table, it is usually the contents of the 'value' attribute of the row selector checkbox */ @@ -798,9 +761,9 @@ private String _getHref(int row, int column) return link(row, column).getAttribute("href"); } - public String getHref(int row, String columnName) + public String getHref(int row, CharSequence columnIdentifier) { - return getHref(row, getColumnIndexStrict(columnName)); + return getHref(row, getColumnIndexStrict(columnIdentifier)); } public boolean hasHref(int row, int column) @@ -817,45 +780,45 @@ private boolean _hasHref(int row, int column) return !Locator.xpath("a").findElements(cell).isEmpty(); } - public boolean hasHref(int row, String columnName) + public boolean hasHref(int row, CharSequence columnIdentifier) { - return hasHref(row, getColumnIndexStrict(columnName)); + return hasHref(row, getColumnIndexStrict(columnIdentifier)); } - public WebElement getFlag(int row, String columnName) + public WebElement getFlag(int row, CharSequence columnIdentifier) { - var cell = findCell(row, columnName); + var cell = findCell(row, columnIdentifier); return tagWithAttribute("i", "data-flagid").findElement(cell); } - public boolean isFlagEnabled(int row, String columnName) + public boolean isFlagEnabled(int row, CharSequence columnIdentifier) { - var flag = getFlag(row, columnName); + var flag = getFlag(row, columnIdentifier); return isFlagEnabled(flag); } private boolean isFlagEnabled(WebElement flag) { - return flag.getAttribute("class").contains("lk-flag-enabled"); + return StringUtils.trimToEmpty(flag.getDomAttribute("class")).contains("lk-flag-enabled"); } - public boolean isFlagDisabled(int row, String columnName) + public boolean isFlagDisabled(int row, CharSequence columnIdentifier) { - var flag = getFlag(row, columnName); + var flag = getFlag(row, columnIdentifier); return isFlagDisabled(flag); } private boolean isFlagDisabled(WebElement flag) { - return flag.getAttribute("class").contains("lk-flag-disabled"); + return StringUtils.trimToEmpty(flag.getDomAttribute("class")).contains("lk-flag-disabled"); } /** * Get the flag value for the column or null if unset. */ - public String getFlagValue(int row, String columnName) + public String getFlagValue(int row, CharSequence columnIdentifier) { - var flag = getFlag(row, columnName); + var flag = getFlag(row, columnIdentifier); return getFlagValue(flag); } @@ -874,12 +837,12 @@ else if (isFlagDisabled(flag)) throw new AssertionError("Expected flag class to be either 'lk-flag-enabled' or 'lk-flag-disabled'"); } - public void setFlagValueForSelectedRows(String columnName, String value) + public void setFlagValueForSelectedRows(CharSequence columnIdentifier, String value) { int checkedCount = getCheckedCount(); assertTrue(checkedCount > 0); - var flag = getFlag(0, columnName); + var flag = getFlag(0, columnIdentifier); flag.click(); var prompt = new MessagePrompt("Review", getDriver()); @@ -890,9 +853,9 @@ public void setFlagValueForSelectedRows(String columnName, String value) /** * Set the flag value for the column or null to clear the flag. */ - public void setFlagValue(int row, String columnName, String value) + public void setFlagValue(int row, CharSequence columnIdentifier, String value) { - var flag = getFlag(row, columnName); + var flag = getFlag(row, columnIdentifier); setFlagValue(flag, value); } @@ -903,9 +866,9 @@ private void setFlagValue(WebElement flag, String value) assertEquals(value, getFlagValue(flag)); } - public void clearFlagValue(int row, String columnName) + public void clearFlagValue(int row, CharSequence columnIdentifier) { - setFlagValue(row, columnName, null); + setFlagValue(row, columnIdentifier, null); } /** @@ -921,28 +884,28 @@ public void clearFlagValues() } } - public ColumnChartRegion createBarChart(String columnName) + public ColumnChartRegion createBarChart(CharSequence columnIdentifier) { - return createColumnChart(columnName, "Bar Chart"); + return createColumnChart(columnIdentifier, "Bar Chart"); } - public ColumnChartRegion createPieChart(String columnName) + public ColumnChartRegion createPieChart(CharSequence columnIdentifier) { - return createColumnChart(columnName, "Pie Chart"); + return createColumnChart(columnIdentifier, "Pie Chart"); } - public ColumnChartRegion createBoxAndWhiskerChart(String columnName) + public ColumnChartRegion createBoxAndWhiskerChart(CharSequence columnIdentifier) { - return createColumnChart(columnName, "Box & Whisker"); + return createColumnChart(columnIdentifier, "Box & Whisker"); } @LogMethod - public ColumnChartRegion createColumnChart(String columnName, String chartType) + public ColumnChartRegion createColumnChart(CharSequence columnIdentifier, String chartType) { Locator cssPlotLocator = Locator.css("div.labkey-dataregion-msg-plot-analytic svg"); int initialNumOfPlots = cssPlotLocator.findElements(this).size(); - clickColumnMenu(columnName, false, chartType); + clickColumnMenu(columnIdentifier, false, chartType); cssPlotLocator.index(initialNumOfPlots).waitForElement(this, getUpdateTimeout()); return new ColumnChartRegion(this); @@ -953,27 +916,27 @@ public ColumnChartRegion getColumnPlotRegion() return new ColumnChartRegion(this); } - public void setSort(String columnName, SortDirection direction) + public void setSort(CharSequence columnIdentifier, SortDirection direction) { - getWrapper().log("Setting sort in " + getDataRegionName() + " for " + columnName + " to " + direction.toString()); - doAndWaitForUpdate(() -> clickColumnMenu(columnName, !isAsync(), "Sort " + (direction.equals(SortDirection.ASC) ? "Ascending" : "Descending"))); + getWrapper().log("Setting sort in " + getDataRegionName() + " for " + columnIdentifier + " to " + direction.toString()); + doAndWaitForUpdate(() -> clickColumnMenu(columnIdentifier, !isAsync(), "Sort " + (direction.equals(SortDirection.ASC) ? "Ascending" : "Descending"))); } - public void setSummaryStatistic(String columnName, String stat, @Nullable String expectedValue) + public void setSummaryStatistic(CharSequence columnIdentifier, String stat, @Nullable String expectedValue) { - clickSummaryStatistic(columnName, stat, false, expectedValue); + clickSummaryStatistic(columnIdentifier, stat, false, expectedValue); } - public void clearSummaryStatistic(String columnName, String stat, @Nullable String expectedValue) + public void clearSummaryStatistic(CharSequence columnIdentifier, String stat, @Nullable String expectedValue) { - clickSummaryStatistic(columnName, stat, true, expectedValue); + clickSummaryStatistic(columnIdentifier, stat, true, expectedValue); } - private void clickSummaryStatistic(String columnName, String stat, boolean isExpectedToBeChecked, @Nullable String expectedValue) + private void clickSummaryStatistic(CharSequence columnIdentifier, String stat, boolean isExpectedToBeChecked, @Nullable String expectedValue) { String clearOrSet = isExpectedToBeChecked ? "Clearing" : "Setting"; - TestLogger.log(clearOrSet + " the " + stat + " summary statistic in " + getDataRegionName() + " for " + columnName); - clickColumnMenu(columnName, false, "Summary Statistics..."); + TestLogger.log(clearOrSet + " the " + stat + " summary statistic in " + getDataRegionName() + " for " + columnIdentifier); + clickColumnMenu(columnIdentifier, false, "Summary Statistics..."); SummaryStatisticsDialog statsWindow = new SummaryStatisticsDialog(this); @@ -988,23 +951,23 @@ private void clickSummaryStatistic(String columnName, String stat, boolean isExp statsWindow.apply(); } - public void verifySummaryStatisticValue(String columnName, String stat, String expectedValue, String filterDescription) + public void verifySummaryStatisticValue(CharSequence columnIdentifier, String stat, String expectedValue, String filterDescription) { - clickColumnMenu(columnName, false, "Summary Statistics..."); + clickColumnMenu(columnIdentifier, false, "Summary Statistics..."); SummaryStatisticsDialog statsWindow = new SummaryStatisticsDialog(this); assertEquals("Stat value not as expected for " + stat + " with filter " + filterDescription, expectedValue, statsWindow.getValue(stat)); statsWindow.cancel(); } - public void removeColumn(String columnName) + public void removeColumn(CharSequence columnIdentifier) { - removeColumn(columnName, false); + removeColumn(columnIdentifier, false); } - public void removeColumn(String columnName, boolean errorExpected) + public void removeColumn(CharSequence columnIdentifier, boolean errorExpected) { - TestLogger.log("Removing column " + columnName + " in " + getDataRegionName()); - clickColumnMenu(columnName, !errorExpected, "Remove Column"); + TestLogger.log("Removing column " + columnIdentifier + " in " + getDataRegionName()); + clickColumnMenu(columnIdentifier, !errorExpected, "Remove Column"); if (errorExpected) { @@ -1017,22 +980,22 @@ public void removeColumn(String columnName, boolean errorExpected) } else { - Window removeError = new Window("Error", getDriver()); + Window removeError = new Window<>("Error", getDriver()); assertTrue(removeError.getBody().contains("You must select at least one field to display in the grid.")); removeError.clickButton("OK", true); } } } - public void clearSort(String columnName) + public void clearSort(CharSequence columnIdentifier) { - doAndWaitForUpdate(() -> clickColumnMenu(columnName, !isAsync(), "Clear Sort")); + doAndWaitForUpdate(() -> clickColumnMenu(columnIdentifier, !isAsync(), "Clear Sort")); } - public WebElement openFilterDialog(String columnName) + public WebElement openFilterDialog(CharSequence columnIdentifier) { - String columnLabel = elementCache().getColumnHeader(columnName).getText(); - clickColumnMenu(columnName, false, "Filter..."); + String columnLabel = elementCache().getColumnHeader(columnIdentifier).getLabel(); + clickColumnMenu(columnIdentifier, false, "Filter..."); final Locator.XPathLocator filterDialog = ExtHelper.Locators.window("Show Rows Where " + columnLabel + "..."); WebElement filterDialogElement = getWrapper().waitForElement(filterDialog); @@ -1047,15 +1010,15 @@ public WebElement openFilterDialog(String columnName) } @LogMethod (quiet = true) - public void setFilter(@LoggedParam String columnName, @LoggedParam String filterType) + public void setFilter(@LoggedParam CharSequence columnIdentifier, @LoggedParam String filterType) { - setFilter(columnName, filterType, null); + setFilter(columnIdentifier, filterType, null); } @LogMethod (quiet = true) - public void setFilter(@LoggedParam String columnName, @LoggedParam String filterType, @Nullable @LoggedParam String filter) + public void setFilter(@LoggedParam CharSequence columnIdentifier, @LoggedParam String filterType, @Nullable @LoggedParam String filter) { - setUpFilter(columnName, filterType, filter); + setUpFilter(columnIdentifier, filterType, filter); doAndWaitForUpdate(() -> getWrapper().clickButton("OK", isAsync() ? 0 : getUpdateTimeout())); } @@ -1064,41 +1027,41 @@ public void setFilter(@LoggedParam String columnName, @LoggedParam String filter */ @Deprecated @LogMethod (quiet = true) - public void setFilter(@LoggedParam String columnName, @LoggedParam String filterType, @Nullable @LoggedParam String filter, int waitMillis) + public void setFilter(@LoggedParam CharSequence columnIdentifier, @LoggedParam String filterType, @Nullable @LoggedParam String filter, int waitMillis) { - setUpFilter(columnName, filterType, filter); + setUpFilter(columnIdentifier, filterType, filter); doAndWaitForUpdate(() -> getWrapper().clickButton("OK", waitMillis)); } @LogMethod (quiet = true) - public void setFilter(@LoggedParam String columnName, @LoggedParam String filterType, @Nullable @LoggedParam String filter, @Nullable @LoggedParam String filter2Type, @Nullable @LoggedParam String filter2) + public void setFilter(@LoggedParam CharSequence columnIdentifier, @LoggedParam String filterType, @Nullable @LoggedParam String filter, @Nullable @LoggedParam String filter2Type, @Nullable @LoggedParam String filter2) { - setUpFilter(columnName, filterType, filter, filter2Type, filter2); + setUpFilter(columnIdentifier, filterType, filter, filter2Type, filter2); doAndWaitForUpdate(() -> getWrapper().clickButton("OK", isAsync() ? 0 : getUpdateTimeout())); } @LogMethod (quiet = true) - public void setFacetedFilter(@LoggedParam String columnName, @LoggedParam String... values) + public void setFacetedFilter(@LoggedParam CharSequence columnIdentifier, @LoggedParam String... values) { - setUpFacetedFilter(columnName, values); + setUpFacetedFilter(columnIdentifier, values); doAndWaitForUpdate(() -> getWrapper().clickButton("OK", isAsync() ? 0 : getUpdateTimeout())); } - public void setUpFilter(String columnName, String filterType, String filter) + public void setUpFilter(CharSequence columnIdentifier, String filterType, String filter) { - setUpFilter(columnName, filterType, filter, null, null); + setUpFilter(columnIdentifier, filterType, filter, null, null); } - public void setUpFilter(String columnName, String filter1Type, @Nullable String filter1, @Nullable String filter2Type, @Nullable String filter2) + public void setUpFilter(CharSequence columnIdentifier, String filter1Type, @Nullable String filter1, @Nullable String filter2Type, @Nullable String filter2) { - String log = "Setting filter in " + getDataRegionName() + " for " + columnName + " to " + filter1Type.toLowerCase() + (filter1 != null ? " " + filter1 : ""); + String log = "Setting filter in " + getDataRegionName() + " for " + columnIdentifier + " to " + filter1Type.toLowerCase() + (filter1 != null ? " " + filter1 : ""); if (filter2Type != null) { log += " and " + filter2Type.toLowerCase() + (filter2 != null ? " " + filter2 : ""); } TestLogger.log(log); - openFilterDialog(columnName); + openFilterDialog(columnIdentifier); if (getWrapper().isElementPresent(Locator.css("span.x-tab-strip-text").withText("Choose Values"))) { @@ -1128,25 +1091,24 @@ public void setUpFilter(String columnName, String filter1Type, @Nullable String } } - public void setUpFacetedFilter(String columnName, String... values) + public void setUpFacetedFilter(CharSequence columnIdentifier, String... values) { if (values.length > 0) { - TestLogger.log("Setting filter in " + getDataRegionName() + " for " + columnName + " to one of: [" + String.join(", ", values) + "]"); + TestLogger.log("Setting filter in " + getDataRegionName() + " for " + columnIdentifier + " to one of: [" + String.join(", ", values) + "]"); } else { - TestLogger.log("Clear filter in " + getDataRegionName() + " for " + columnName); + TestLogger.log("Clear filter in " + getDataRegionName() + " for " + columnIdentifier); } - openFilterDialog(columnName); - String columnLabel = elementCache().getColumnHeader(columnName).getText(); + WebElement filterDialog = openFilterDialog(columnIdentifier); sleep(500); // Clear selections. assertEquals("Faceted filter tab should be selected.", "Choose Values", getWrapper().getText(Locator.css(".x-tab-strip-active"))); - if (!getWrapper().isElementPresent(Locator.xpath("//div[contains(@class, 'x-grid3-hd-checker-on')]"))) + if (!Locator.xpath("//div[contains(@class, 'x-grid3-hd-checker-on')]").existsIn(filterDialog)) { getWrapper().click(Locator.tagWithText("span","[All]")); } @@ -1156,26 +1118,26 @@ public void setUpFacetedFilter(String columnName, String... values) { for (String v : values) { - getWrapper().click(Locator.xpath(getWrapper()._extHelper.getExtDialogXPath("Show Rows Where " + columnLabel + "...") + - "//div[contains(@class,'x-grid3-row') and .//span[text()='" + v + "']]//div[@class='x-grid3-row-checker']")); + Locator.xpath("//div[contains(@class,'x-grid3-row') and .//span[text()='" + v + "']]//div[@class='x-grid3-row-checker']") + .findElement(filterDialog).click(); } } else if (values.length == 1) { - getWrapper().click(Locator.xpath(getWrapper()._extHelper.getExtDialogXPath("Show Rows Where " + columnLabel + "...")+ - "//div[contains(@class,'x-grid3-row')]//span[text()='" + values[0] + "']")); + Locator.xpath("//div[contains(@class,'x-grid3-row')]//span[text()='" + values[0] + "']") + .findElement(filterDialog).click(); } } - public void clearFilter(String columnName) + public void clearFilter(CharSequence columnIdentifier) { - clearFilter(columnName, isAsync() ? 0 : BaseWebDriverTest.WAIT_FOR_PAGE); + clearFilter(columnIdentifier, isAsync() ? 0 : BaseWebDriverTest.WAIT_FOR_PAGE); } - public void clearFilter(String columnName, int waitMillis) + public void clearFilter(CharSequence columnIdentifier, int waitMillis) { - TestLogger.log("Clearing filter in " + getDataRegionName() + " for " + columnName); - doAndWaitForUpdate(() -> clickColumnMenu(columnName, waitMillis > 0, "Clear Filter")); + TestLogger.log("Clearing filter in " + getDataRegionName() + " for " + columnIdentifier); + doAndWaitForUpdate(() -> clickColumnMenu(columnIdentifier, waitMillis > 0, "Clear Filter")); } public void clearAllFilters() @@ -1185,10 +1147,10 @@ public void clearAllFilters() api().expectingRefresh().executeScript("clearAllFilters()"); } - public void clearAllFilters(String columnName) + public void clearAllFilters(CharSequence columnIdentifier) { - TestLogger.log("Clearing filter in " + getDataRegionName() + " for " + columnName); - openFilterDialog(columnName); + TestLogger.log("Clearing filter in " + getDataRegionName() + " for " + columnIdentifier); + openFilterDialog(columnIdentifier); doAndWaitForUpdate(() -> getWrapper().clickButton("Clear All Filters", isAsync() ? 0 : getUpdateTimeout())); } @@ -1198,9 +1160,9 @@ public void clearVariables() doAndWaitForUpdate(()-> getWrapper().click(Locator.tagWithClass("span", "labkey-button ctx-clear-var"))); } - public void clickColumnMenu(String columnName, boolean pageLoad, String... menuItems) + public void clickColumnMenu(CharSequence columnIdentifier, boolean pageLoad, String... menuItems) { - final WebElement menu = elementCache().getColumnHeader(columnName); + final WebElement menu = elementCache().getColumnHeader(columnIdentifier).getElement(); getWrapper().scrollIntoView(menu); // some columns will be scrolled out of view; new BootstrapMenu(getDriver(), menu) .clickSubMenu(pageLoad ? getUpdateTimeout() : 0, menuItems); @@ -1290,10 +1252,10 @@ public void pagePrev() } /** - * @deprecated Ambiguous. Use {@link #rowSelector()}.{@link SelectorMenu#showAll()} + * Show all rows using the row selector menu (actually maxRows=5000). + * For more precise control, use {@link #rowSelector()}.{@link SelectorMenu#showAll()} * or {@link #getPagingWidget()}.{@link PagingWidget#clickShowAll()} */ - @Deprecated public void showAll() { rowSelector().showAll(); @@ -1348,9 +1310,9 @@ public void setMaxRows(int size) } @LogMethod - public TimeChartWizard createQuickChart(String columnName) + public TimeChartWizard createQuickChart(CharSequence columnIdentifier) { - clickColumnMenu(columnName, true, "Quick Chart"); + clickColumnMenu(columnIdentifier, true, "Quick Chart"); return new TimeChartWizard(getWrapper()).waitForReportRender(); } @@ -1375,9 +1337,9 @@ public void clickExportButton() elementCache().getExportButton().click(); } - public boolean columnHasChartOption(String columnName, String chartType) + public boolean columnHasChartOption(CharSequence columnIdentifier, String chartType) { - BootstrapMenu menu = new BootstrapMenu(getDriver(), elementCache().getColumnHeader(columnName)); + BootstrapMenu menu = new BootstrapMenu(getDriver(), elementCache().getColumnHeader(columnIdentifier).getElement()); menu.expand(); return menu.findVisibleMenuItems() .stream() @@ -1535,8 +1497,6 @@ protected boolean hasSelectors() } private final WebElement columnHeaderRow = Locator.id(getTableId() + "-column-header-row").findWhenNeeded(this); - private List columnHeaders; - private final Map columnHeadersByName = new CaseInsensitiveHashMap<>(); private List rows; private Map> cells; private final WebElement summaryStatRow = Locator.css("#" + getTableId() + " > tbody > tr.labkey-col-total").findWhenNeeded(getDriver()); @@ -1545,6 +1505,75 @@ protected boolean hasSelectors() private final WebElement toggleAllOnPage = Locator.input(".toggle").findWhenNeeded(toggleHeaderCell); // tri-state checkbox private final SelectorMenu selectionMenu = new SelectorMenu(toggleHeaderCell); + private FieldReferenceManager _fieldReferenceManager; + protected FieldReferenceManager getColumnHeaderManager() + { + if (_fieldReferenceManager == null) + { + List columnHeaders = new ArrayList<>(); + List headerCellElements = Locator.css("th.labkey-column-header").waitForElements(columnHeaderRow, WAIT_FOR_JAVASCRIPT); + + headerCellElements = headerCellElements.subList(hasSelectors() ? 1 : 0, headerCellElements.size()); + + int domIndex = 0; // Not actually the DOM index but many usages don't include the selector column + for (; domIndex < headerCellElements.size(); domIndex++) + { + columnHeaders.add(new DataRegionColumnHeader(headerCellElements.get(domIndex), domIndex)); + } + + _fieldReferenceManager = new FieldReferenceManager(columnHeaders); + } + return _fieldReferenceManager; + } + + public @NotNull FieldReference getColumnIndex(@NotNull CharSequence columnIdentifier) + { + boolean foundWithLabel = false; + FieldReference fieldReference = elementCache().getColumnHeaderManager().findFieldReferenceOrNull(columnIdentifier); + + if (fieldReference == null) + { + String noSpaces = columnIdentifier.toString().replace(" ", ""); + if (!noSpaces.equals(columnIdentifier.toString())) + { + fieldReference = elementCache().getColumnHeaderManager().findFieldReferenceOrNull(columnIdentifier); + if (fieldReference != null) + { + TestLogger.warn("Unnecessary space in requested column name. " + + "Requested: \"%s\" Found: \"%s\"".formatted(columnIdentifier, fieldReference.getFieldKey())); + } + else + { + Map columnLabelsWithoutSpaces = new HashMap<>(); + getColumnHeaderManager().getColumnHeaders().forEach(fr -> columnLabelsWithoutSpaces.putIfAbsent(fr.getLabel().replace(" ", ""), fr)); + fieldReference = columnLabelsWithoutSpaces.get(noSpaces); + if (fieldReference != null) + { + TestLogger.warn("Weird column label specified. Use actual label. " + + "Requested: \"%s\" Found: \"%s\"".formatted(columnIdentifier, fieldReference.getLabel())); + foundWithLabel = true; + } + } + } + } + + if (fieldReference == null) + { + throw new NoSuchElementException("No column: " + columnIdentifier); + } + + if (foundWithLabel || + !columnIdentifier.equals(fieldReference.getFieldKey()) && + !columnIdentifier.toString().equals(fieldReference.getName())) + { + TestLogger.warn("Please reference columns by name/fieldKey instead of label. " + + "Requested column with label: \"%s\" Found column with fieldKey: \"%s\"" + .formatted(columnIdentifier, fieldReference.getFieldKey())); + } + + return fieldReference; + } + protected List getDataRows() { if (rows == null) @@ -1584,7 +1613,7 @@ public void toggle() } catch (WebDriverException e) { - if (e.getMessage().contains("Other element would receive the click:
")) + if (StringUtils.trimToEmpty(e.getMessage()).contains("Other element would receive the click:
")) { // The checkbox obscured by the floating header getWrapper().scrollIntoView(getComponentElement()); @@ -1599,7 +1628,7 @@ public void toggle() protected List getCells(int row) { if (cells == null) - cells = new TreeMap<>(); + cells = new LinkedHashMap<>(); if (cells.get(row) == null) cells.put(row, Collections.unmodifiableList(Locator.xpath("td").findElements(getDataRow(row)))); return cells.get(row); @@ -1626,22 +1655,14 @@ protected List getSummaryStatisticCells() return summaryStatCells; } - protected WebElement getColumnHeader(String colName) + protected @NotNull FieldReference getColumnHeader(CharSequence columnIdentifier) { - if (!columnHeadersByName.containsKey(colName)) - { - columnHeadersByName.put(colName, Locator.findAnyElement("Column header named " + colName, this, - Locators.columnHeader(getDataRegionName(), colName), - Locators.columnHeader(getDataRegionName(), colName.toLowerCase()))); - } - return columnHeadersByName.get(colName); + return getColumnHeaderManager().findFieldReference(columnIdentifier); } - protected List getColumnHeaders() + protected List getColumnHeaders() { - if (columnHeaders == null) - columnHeaders = Collections.unmodifiableList(Locator.css("th.labkey-column-header").findElements(columnHeaderRow)); - return columnHeaders; + return getColumnHeaderManager().getColumnHeaders(); } protected WebElement getExportButton() @@ -1718,4 +1739,27 @@ public String getLabel() return _label; } } + + protected class DataRegionColumnHeader extends FieldReference + { + public DataRegionColumnHeader(WebElement element, int domIndex) + { + super(element, domIndex); + } + + @Override + protected String labelSupplier() + { + return getElement().getText(); + } + + @Override + protected FieldKey fieldKeySupplier() + { + String columnNameAttribute = StringUtils.trimToEmpty(getElement().getDomAttribute("data-column-name")); + if (columnNameAttribute.startsWith(getDataRegionName() + ":")) + columnNameAttribute = columnNameAttribute.substring(getDataRegionName().length() + 1); + return FieldKey.fromFieldKey(columnNameAttribute); + } + } } diff --git a/src/org/labkey/test/util/IssuesHelper.java b/src/org/labkey/test/util/IssuesHelper.java index 7783a394f0..87bb67faf9 100644 --- a/src/org/labkey/test/util/IssuesHelper.java +++ b/src/org/labkey/test/util/IssuesHelper.java @@ -143,7 +143,7 @@ public void deleteIssueLists(String projectName, LabKeySiteWrapper test) DataRegionTable table = new DataRegionTable("IssueListDef", getDriver()); if (table.getDataRowCount() > 0) { - table.checkAll(); + table.checkAllOnPage(); table.clickHeaderButton("Delete"); // delete confirmation for issues-deleteIssueList action clickButton("Delete"); diff --git a/src/org/labkey/test/util/TestDataGenerator.java b/src/org/labkey/test/util/TestDataGenerator.java index 40e05536f1..68315ca9bf 100644 --- a/src/org/labkey/test/util/TestDataGenerator.java +++ b/src/org/labkey/test/util/TestDataGenerator.java @@ -74,7 +74,7 @@ public class TestDataGenerator private static final char WIDE_PLACEHOLDER = '\u03A0'; // 'Π' - Wide character can't be picked from the string with 'charAt' private static final String NON_LATIN_STRING = "\u0438\uC548\u306F"; // "и안は" // chose a Character random from this String - public static final String CHARSET_STRING = "ABCDEFG01234abcdefvxyz~!@#$%^&*()-+=_{}[]|:;\"\\',.<>" + NON_LATIN_STRING + WIDE_PLACEHOLDER; + public static final String CHARSET_STRING = "ABCDEFG01234abcdefvxyz~!@#$%^&*()-+=_{}[]|:;\"',.<>" + NON_LATIN_STRING + WIDE_PLACEHOLDER; public static final String ALPHANUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvxyz"; public static final String DOMAIN_SPECIAL_STRING = "+- _.:&()/"; public static final String ILLEGAL_DOMAIN_NAME_CHARSET = "<>[]{};,`\"~!@#$%^*=|?\\"; diff --git a/src/org/labkey/test/util/compliance/ComplianceAccountUtils.java b/src/org/labkey/test/util/compliance/ComplianceAccountUtils.java index c10eb6ca87..79dd94dbe5 100644 --- a/src/org/labkey/test/util/compliance/ComplianceAccountUtils.java +++ b/src/org/labkey/test/util/compliance/ComplianceAccountUtils.java @@ -27,7 +27,7 @@ public void reactivateAllAccounts() _test.clickAndWait(Locator.linkWithText("include inactive users")); usersTable = new DataRegionTable("Users", _test); int countOfAllUsers = usersTable.getDataRowCount(); - usersTable.checkAll(); + usersTable.checkAllOnPage(); _test.log("Number of active users: " + countOfActiveUsers + " Number of total users: " + countOfAllUsers);