From fcc60fa2cbc306760be36f406a25e3cc0740d98b Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Thu, 12 Jun 2025 17:48:14 -0700 Subject: [PATCH 1/7] Add a test to cover sample type name containing a period and used as a required parent. Add a list of realistic domain names. Remove deprecated code. Add an isRequired parameter to EntityTypeDesigner --- .../domainproperties/EntityTypeDesigner.java | 12 ++ .../samples/SampleTypeDesigner.java | 7 +- .../tests/SampleTypeNameExpressionTest.java | 8 +- .../labkey/test/util/data/TestDataUtils.java | 111 +++++++++++++++++- .../test/util/exp/SampleTypeAPIHelper.java | 22 +--- 5 files changed, 137 insertions(+), 23 deletions(-) diff --git a/src/org/labkey/test/components/ui/domainproperties/EntityTypeDesigner.java b/src/org/labkey/test/components/ui/domainproperties/EntityTypeDesigner.java index c21451262b..1c7e0677a9 100644 --- a/src/org/labkey/test/components/ui/domainproperties/EntityTypeDesigner.java +++ b/src/org/labkey/test/components/ui/domainproperties/EntityTypeDesigner.java @@ -366,6 +366,11 @@ public List getParentAliasOptions(int index) } public T setParentAlias(int index, @Nullable String alias, @Nullable String optionDisplayText) + { + return setParentAlias(index, alias, optionDisplayText, false); + } + + public T setParentAlias(int index, @Nullable String alias, @Nullable String optionDisplayText, boolean isRequired) { expandPropertiesPanel(); elementCache().parentAlias(index).setValue(alias); @@ -373,6 +378,7 @@ public T setParentAlias(int index, @Nullable String alias, @Nullable String opti { elementCache().parentAliasSelect(index).select(optionDisplayText); } + getWrapper().setCheckbox(elementCache().parentAliasRequiredCheckbox(index), isRequired); return getThis(); } @@ -447,6 +453,12 @@ public Input parentAlias(int index) return parentAliases().get(index); } + public WebElement parentAliasRequiredCheckbox(int index) + { + return Locator.tagWithName("input","required").withAttribute("type", "checkbox") + .findElements(propertiesPanel).get(index); + } + public WebElement removeParentAliasIcon(int index) { return Locator.tagWithClass("span","domain-field-delete-icon").findElements(propertiesPanel).get(index); diff --git a/src/org/labkey/test/components/ui/domainproperties/samples/SampleTypeDesigner.java b/src/org/labkey/test/components/ui/domainproperties/samples/SampleTypeDesigner.java index 08cf7c1e5f..522a4726f5 100644 --- a/src/org/labkey/test/components/ui/domainproperties/samples/SampleTypeDesigner.java +++ b/src/org/labkey/test/components/ui/domainproperties/samples/SampleTypeDesigner.java @@ -42,6 +42,11 @@ public T addParentAlias(String alias) } public T addParentAlias(String alias, @Nullable String optionDisplayText) + { + return addParentAlias(alias, optionDisplayText, false); + } + + public T addParentAlias(String alias, @Nullable String optionDisplayText, boolean isRequired) { expandPropertiesPanel(); @@ -54,7 +59,7 @@ public T addParentAlias(String alias, @Nullable String optionDisplayText) { optionDisplayText = CURRENT_SAMPLE_TYPE; } - setParentAlias(initialCount, alias, optionDisplayText); + setParentAlias(initialCount, alias, optionDisplayText, isRequired); return getThis(); } diff --git a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java index bc83b90ada..811fb2b8d9 100644 --- a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java +++ b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java @@ -580,17 +580,14 @@ private void verifyNames(String sampleTypeName, String header, String nameExpres public void testDeriveSampleFromSampleDetailsPage() throws Exception { - // This test exposes Issue 44760. The issue is not caused by using the UI but rather by the latest lineage lookup - // name expression feature. goToProjectHome(); SampleTypeHelper sampleHelper = new SampleTypeHelper(this); final String sampleType = "DerivedUI_SampleType"; + // Covers Issue 44760 final String nameExpression = String.format("DUI_${genId}_${materialInputs/%s/Str}", PARENT_SAMPLE_TYPE_INPUT); - // TODO: When Issue 44760 this test can be updated to use a parent alias in the name expression. - log(String.format("Create a sample type named '%s' with a name expression of '%s'.", sampleType, nameExpression)); CreateSampleTypePage createPage = sampleHelper.goToCreateNewSampleType(); @@ -614,6 +611,7 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", PARENT_SAMPLE_01), isElementPresent(Locator.linkWithText(PARENT_SAMPLE_01))); + // Covers Issue 44760 final String ancestorNameExpression = String.format("GrandChild_${MaterialInputs/%s/..[MaterialInputs/%s]/Str}_${genId}", sampleType, PARENT_SAMPLE_TYPE_INPUT); log("Change the sample type name expression to support grandparent property lookup: " + ancestorNameExpression); goToProjectHome(); @@ -659,7 +657,7 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception private String deriveSample(String parentSampleName, String parentSampleType, String targetSampleType, String strVal, String intVal) throws IOException, CommandException { log(String.format("Go to the 'overview' page for sample '%s' in sample type '%s'", parentSampleName, parentSampleType)); - Integer sampleRowNum = SampleTypeAPIHelper.getSampleIdFromName(getProjectName(), parentSampleType, Arrays.asList(parentSampleName)).get(parentSampleName); + Integer sampleRowNum = SampleTypeAPIHelper.getRowIdsForSamples(getProjectName(), parentSampleType, Arrays.asList(parentSampleName)).get(parentSampleName); String url = WebTestHelper.buildRelativeUrl("experiment", getCurrentContainerPath(), "showMaterial", Map.of("rowId", sampleRowNum)); beginAt(url); diff --git a/src/org/labkey/test/util/data/TestDataUtils.java b/src/org/labkey/test/util/data/TestDataUtils.java index 30739cca0d..16f9390fa8 100644 --- a/src/org/labkey/test/util/data/TestDataUtils.java +++ b/src/org/labkey/test/util/data/TestDataUtils.java @@ -134,6 +134,96 @@ public class TestDataUtils "CIS43LS ABCD PK Pre-Qual Run 3" ); + public static final List REALISTIC_DOMAIN_NAMES = List.of( + "10 minute placenta", + "30 minute placenta", + "Adiponectin ELISA Data Fields", + "Aqueous", + "Ascitic fluid", + "Biopsy tissue", + "Buccal swabs", + "Buffy coat", + "DSX Study", + "Cell-free DNA (cfDNA) and circulating tumor DNA (ctDNA) from plasma", + "Cerebrospinal fluid (CSF)", + "cfDNA", + "Chemical compounds-Solid powders", + "Circulating tumor cells (CTCs)", + "circulating tumor DNA (ctDNA) from plasma", + "Cord blood heparin", + "cord.blood.heparin", + "Cord DNA", + "cord.DNA", + "CRC_IV st_MMR", + "CSF", + "CTC", + "ctDNA", + "Cultured Cell Lines", + "Cytokine ELISA Data Fields", + "D2G Oncology", + "2O18 Data Fields", + "NA", + "External Development", + "External Fixed Cells (Haematology)", + "FFPE - Blocks", + "FFPE", + "Formalin-fixed paraffin-embedded (FFPE) tissue", + "Fresh frozen tissue", + "Gastric_MMR", + "Gastrointestinal fluid (GI)", + "GI", + "In House Fixed Cells (Haematology)", + "Insulin ELISA Data Fields", + "Juul", + "Kindeva", + "Leptin ELISA Data Fields", + "LOY-001 PK Data Fields", + "Maternal DNA", + "Membrane", + "Microbiome", + "Molecular&other testing", + "Multiple Pathogen", + "mRNA", + "miRNA", + "NEFA Data Fields", + "Nonn Primers", + "Organoids", + "Paternal DNA", + "PAXGENE", + "PBMC", + "Peripheral blood mononuclear cells", + "Placenta DNA", + "Placenta RNA", + "Plasma", + "Platelet count", + "Primary Cells", + "Primary Cells from Tumor Tissue", + "Primes", + "PTSD", + "Recode Therapeutics", + "Research Development", + "Research Project", + "Retrospective archive_FFPE", + "RNA", + "Saliva swabs", + "Sample release", + "Samples created for V&V studies", + "Serum", + "Studies", + "Study", + "Surgical resection specimens", + "TFF pharmaceuticals", + "Trizol", + "Truvian Sciences", + "TSS", + "Urine", + "Virome", + "Vitreous", + "Water Stock", + "Whole Blood", + "Whole Globe" + ); + private TestDataUtils() { // Utility class. Do not instantiate. @@ -141,7 +231,26 @@ private TestDataUtils() public static String getRealisticPlateName() { - return REALISTIC_PLATE_NAMES.get(TestDataGenerator.randomInt(0, REALISTIC_PLATE_NAMES.size() - 1)); + return getRealisticPlateName(new ArrayList<>()); + } + + public static String getRealisticPlateName(List excludePlateNames) + { + List includeNames = new ArrayList<>(REALISTIC_PLATE_NAMES); + includeNames.removeAll(excludePlateNames); + return includeNames.get(TestDataGenerator.randomInt(0, includeNames.size() - 1)); + } + + public static String getRealisticDomainName() + { + return getRealisticDomainName(new ArrayList<>()); + } + + public static String getRealisticDomainName(List excludeDomains) + { + List includeNames = new ArrayList<>(REALISTIC_DOMAIN_NAMES); + includeNames.removeAll(excludeDomains); + return includeNames.get(TestDataGenerator.randomInt(0, includeNames.size() - 1)); } public static List> rowMapsFromTsv(File tsvFile) throws IOException diff --git a/src/org/labkey/test/util/exp/SampleTypeAPIHelper.java b/src/org/labkey/test/util/exp/SampleTypeAPIHelper.java index e367505d9d..a506f039a5 100644 --- a/src/org/labkey/test/util/exp/SampleTypeAPIHelper.java +++ b/src/org/labkey/test/util/exp/SampleTypeAPIHelper.java @@ -58,13 +58,13 @@ public static TestDataGenerator createEmptySampleType(String containerPath, Samp public static List sampleTypeTestFields(boolean withFileField) { List fields = new ArrayList<>(Arrays.asList( - new FieldDefinition("intColumn", FieldDefinition.ColumnType.Integer), - new FieldDefinition("decimalColumn", FieldDefinition.ColumnType.Decimal), - new FieldDefinition("stringColumn", FieldDefinition.ColumnType.String), - new FieldDefinition("sampleDate", FieldDefinition.ColumnType.DateAndTime), - new FieldDefinition("boolColumn", FieldDefinition.ColumnType.Boolean))); + new FieldDefinition(TestDataGenerator.randomFieldName("intColumn"), FieldDefinition.ColumnType.Integer), + new FieldDefinition(TestDataGenerator.randomFieldName("decimalColumn"), FieldDefinition.ColumnType.Decimal), + new FieldDefinition(TestDataGenerator.randomFieldName("stringColumn"), FieldDefinition.ColumnType.String), + new FieldDefinition(TestDataGenerator.randomFieldName("sampleDate"), FieldDefinition.ColumnType.DateAndTime), + new FieldDefinition(TestDataGenerator.randomFieldName("boolColumn"), FieldDefinition.ColumnType.Boolean))); if (withFileField) - fields.add(new FieldDefinition("fileColumn", FieldDefinition.ColumnType.File)); + fields.add(new FieldDefinition(TestDataGenerator.randomFieldName("fileColumn"), FieldDefinition.ColumnType.File)); return fields; } @@ -176,14 +176,4 @@ public static Map getRowIdsForSamples(String containerPath, Str return rowIds; } - /** - * This method has a misleading name. "Name" and "Sample ID" refer to the same column. This is actually fetching - * row IDs of the specified samples. - * @deprecated Use {@link #getRowIdsForSamples(String, String, List)} - */ - @Deprecated(since = "22.4") - public static Map getSampleIdFromName(String folder, String sampleTypeName, List sampleNames) throws IOException, CommandException - { - return getRowIdsForSamples(folder, sampleTypeName, sampleNames); - } } From 7bb118bcab38c97f583f2aa3e6fd75051f6fe72b Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Wed, 18 Jun 2025 14:20:42 -0700 Subject: [PATCH 2/7] Add a test that checks tricky characters in field names that are also used in the name expression. Remove check for 'Add' button being disabled if all parent entity types are added. It added little value and made the test fragile. --- .../domainproperties/EntityTypeDesigner.java | 9 +- .../tests/SampleTypeNameExpressionTest.java | 132 ++++++++++++++++-- 2 files changed, 126 insertions(+), 15 deletions(-) diff --git a/src/org/labkey/test/components/ui/domainproperties/EntityTypeDesigner.java b/src/org/labkey/test/components/ui/domainproperties/EntityTypeDesigner.java index 1c7e0677a9..0a8678bda1 100644 --- a/src/org/labkey/test/components/ui/domainproperties/EntityTypeDesigner.java +++ b/src/org/labkey/test/components/ui/domainproperties/EntityTypeDesigner.java @@ -378,7 +378,14 @@ public T setParentAlias(int index, @Nullable String alias, @Nullable String opti { elementCache().parentAliasSelect(index).select(optionDisplayText); } - getWrapper().setCheckbox(elementCache().parentAliasRequiredCheckbox(index), isRequired); + + // The "Required" checkbox is not presented outside of the apps. Only a test running in the app could set + // this parent field to being required. + if (isRequired) + { + getWrapper().setCheckbox(elementCache().parentAliasRequiredCheckbox(index), isRequired); + } + return getThis(); } diff --git a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java index 811fb2b8d9..a4d0e81d49 100644 --- a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java +++ b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java @@ -51,6 +51,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -81,6 +82,23 @@ public class SampleTypeNameExpressionTest extends BaseWebDriverTest private static final String PARENT_SAMPLE_06 = "parent,06"; private static final String PARENT_SAMPLE_07 = "\"parent07"; + private static final String PARENT_FIELD_TRICKY01 = "Str01 \\"; + private static final String PARENT_FIELD_TRICKY01_ESC = "Str01 \\\\"; + private static final String PARENT_FIELD_TRICKY02 = "Str02 $"; + private static final String PARENT_FIELD_TRICKY02_ESC = "Str02 \\$"; + private static final String PARENT_FIELD_TRICKY03 = "Str03 /"; + private static final String PARENT_FIELD_TRICKY03_ESC = "Str03 \\/"; + private static final String PARENT_FIELD_TRICKY04 = "Str04 ."; + private static final String PARENT_FIELD_TRICKY04_ESC = "Str04 \\."; + private static final String PARENT_FIELD_TRICKY05 = "Str05 &"; + private static final String PARENT_FIELD_TRICKY05_ESC = "Str05 \\&"; + private static final String PARENT_FIELD_TRICKY06 = "Str06 {}"; + private static final String PARENT_FIELD_TRICKY06_ESC = "Str06 {\\}"; + private static final String PARENT_FIELD_TRICKY07 = "Str07 ~"; + private static final String PARENT_FIELD_TRICKY07_ESC = "Str07 \\~"; + private static final String PARENT_FIELD_TRICKY08 = "Str08 ,"; + private static final String PARENT_FIELD_TRICKY08_ESC = "Str08 \\,"; + private static final File PARENT_EXCEL = TestFileUtils.getSampleData("samples/ParentSamples.xlsx"); protected final AuditLogHelper _auditLogHelper = new AuditLogHelper(this); @@ -107,11 +125,21 @@ public static void setupProject() throws IOException, CommandException private void addDataRow(TestDataGenerator dataGenerator, String name, int intVal) { - Map sampleData = Map.of( - "name", name, - "Int", intVal, - "Str", "Parent Sample " + ((char) (intVal + 95)), - "Date", intVal + "/14/2020"); + Map sampleData = new HashMap<>(); + + sampleData.put("name", name); + sampleData.put("Int", intVal); + sampleData.put("Str", "Parent Sample " + ((char) (intVal + 95))); + sampleData.put("Date", intVal + "/14/2020"); + sampleData.put(PARENT_FIELD_TRICKY01, intVal + " tricky01"); + sampleData.put(PARENT_FIELD_TRICKY02, intVal + " tricky02"); + sampleData.put(PARENT_FIELD_TRICKY03, intVal + " tricky03"); + sampleData.put(PARENT_FIELD_TRICKY04, intVal + " tricky04"); + sampleData.put(PARENT_FIELD_TRICKY05, intVal + " tricky05"); + sampleData.put(PARENT_FIELD_TRICKY06, intVal + " tricky06"); + sampleData.put(PARENT_FIELD_TRICKY07, intVal + " tricky07"); + sampleData.put(PARENT_FIELD_TRICKY08, intVal + " tricky08"); + dataGenerator.addCustomRow(sampleData); } @@ -128,12 +156,19 @@ private void doSetup() throws IOException, CommandException definition = definition.setFields(List.of( new FieldDefinition("Str", FieldDefinition.ColumnType.String), new FieldDefinition("Int", FieldDefinition.ColumnType.Integer), - new FieldDefinition("Date", FieldDefinition.ColumnType.DateAndTime))); + new FieldDefinition("Date", FieldDefinition.ColumnType.DateAndTime), + new FieldDefinition(PARENT_FIELD_TRICKY01, FieldDefinition.ColumnType.String), + new FieldDefinition(PARENT_FIELD_TRICKY02, FieldDefinition.ColumnType.String), + new FieldDefinition(PARENT_FIELD_TRICKY03, FieldDefinition.ColumnType.String), + new FieldDefinition(PARENT_FIELD_TRICKY04, FieldDefinition.ColumnType.String), + new FieldDefinition(PARENT_FIELD_TRICKY05, FieldDefinition.ColumnType.String), + new FieldDefinition(PARENT_FIELD_TRICKY06, FieldDefinition.ColumnType.String), + new FieldDefinition(PARENT_FIELD_TRICKY07, FieldDefinition.ColumnType.String), + new FieldDefinition(PARENT_FIELD_TRICKY08, FieldDefinition.ColumnType.String))); TestDataGenerator dataGenerator = SampleTypeAPIHelper.createEmptySampleType(getCurrentContainerPath(), definition); - log(String.format("Give the parent sample type '%1$s' three samples named '%2$s', '%3$s' and '%4$s'.", - PARENT_SAMPLE_TYPE, PARENT_SAMPLE_01, PARENT_SAMPLE_02, PARENT_SAMPLE_03)); + log("Add some 'parent' samples."); addDataRow(dataGenerator, PARENT_SAMPLE_01, 1); addDataRow(dataGenerator, PARENT_SAMPLE_02, 2); @@ -388,6 +423,67 @@ public void testWithTrickyCharacters() checker().screenShotIfNewError("SampleCreationError"); } + /** + *

+ * Automation to cover issue 52180. The name expression for the sample type will reference fields that contain + * "special" characters. + *

+ */ + @Test + public void testWithTrickyFieldNames() throws IOException, CommandException + { + goToProjectHome(); + + SampleTypeHelper sampleHelper = new SampleTypeHelper(this); + + final String sampleType = "Issue 52180 Sample Type"; + + StringBuilder sbNameExpression = new StringBuilder(); + + // Covers Issue 52180 + String parentsampleTypeEncoded = PARENT_SAMPLE_TYPE.replace("/", "\\/"); + sbNameExpression.append("Trick-Field ${genId} "); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_TRICKY01_ESC)); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_TRICKY02_ESC)); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_TRICKY03_ESC)); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_TRICKY04_ESC)); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_TRICKY05_ESC)); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_TRICKY06_ESC)); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_TRICKY07_ESC)); + sbNameExpression.append(String.format("${materialInputs/%s/%s}", parentsampleTypeEncoded, PARENT_FIELD_TRICKY08_ESC)); + + log(String.format("Create a sample type named '%s' with a name expression of '%s'.", sampleType, sbNameExpression)); + + CreateSampleTypePage createPage = sampleHelper.goToCreateNewSampleType(); + + createPage.setName(sampleType); + + createPage.setNameExpression(sbNameExpression.toString()); + + // Issue 53306 There is a problem with the derived sample form and field names that contain "special" characters. +// String intField = TestDataGenerator.randomFieldName("Int"); +// String strField = TestDataGenerator.randomFieldName("Str"); + String intField = "Int"; + String strField = "Str"; + createPage.addFields(Arrays.asList( + new FieldDefinition(intField, FieldDefinition.ColumnType.Integer), + new FieldDefinition(strField, FieldDefinition.ColumnType.String))); + + createPage.clickSave(); + + String flagString = "Hello, I'm a derived sample."; + String intVal = "678"; + String derivedSampleName = deriveSample(PARENT_SAMPLE_01, PARENT_SAMPLE_TYPE, sampleType, + Map.of(intField, intVal, + strField, flagString)); + + checker().verifyTrue("Name of derived sample doesn't look correct. Should start with 'Trick-Field '.", + derivedSampleName.startsWith("Trick-Field ")); + checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", PARENT_SAMPLE_01), + isElementPresent(Locator.linkWithText(PARENT_SAMPLE_01))); + + } + @Test public void testInputsExpression() { @@ -604,7 +700,9 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception String flagString = "Hello, I'm a derived sample."; String intVal = "987"; - String derivedSampleName = deriveSample(PARENT_SAMPLE_01, PARENT_SAMPLE_TYPE, sampleType, flagString, intVal); + String derivedSampleName = deriveSample(PARENT_SAMPLE_01, PARENT_SAMPLE_TYPE, sampleType, + Map.of("Str", flagString, + "Int", intVal)); checker().verifyTrue("Name of derived sample doesn't look correct. Should contain 'Parent Sample'.", derivedSampleName.contains("Parent Sample")); @@ -624,7 +722,9 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception String flagStringGD = "grand child sample."; String intValGD = "567"; - String grandChildSampleName = deriveSample(derivedSampleName, sampleType, sampleType, flagStringGD, intValGD); + String grandChildSampleName = deriveSample(derivedSampleName, sampleType, sampleType, + Map.of("Str", flagStringGD, + "Int", intValGD)); checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain 'Parent Sample' and not contain '%s'.", flagString), grandChildSampleName.contains("Parent Sample") && !grandChildSampleName.contains(flagString)); checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", derivedSampleName), @@ -654,7 +754,7 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception isElementPresent(Locator.linkWithText(derivedSampleName))); } - private String deriveSample(String parentSampleName, String parentSampleType, String targetSampleType, String strVal, String intVal) throws IOException, CommandException + private String deriveSample(String parentSampleName, String parentSampleType, String targetSampleType, Map setField) throws IOException, CommandException { log(String.format("Go to the 'overview' page for sample '%s' in sample type '%s'", parentSampleName, parentSampleType)); Integer sampleRowNum = SampleTypeAPIHelper.getRowIdsForSamples(getProjectName(), parentSampleType, Arrays.asList(parentSampleName)).get(parentSampleName); @@ -671,11 +771,15 @@ private String deriveSample(String parentSampleName, String parentSampleType, St selectOptionByText(Locator.name("targetSampleTypeId"), String.format("%s in /%s", targetSampleType, getProjectName())); clickButton("Next"); - setFormElement(Locator.name("outputSample1_Int"), intVal); - setFormElement(Locator.name("outputSample1_Str"), strVal); + String flagString = ""; + for(Map.Entry entry : setField.entrySet()) + { + setFormElement(Locator.name(String.format("outputSample1_%s", entry.getKey())), entry.getValue()); + flagString = entry.getValue(); + } clickButton("Submit"); - waitForElement(Locator.tagWithText("td", strVal)); + waitForElement(Locator.tagWithText("td", flagString)); return Locator.tagWithText("td", "Name:").followingSibling("td").findElement(getDriver()).getText(); } From 0da26689586de71ee11dad568250d1e7bfb9952e Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Tue, 1 Jul 2025 12:15:58 -0700 Subject: [PATCH 3/7] Avoid issue 53315 by having variations in the field names. --- .../tests/SampleTypeNameExpressionTest.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java index 7ed1e0778a..e15ee8e87e 100644 --- a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java +++ b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java @@ -72,7 +72,9 @@ public class SampleTypeNameExpressionTest extends BaseWebDriverTest { private static final String PROJECT_NAME = "SampleType_Name_Expression_Test"; - private static final String DEFAULT_SAMPLE_PARENT_VALUE = "SS" + TestDataGenerator.randomString(3).replaceAll("[_)]", "."); // '_' is used as delimiter to get batchRandomId and ) is used to close the defaultValue() + + private static final String DEFAULT_SAMPLE_PARENT_VALUE = "SS" + + EscapeUtil.escapeForNameExpression(TestDataGenerator.randomString(3).replaceAll("[_)]", ".")); // '_' is used as delimiter to get batchRandomId and ) is used to close the defaultValue() private static final String PARENT_SAMPLE_TYPE = "PS" + DOMAIN_TRICKY_CHARACTERS; private static final String PARENT_SAMPLE_TYPE_INPUT = escapeForNameExpression(PARENT_SAMPLE_TYPE); @@ -90,15 +92,17 @@ public class SampleTypeNameExpressionTest extends BaseWebDriverTest private static final String PARENT_SAMPLE_06 = "parent,06"; private static final String PARENT_SAMPLE_07 = "\"parent07"; - private static final FieldInfo PARENT_FIELD_BACKSLASH = new FieldInfo("Str \\", ColumnType.String); - private static final FieldInfo PARENT_FIELD_DOLLAR_DATE = new FieldInfo("Date $", ColumnType.DateAndTime); - private static final FieldInfo PARENT_FIELD_FORWARDSLASH = new FieldInfo("Str /", ColumnType.String); - private static final FieldInfo PARENT_FIELD_PERIOD = new FieldInfo("Str .", ColumnType.String); - private static final FieldInfo PARENT_FIELD_AMPERSAND = new FieldInfo("Str &", ColumnType.String); - private static final FieldInfo PARENT_FIELD_CURLY_LEFT = new FieldInfo("Str {", ColumnType.String); - private static final FieldInfo PARENT_FIELD_CURLY_RIGHT_INT = new FieldInfo("Int }", ColumnType.Integer); - private static final FieldInfo PARENT_FIELD_TILDE = new FieldInfo("Str ~", ColumnType.String); - private static final FieldInfo PARENT_FIELD_COMMA = new FieldInfo("Str ,", ColumnType.String); + // Create fields that have each of the tricky characters for name expressions. + // Having field names that differ only by special characters causes problems (Issue 53315) + private static final FieldInfo PARENT_FIELD_BACKSLASH = new FieldInfo("Str A \\", ColumnType.String); + private static final FieldInfo PARENT_FIELD_DOLLAR_DATE = new FieldInfo("Date B $", ColumnType.DateAndTime); + private static final FieldInfo PARENT_FIELD_FORWARDSLASH = new FieldInfo("Str C /", ColumnType.String); + private static final FieldInfo PARENT_FIELD_PERIOD = new FieldInfo("Str D .", ColumnType.String); + private static final FieldInfo PARENT_FIELD_AMPERSAND = new FieldInfo("Str E &", ColumnType.String); + private static final FieldInfo PARENT_FIELD_CURLY_LEFT = new FieldInfo("Str F {", ColumnType.String); + private static final FieldInfo PARENT_FIELD_CURLY_RIGHT_INT = new FieldInfo("Int G }", ColumnType.Integer); + private static final FieldInfo PARENT_FIELD_TILDE = new FieldInfo("Str H ~", ColumnType.String); + private static final FieldInfo PARENT_FIELD_COMMA = new FieldInfo("Str I ,", ColumnType.String); private static final File PARENT_EXCEL = TestFileUtils.getSampleData("samples/ParentSamples.xlsx"); From 796e52438186ca977caef60e7de8b33e49cb72ba Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Tue, 1 Jul 2025 14:01:32 -0700 Subject: [PATCH 4/7] Update the expected value from the field on the parent sample. --- .../test/tests/SampleTypeNameExpressionTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java index e15ee8e87e..845f543178 100644 --- a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java +++ b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java @@ -726,8 +726,8 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception Map.of(COL_DSTR.getName(), flagString, COL_DINT.getName(), intVal)); - checker().verifyTrue("Name of derived sample doesn't look correct. Should contain 'Parent Sample'.", - derivedSampleName.contains("Parent Sample")); + checker().verifyTrue("Name of derived sample doesn't look correct. Should contain 'tricky06'.", + derivedSampleName.contains("tricky06")); checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", PARENT_SAMPLE_01), isElementPresent(Locator.linkWithText(PARENT_SAMPLE_01))); @@ -747,8 +747,8 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception String grandChildSampleName = deriveSample(derivedSampleName, sampleType, sampleType, Map.of(COL_DSTR.getName(), flagStringGD, COL_DINT.getName(), intValGD)); - checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain 'Parent Sample' and not contain '%s'.", flagString), - grandChildSampleName.contains("Parent Sample") && !grandChildSampleName.contains(flagString)); + checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain 'tricky06' and not contain '%s'.", flagString), + grandChildSampleName.contains("tricky06") && !grandChildSampleName.contains(flagString)); checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", derivedSampleName), isElementPresent(Locator.linkWithText(derivedSampleName))); @@ -771,8 +771,8 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception click(Locator.tagWithText("td", grandImportChildSampleName)); waitForElement(Locator.tagWithText("td", flagStringBulkImport)); - checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain 'Parent Sample' and not contain '%s'.", flagString), - grandChildSampleName.contains("Parent Sample") && !grandChildSampleName.contains(flagString)); + checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain 'tricky06' and not contain '%s'.", flagString), + grandChildSampleName.contains("tricky06") && !grandChildSampleName.contains(flagString)); checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", derivedSampleName), isElementPresent(Locator.linkWithText(derivedSampleName))); } From e33da798cdf01241760fcb4d7faf6e1662a7c83d Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Wed, 2 Jul 2025 15:21:27 -0700 Subject: [PATCH 5/7] remove replaceAll --- src/org/labkey/test/tests/SampleTypeNameExpressionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java index 845f543178..b427a4f744 100644 --- a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java +++ b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java @@ -74,7 +74,7 @@ public class SampleTypeNameExpressionTest extends BaseWebDriverTest private static final String PROJECT_NAME = "SampleType_Name_Expression_Test"; private static final String DEFAULT_SAMPLE_PARENT_VALUE = "SS" + - EscapeUtil.escapeForNameExpression(TestDataGenerator.randomString(3).replaceAll("[_)]", ".")); // '_' is used as delimiter to get batchRandomId and ) is used to close the defaultValue() + EscapeUtil.escapeForNameExpression(TestDataGenerator.randomString(3)); private static final String PARENT_SAMPLE_TYPE = "PS" + DOMAIN_TRICKY_CHARACTERS; private static final String PARENT_SAMPLE_TYPE_INPUT = escapeForNameExpression(PARENT_SAMPLE_TYPE); From c5dd51740de7fd3e1a3c637798c1480d3c104593 Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Wed, 2 Jul 2025 16:05:59 -0700 Subject: [PATCH 6/7] Implementing code review feedback. --- .../tests/SampleTypeNameExpressionTest.java | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java index b427a4f744..5f6eb4ce17 100644 --- a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java +++ b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java @@ -91,6 +91,7 @@ public class SampleTypeNameExpressionTest extends BaseWebDriverTest private static final String PARENT_SAMPLE_05 = "\"parent05"; private static final String PARENT_SAMPLE_06 = "parent,06"; private static final String PARENT_SAMPLE_07 = "\"parent07"; + private static final String PARENT_FIELD_VALUE_PREFIX = "Tricky Parent"; // Create fields that have each of the tricky characters for name expressions. // Having field names that differ only by special characters causes problems (Issue 53315) @@ -134,15 +135,22 @@ private void addDataRow(TestDataGenerator dataGenerator, String name, int intVal // Cannot use Map.of there are too many fields. sampleData.put("name", name); - sampleData.put(PARENT_FIELD_BACKSLASH.getName(), intVal + " tricky01"); + sampleData.put(PARENT_FIELD_BACKSLASH.getName(), + String.format("%d %s 01", intVal, PARENT_FIELD_VALUE_PREFIX)); sampleData.put(PARENT_FIELD_DOLLAR_DATE.getName(), intVal + "/14/2020"); - sampleData.put(PARENT_FIELD_FORWARDSLASH.getName(), intVal + " tricky03"); - sampleData.put(PARENT_FIELD_PERIOD.getName(), intVal + " tricky04"); - sampleData.put(PARENT_FIELD_AMPERSAND.getName(), intVal + " tricky05"); - sampleData.put(PARENT_FIELD_CURLY_LEFT.getName(), intVal + " tricky06"); + sampleData.put(PARENT_FIELD_FORWARDSLASH.getName(), + String.format("%d %s 03", intVal, PARENT_FIELD_VALUE_PREFIX)); + sampleData.put(PARENT_FIELD_PERIOD.getName(), + String.format("%d %s 04", intVal, PARENT_FIELD_VALUE_PREFIX)); + sampleData.put(PARENT_FIELD_AMPERSAND.getName(), + String.format("%d %s 05", intVal, PARENT_FIELD_VALUE_PREFIX)); + sampleData.put(PARENT_FIELD_CURLY_LEFT.getName(), + String.format("%d %s 06", intVal, PARENT_FIELD_VALUE_PREFIX)); sampleData.put(PARENT_FIELD_CURLY_RIGHT_INT.getName(), intVal); - sampleData.put(PARENT_FIELD_TILDE.getName(), intVal + " tricky08"); - sampleData.put(PARENT_FIELD_COMMA.getName(), intVal + " tricky09"); + sampleData.put(PARENT_FIELD_TILDE.getName(), + String.format("%d %s 08", intVal, PARENT_FIELD_VALUE_PREFIX)); + sampleData.put(PARENT_FIELD_COMMA.getName(), + String.format("%d %s 09", intVal, PARENT_FIELD_VALUE_PREFIX)); dataGenerator.addCustomRow(sampleData); } @@ -462,17 +470,16 @@ public void testWithTrickyFieldNames() throws IOException, CommandException StringBuilder sbNameExpression = new StringBuilder(); // Covers Issue 52180 - String parentsampleTypeEncoded = PARENT_SAMPLE_TYPE.replace("/", "\\/"); sbNameExpression.append("Trick-Field ${genId} "); - sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_BACKSLASH.getExpName())); - sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_DOLLAR_DATE.getExpName())); - sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_FORWARDSLASH.getExpName())); - sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_PERIOD.getExpName())); - sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_AMPERSAND.getExpName())); - sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_CURLY_LEFT.getExpName())); - sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_CURLY_RIGHT_INT.getExpName())); - sbNameExpression.append(String.format("${materialInputs/%s/%s} ", parentsampleTypeEncoded, PARENT_FIELD_TILDE.getExpName())); - sbNameExpression.append(String.format("${materialInputs/%s/%s}", parentsampleTypeEncoded, PARENT_FIELD_COMMA.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_BACKSLASH.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_DOLLAR_DATE.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_FORWARDSLASH.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_PERIOD.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_AMPERSAND.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_CURLY_LEFT.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_CURLY_RIGHT_INT.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s} ", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_TILDE.getExpName())); + sbNameExpression.append(String.format("${materialInputs/%s/%s}", PARENT_SAMPLE_TYPE_INPUT, PARENT_FIELD_COMMA.getExpName())); log(String.format("Create a sample type named '%s' with a name expression of '%s'.", sampleType, sbNameExpression)); @@ -726,8 +733,8 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception Map.of(COL_DSTR.getName(), flagString, COL_DINT.getName(), intVal)); - checker().verifyTrue("Name of derived sample doesn't look correct. Should contain 'tricky06'.", - derivedSampleName.contains("tricky06")); + checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain %s.", PARENT_FIELD_VALUE_PREFIX), + derivedSampleName.contains(PARENT_FIELD_VALUE_PREFIX)); checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", PARENT_SAMPLE_01), isElementPresent(Locator.linkWithText(PARENT_SAMPLE_01))); @@ -747,8 +754,8 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception String grandChildSampleName = deriveSample(derivedSampleName, sampleType, sampleType, Map.of(COL_DSTR.getName(), flagStringGD, COL_DINT.getName(), intValGD)); - checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain 'tricky06' and not contain '%s'.", flagString), - grandChildSampleName.contains("tricky06") && !grandChildSampleName.contains(flagString)); + checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain %s and not contain '%s'.", PARENT_FIELD_VALUE_PREFIX, flagString), + grandChildSampleName.contains(PARENT_FIELD_VALUE_PREFIX) && !grandChildSampleName.contains(flagString)); checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", derivedSampleName), isElementPresent(Locator.linkWithText(derivedSampleName))); @@ -771,8 +778,8 @@ public void testDeriveSampleFromSampleDetailsPage() throws Exception click(Locator.tagWithText("td", grandImportChildSampleName)); waitForElement(Locator.tagWithText("td", flagStringBulkImport)); - checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain 'tricky06' and not contain '%s'.", flagString), - grandChildSampleName.contains("tricky06") && !grandChildSampleName.contains(flagString)); + checker().verifyTrue(String.format("Name of derived sample doesn't look correct. Should contain %s and not contain '%s'.", PARENT_FIELD_VALUE_PREFIX, flagString), + grandChildSampleName.contains(PARENT_FIELD_VALUE_PREFIX) && !grandChildSampleName.contains(flagString)); checker().verifyTrue(String.format("Doesn't look like there is a link to the parent sample '%s'.", derivedSampleName), isElementPresent(Locator.linkWithText(derivedSampleName))); } From 6208ba8a770b69d0a4cac5e3765b92fefcfd1f22 Mon Sep 17 00:00:00 2001 From: labkey-danield Date: Mon, 7 Jul 2025 16:17:16 -0700 Subject: [PATCH 7/7] Implementing code review feedback. --- src/org/labkey/test/BaseWebDriverTest.java | 2 ++ src/org/labkey/test/params/FieldDefinition.java | 4 ++-- src/org/labkey/test/util/data/TestDataUtils.java | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/BaseWebDriverTest.java b/src/org/labkey/test/BaseWebDriverTest.java index 61a5167cb3..96bc04b333 100644 --- a/src/org/labkey/test/BaseWebDriverTest.java +++ b/src/org/labkey/test/BaseWebDriverTest.java @@ -64,6 +64,7 @@ import org.labkey.test.pages.query.NewQueryPage; import org.labkey.test.pages.query.SourceQueryPage; import org.labkey.test.pages.search.SearchResultsPage; +import org.labkey.test.params.FieldDefinition; import org.labkey.test.params.FieldKey; import org.labkey.test.teamcity.TeamCityUtils; import org.labkey.test.util.APIAssayHelper; @@ -224,6 +225,7 @@ public abstract class BaseWebDriverTest extends LabKeySiteWrapper implements Cle public static final String TRICKY_CHARACTERS = "><&/%\\' \"1\u00E4\u00F6\u00FC\u00C5"; public static final String TRICKY_CHARACTERS_NO_QUOTES = "><&$,/%\\'}{][ \";:" + SNOWMAN + ANGSTROM + A_UMLAUT; public static final String DOMAIN_TRICKY_CHARACTERS = DOMAIN_SPECIAL_STRING + SNOWMAN + ANGSTROM + A_UMLAUT; diff --git a/src/org/labkey/test/util/data/TestDataUtils.java b/src/org/labkey/test/util/data/TestDataUtils.java index b284b1d2bd..e4c7fbc4b8 100644 --- a/src/org/labkey/test/util/data/TestDataUtils.java +++ b/src/org/labkey/test/util/data/TestDataUtils.java @@ -134,7 +134,6 @@ public class TestDataUtils "Miniprep Quant BL 18JAN2023", "CIS43LS ABCD PK Pre-Qual Run 3" ); - public static final List REALISTIC_DOMAIN_NAMES = List.of( "10 minute placenta", "30 minute placenta",