From dd8ae10ec997c4d1e3800f6c90ceabd3b9d9f359 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 9 Sep 2025 15:59:27 -0500 Subject: [PATCH 1/4] SampleTypeTest test cases for amounts and units (error when missing amount or unit, successful conversion, success insert/update/import) --- .../experiment/SampleTypeDefinition.java | 2 +- src/org/labkey/test/tests/SampleTypeTest.java | 165 ++++++++++++++++++ .../labkey/test/util/SampleTypeHelper.java | 11 ++ 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/params/experiment/SampleTypeDefinition.java b/src/org/labkey/test/params/experiment/SampleTypeDefinition.java index f264846cb6..f23fcf8d14 100644 --- a/src/org/labkey/test/params/experiment/SampleTypeDefinition.java +++ b/src/org/labkey/test/params/experiment/SampleTypeDefinition.java @@ -101,7 +101,7 @@ protected MetricUnit getInventoryMetricUnit() return _inventoryMetricUnit; } - protected SampleTypeDefinition setInventoryMetricUnit(MetricUnit inventoryMetricUnit) + public SampleTypeDefinition setInventoryMetricUnit(MetricUnit inventoryMetricUnit) { _inventoryMetricUnit = inventoryMetricUnit; return this; diff --git a/src/org/labkey/test/tests/SampleTypeTest.java b/src/org/labkey/test/tests/SampleTypeTest.java index 4962306eb9..4d5b44025e 100644 --- a/src/org/labkey/test/tests/SampleTypeTest.java +++ b/src/org/labkey/test/tests/SampleTypeTest.java @@ -51,6 +51,7 @@ import org.labkey.test.params.FieldDefinition.ColumnType; import org.labkey.test.params.FieldDefinition.LookupInfo; import org.labkey.test.params.FieldInfo; +import org.labkey.test.params.experiment.MetricUnit; import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.util.ApiPermissionsHelper; import org.labkey.test.util.AuditLogHelper; @@ -1858,6 +1859,138 @@ public void testFieldUniqueConstraint() assertTextNotPresent("unique_constraint_test_fieldname_2"); } + @Test + public void testAmountsAndUnitsWithDisplayUnit() + { + SampleTypeHelper sampleHelper = new SampleTypeHelper(this); + String sampleTypeName = "Sample Amounts and Units with Display Unit Test"; + + clickProject(PROJECT_NAME); + SampleTypeDefinition sampleTypeDefinition = new SampleTypeDefinition(sampleTypeName); + sampleTypeDefinition.setInventoryMetricUnit(TestMetricUnit.L); + SampleTypeAPIHelper.createEmptySampleType(getProjectName(), sampleTypeDefinition); + refresh(); + sampleHelper.goToSampleType(sampleTypeName); + + log("verify error when inserting a row with an amount but no unit"); + sampleHelper.insertRow(Map.of("Name", "AU-ERR-1", "StoredAmount", "0.0")); + assertTextPresent("No Units value provided for Amount 0.0."); + clickButton("Cancel"); + log("verify error when inserting a row with a unit but no amount"); + sampleHelper.insertRow(Map.of("Name", "AU-ERR-2", "Units", "mL")); + assertTextPresent("No Amount value provided for Units mL."); + clickButton("Cancel"); + + log("verify error when inserting a row with incompatible units"); + sampleHelper.insertRow(Map.of("Name", "AU-ERR-1", "StoredAmount", "5.0", "Units", "mg")); + assertTextPresent("Units value (mg) is not compatible with the display units (mL)."); + clickButton("Cancel"); + sampleHelper.insertRow(Map.of("Name", "AU-ERR-1", "StoredAmount", "5.0", "Units", "unit")); + assertTextPresent("Units value (unit) is not compatible with the display units (mL)."); + clickButton("Cancel"); + + log("verify inserting a row with compatible units succeeds and are converted"); + sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-1", "StoredAmount", "5.0", "Units", "mL")); + verifySampleAmountUnitValues("AU-SUCCESS-1", "0.005", "L"); + sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-2", "StoredAmount", "0.005", "Units", "L")); + verifySampleAmountUnitValues("AU-SUCCESS-2", "0.005", "L"); + sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-3", "StoredAmount", "5000", "Units", "uL")); + verifySampleAmountUnitValues("AU-SUCCESS-3", "0.005", "L"); + + log("verify updating a row with incompatible units fails"); + sampleHelper.updateRow(0, Map.of("Units", "mg")); + assertTextPresent("Units value (mg) is not compatible with the display units (mL)."); + clickButton("Cancel"); + + log("verify updating a row with compatible units succeeds and are converted"); + sampleHelper.updateRow(2, Map.of("StoredAmount", "0.00123", "Units", "mL")); + verifySampleAmountUnitValues("AU-SUCCESS-1", "1.23E-6", "L"); + + log("verify rounding precision for display units"); + sampleHelper.updateRow(2, Map.of("StoredAmount", "0.12345678999", "Units", "L")); + verifySampleAmountUnitValues("AU-SUCCESS-1", "0.12345679", "L"); + + log("verify bulk import with incompatible units fails"); + sampleHelper.bulkImportExpectingError(List.of(Map.of("Name", "AU-BULK-ERR-1", "StoredAmount", "0", "Units", "kg")), SampleTypeHelper.IMPORT_OPTION); + assertTextPresent("Units value (kg) is not compatible with the display units (mL)."); + clickButton("Cancel"); + + log("verify bulk import with compatible units succeeds and are converted"); + sampleHelper.bulkImport(List.of(Map.of("Name", "AU-BULK-SUCCESS-1", "StoredAmount", "0", "Units", "mL"))); + verifySampleAmountUnitValues("AU-BULK-SUCCESS-1", "0.0", "L"); + sampleHelper.bulkImport(List.of(Map.of("Name", "AU-BULK-SUCCESS-2", "StoredAmount", "0.005", "Units", "L"))); + verifySampleAmountUnitValues("AU-BULK-SUCCESS-2", "0.005", "L"); + sampleHelper.bulkImport(List.of(Map.of("Name", "AU-BULK-SUCCESS-3", "StoredAmount", "5000", "Units", "uL"))); + verifySampleAmountUnitValues("AU-BULK-SUCCESS-3", "0.005", "L"); + } + + @Test + public void testAmountsAndUnitsWithoutDisplayUnit() + { + SampleTypeHelper sampleHelper = new SampleTypeHelper(this); + String sampleTypeName = "Sample Amounts and Units without Display Unit Test"; + + clickProject(PROJECT_NAME); + CreateSampleTypePage createPage = sampleHelper + .goToCreateNewSampleType() + .setName(sampleTypeName); + assertTextNotPresent("Amount Display Units"); + createPage.clickSave(); + sampleHelper.goToSampleType(sampleTypeName); + + log("verify that inserting a row with an amount or unit requires both fields to be filled in"); + // insert row with amount but not unit (error expected) + sampleHelper.insertRow(Map.of("Name", "AU-ERR-1", "StoredAmount", "5.0")); + assertTextPresent("No Units value provided for Amount 5.0."); + clickButton("Cancel"); + // insert row with unit but not amount (error expected) + sampleHelper.insertRow(Map.of("Name", "AU-ERR-2", "Units", "mg")); + assertTextPresent("No Amount value provided for Units mg."); + clickButton("Cancel"); + // insert row with both amount and unit (success) + sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-1", "StoredAmount", "5.0", "Units", "mg")); + verifySampleAmountUnitValues("AU-SUCCESS-1", "5", "mg"); + + log("verify that updating a row with an amount or unit requires both fields to be filled in"); + // update row with amount but not unit (error expected) + sampleHelper.updateRow(0, Map.of("Units", "")); + assertTextPresent("No Units value provided for Amount 5.0."); + clickButton("Cancel"); + // update row with unit but not amount (error expected) + sampleHelper.updateRow(0, Map.of("StoredAmount", "")); + assertTextPresent("No Amount value provided for Units mg."); + clickButton("Cancel"); + // update row with both amount and unit (success) + sampleHelper.updateRow(0, Map.of("StoredAmount", "10.0123", "Units", "g")); + verifySampleAmountUnitValues("AU-SUCCESS-1", "10.0123", "g"); + + log("verify that bulk import with an amount or unit requires both fields to be filled in"); + // bulk import with amount but not unit (error expected) + sampleHelper.bulkImportExpectingError(List.of(Map.of("Name", "AU-BULK-ERR-1", "StoredAmount", "0")), SampleTypeHelper.IMPORT_OPTION); + assertTextPresent("When adding or updating samples, a Units value must be provided when there is a value for Amount."); + clickButton("Cancel"); + // bulk import with unit but not amount (error expected) + sampleHelper.bulkImportExpectingError(List.of(Map.of("Name", "AU-BULK-ERR-2", "Units", "mL")), SampleTypeHelper.IMPORT_OPTION); + assertTextPresent("When adding or updating samples, a Amount value must be provided when there is a value for Units."); + clickButton("Cancel"); + // bulk import with both amount and unit (success expected) + sampleHelper.bulkImport(List.of(Map.of("Name", "AU-BULK-SUCCESS-1", "StoredAmount", "0", "Units", "L"))); + verifySampleAmountUnitValues("AU-BULK-SUCCESS-1", "0", "L"); + + log("verify the bulk import with RawAmount and RawUnits are ignored"); + sampleHelper.bulkImport(List.of(Map.of("Name", "AU-BULK-SUCCESS-2", "RawAmount", "1000", "RawUnits", "kg"))); + verifySampleAmountUnitValues("AU-BULK-SUCCESS-2", " ", " "); + } + + private void verifySampleAmountUnitValues(String name, String expectedAmount, String expectedUnits) + { + DataRegionTable drt = DataRegionTable.findDataRegionWithinWebpart(this, "Sample Type Contents"); + drt.setFilter("Name", "Equals", name); + checker().verifyEquals("StoredAmount value not as expected for sample " + name, expectedAmount, drt.getDataAsText(0, "Amount")); + checker().verifyEquals("Units value not as expected for sample " + name, expectedUnits, drt.getDataAsText(0, "Units")); + drt.clearAllFilters(); + } + private void viewRawTableMetadata(String sampleTypeName) { beginAt(WebTestHelper.buildURL("query", getProjectName(), "rawTableMetaData", Map.of("schemaName", "samples", "query.queryName", sampleTypeName))); @@ -1922,4 +2055,36 @@ public BrowserType bestBrowser() { return BrowserType.CHROME; } + + public enum TestMetricUnit implements MetricUnit + { + G("g", "g (grams)"), + MG("mg", "mg (milligrams)"), + KG("kg", "kg (kilograms)"), + ML("mL", "mL (milliliters)"), + UL("uL", "uL (microliters)"), + L("L", "L (liters)"), + UNIT("unit", "unit"); + + private final String _value; + private final String _label; + + TestMetricUnit(String value, String label) + { + _value = value; + _label = label; + } + + @Override + public String getLabel() + { + return _label; + } + + @Override + public String getValue() + { + return _value; + } + } } diff --git a/src/org/labkey/test/util/SampleTypeHelper.java b/src/org/labkey/test/util/SampleTypeHelper.java index 21c85cf3e4..20175acd72 100644 --- a/src/org/labkey/test/util/SampleTypeHelper.java +++ b/src/org/labkey/test/util/SampleTypeHelper.java @@ -205,6 +205,17 @@ public void insertRow(Map fieldValues) clickButton("Submit"); } + public void updateRow(int rowIndex, Map fieldValues) + { + DataRegionTable drt = getSamplesDataRegionTable(); + drt.clickEditRow(rowIndex); + for (Map.Entry fieldValue : fieldValues.entrySet()) + { + setFormElement(Locator.name("quf_" + fieldValue.getKey()), fieldValue.getValue()); + } + clickButton("Submit"); + } + public void bulkImport(File dataFile) { fileImport(dataFile, IMPORT_OPTION); From 8b9c0c1ff057f9d901c35e82cb85a0712d762560 Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 9 Sep 2025 17:10:48 -0500 Subject: [PATCH 2/4] SampleTypeTest test cases for amounts and units sorting and filtering with display units --- src/org/labkey/test/tests/SampleTypeTest.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/org/labkey/test/tests/SampleTypeTest.java b/src/org/labkey/test/tests/SampleTypeTest.java index 4d5b44025e..ca7e6ce43d 100644 --- a/src/org/labkey/test/tests/SampleTypeTest.java +++ b/src/org/labkey/test/tests/SampleTypeTest.java @@ -32,6 +32,7 @@ import org.labkey.remoteapi.query.SelectRowsResponse; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; +import org.labkey.test.SortDirection; import org.labkey.test.TestFileUtils; import org.labkey.test.WebTestHelper; import org.labkey.test.categories.Daily; @@ -1890,12 +1891,12 @@ public void testAmountsAndUnitsWithDisplayUnit() clickButton("Cancel"); log("verify inserting a row with compatible units succeeds and are converted"); - sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-1", "StoredAmount", "5.0", "Units", "mL")); - verifySampleAmountUnitValues("AU-SUCCESS-1", "0.005", "L"); - sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-2", "StoredAmount", "0.005", "Units", "L")); - verifySampleAmountUnitValues("AU-SUCCESS-2", "0.005", "L"); - sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-3", "StoredAmount", "5000", "Units", "uL")); - verifySampleAmountUnitValues("AU-SUCCESS-3", "0.005", "L"); + sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-1", "StoredAmount", "1.0", "Units", "mL")); + verifySampleAmountUnitValues("AU-SUCCESS-1", "0.001", "L"); + sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-2", "StoredAmount", "0.002", "Units", "L")); + verifySampleAmountUnitValues("AU-SUCCESS-2", "0.002", "L"); + sampleHelper.insertRow(Map.of("Name", "AU-SUCCESS-3", "StoredAmount", "3000", "Units", "uL")); + verifySampleAmountUnitValues("AU-SUCCESS-3", "0.003", "L"); log("verify updating a row with incompatible units fails"); sampleHelper.updateRow(0, Map.of("Units", "mg")); @@ -1920,8 +1921,20 @@ public void testAmountsAndUnitsWithDisplayUnit() verifySampleAmountUnitValues("AU-BULK-SUCCESS-1", "0.0", "L"); sampleHelper.bulkImport(List.of(Map.of("Name", "AU-BULK-SUCCESS-2", "StoredAmount", "0.005", "Units", "L"))); verifySampleAmountUnitValues("AU-BULK-SUCCESS-2", "0.005", "L"); - sampleHelper.bulkImport(List.of(Map.of("Name", "AU-BULK-SUCCESS-3", "StoredAmount", "5000", "Units", "uL"))); - verifySampleAmountUnitValues("AU-BULK-SUCCESS-3", "0.005", "L"); + sampleHelper.bulkImport(List.of(Map.of("Name", "AU-BULK-SUCCESS-3", "StoredAmount", "4000", "Units", "uL"))); + verifySampleAmountUnitValues("AU-BULK-SUCCESS-3", "0.004", "L"); + + log("verify sorting on converted amounts works as expected"); + sampleHelper.getSamplesDataRegionTable().setSort("Amount", SortDirection.ASC); + assertEquals("Sample order sorted asc not as expected", + List.of("AU-BULK-SUCCESS-1", "AU-SUCCESS-2", "AU-SUCCESS-3", "AU-BULK-SUCCESS-3", "AU-BULK-SUCCESS-2", "AU-SUCCESS-1"), + sampleHelper.getSamplesDataRegionTable().getColumnDataAsText("Name")); + + log("verify filtering on converted amounts works as expected"); + sampleHelper.getSamplesDataRegionTable().setFilter("Amount", "Is Greater Than", "0.004"); + assertEquals("Sample order filtered not as expected", + List.of("AU-BULK-SUCCESS-2", "AU-SUCCESS-1"), + sampleHelper.getSamplesDataRegionTable().getColumnDataAsText("Name")); } @Test From a03d7965295146ae32cf332aff94db194732838b Mon Sep 17 00:00:00 2001 From: cnathe Date: Wed, 10 Sep 2025 10:02:56 -0500 Subject: [PATCH 3/4] EditableGridForm cancelChanges() --- .../labkey/test/components/ui/grids/EditableGridForm.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/org/labkey/test/components/ui/grids/EditableGridForm.java b/src/org/labkey/test/components/ui/grids/EditableGridForm.java index 4e1de11180..a70393a36d 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGridForm.java +++ b/src/org/labkey/test/components/ui/grids/EditableGridForm.java @@ -26,5 +26,12 @@ public T saveChanges() return getComponentAfterSave(); } + public T cancelChanges() + { + Locator.tagWithClass("button", "btn-default").withText("Cancel").findElement(_outerScope).click(); + getWrapper().shortWait().until(ExpectedConditions.stalenessOf(getComponentElement())); + return getComponentAfterSave(); + } + protected abstract T getComponentAfterSave(); } From e188855e68cbec889ae1e12a2233be1fdd15881e Mon Sep 17 00:00:00 2001 From: cnathe Date: Thu, 11 Sep 2025 16:14:19 -0500 Subject: [PATCH 4/4] QueryApiHelper selectRows param for ContainerFilter --- .../test/util/query/QueryApiHelper.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/org/labkey/test/util/query/QueryApiHelper.java b/src/org/labkey/test/util/query/QueryApiHelper.java index 2e0d3e63ef..1032ef98a4 100644 --- a/src/org/labkey/test/util/query/QueryApiHelper.java +++ b/src/org/labkey/test/util/query/QueryApiHelper.java @@ -8,6 +8,7 @@ import org.labkey.remoteapi.domain.DropDomainCommand; import org.labkey.remoteapi.domain.GetDomainDetailsCommand; import org.labkey.remoteapi.query.BaseRowsCommand; +import org.labkey.remoteapi.query.ContainerFilter; import org.labkey.remoteapi.query.DeleteRowsCommand; import org.labkey.remoteapi.query.Filter; import org.labkey.remoteapi.query.ImportDataCommand; @@ -71,21 +72,20 @@ public SelectRowsResponse selectRows(List columns, @Nullable List columns, @Nullable List filters, @Nullable List sorts) throws IOException, CommandException { - SelectRowsCommand cmd = new SelectRowsCommand(_schema, _query); + return selectRows(columns, filters, sorts, null); + } - if(filters != null) - { + public SelectRowsResponse selectRows(List columns, @Nullable List filters, @Nullable List sorts, @Nullable ContainerFilter cf) throws IOException, CommandException + { + SelectRowsCommand cmd = new SelectRowsCommand(_schema, _query); + if (filters != null) cmd.setFilters(new ArrayList<>(filters)); - } - - if(sorts != null) - { + if (sorts != null) cmd.setSorts(sorts); - } - if (columns!=null) cmd.setColumns(columns); - + if (cf != null) + cmd.setContainerFilter(cf); return cmd.execute(_connection, _containerPath); }