diff --git a/src/org/labkey/test/TestFileUtils.java b/src/org/labkey/test/TestFileUtils.java index ca119a4c68..1848c71a7b 100644 --- a/src/org/labkey/test/TestFileUtils.java +++ b/src/org/labkey/test/TestFileUtils.java @@ -64,8 +64,6 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.security.Security; import java.util.ArrayList; import java.util.Arrays; @@ -122,21 +120,6 @@ public static String getFileContents(Path path) } } - /** - * Compute MD5 hash for the given file. Useful checking file equivalence. - */ - public static String getMD5Hash(Path path) - { - try - { - return new String(MessageDigest.getInstance("MD5").digest(Files.readAllBytes(path)), StandardCharsets.UTF_8); - } - catch (IOException | NoSuchAlgorithmException fail) - { - throw new RuntimeException(fail); - } - } - public static String getStreamContentsAsString(InputStream is) throws IOException { return StringUtils.join(IOUtils.readLines(is, Charset.defaultCharset()).toArray(), System.lineSeparator()); @@ -372,10 +355,46 @@ public static File getTestTempDir() return new File(buildDir, "testTemp"); } - public static File ensureTestTempDir() throws IOException + /** + * Creates a directory under the 'testTemp' directory: 'build/testTemp/[children]' + * @param children will be appended to the testTemp path + * @return A file pointer to the specified directory. The directory will exist + * @throws IOException if the directories were not created + */ + public static File ensureTestTempDir(String... children) throws IOException { File file = getTestTempDir(); + for (String child : children) + { + file = new File(file, child); + } + FileUtils.forceMkdir(file); + + return file; + } + + /** + * Creates a directory under the 'testTemp' directory to contain the specified file. 'build/testTemp[/children]/lastChild' + * @param children will be appended to the testTemp path to construct the desired file's path + * @return A file pointer to the specified file. The file's parents will exist but the file might not + * @throws IOException if the parent directories were not created + */ + public static File ensureTestTempFile(String... children) throws IOException + { + File file = getTestTempDir(); + + for (String child : children) + { + file = new File(file, child); + } + + if (file.toString().length() == getTestTempDir().toString().length()) + { + throw new IllegalArgumentException("No valid children were provided: " + Arrays.toString(children)); + } + FileUtils.forceMkdirParent(file); + return file; } diff --git a/src/org/labkey/test/components/core/login/SetPasswordForm.java b/src/org/labkey/test/components/core/login/SetPasswordForm.java index 4971592a40..a4150c15f4 100644 --- a/src/org/labkey/test/components/core/login/SetPasswordForm.java +++ b/src/org/labkey/test/components/core/login/SetPasswordForm.java @@ -113,7 +113,9 @@ public void assertPasswordGuidance(@LoggedParam String password, @LoggedParam St private void assertGuidanceMessage(String expectedGuidance) { - Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> + // Though it's usually quite fast, sometimes the password scoring API takes ~4 sec on TeamCity, so give it + // enough time to refresh + Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> assertEquals("Strength guidance for password", expectedGuidance, elementCache().strengthGuidance.getText())); } diff --git a/src/org/labkey/test/params/FieldDefinition.java b/src/org/labkey/test/params/FieldDefinition.java index 1ce8037c67..568e903161 100644 --- a/src/org/labkey/test/params/FieldDefinition.java +++ b/src/org/labkey/test/params/FieldDefinition.java @@ -58,6 +58,8 @@ public class FieldDefinition extends PropertyDescriptor // Collection of JSON properties not explicitly known by 'PropertyDescriptor' private final Map _extraFieldProperties = new HashMap<>(); + private String _namePart; + /** * Define a non-lookup field of the specified type * @param name field name @@ -467,6 +469,16 @@ public void setAliquotOption(ExpSchema.DerivationDataScopeType aliquotOption) _aliquotOption = aliquotOption; } + public void setNamePart(String namePart) + { + _namePart = namePart; + } + + public boolean isNamePartMatch(String namePart) + { + return _namePart != null && _namePart.equals(namePart); + } + public enum RangeType { Equals("Equals", Filter.Operator.EQUAL), diff --git a/src/org/labkey/test/params/FieldInfo.java b/src/org/labkey/test/params/FieldInfo.java index 7e5ce8b71f..f962aa8241 100644 --- a/src/org/labkey/test/params/FieldInfo.java +++ b/src/org/labkey/test/params/FieldInfo.java @@ -20,6 +20,7 @@ public class FieldInfo implements CharSequence, WrapsFieldKey private final String _label; private final ColumnType _columnType; private final Consumer _fieldDefinitionMutator; + private String _namePart; // used for random field generation to track the name part used private FieldInfo(FieldKey fieldKey, String label, ColumnType columnType, Consumer fieldDefinitionMutator) { @@ -54,7 +55,9 @@ public FieldInfo(String name) */ public static FieldInfo random(String namePart, ColumnType columnType) { - return new FieldInfo(TestDataGenerator.randomFieldName(namePart), columnType); + FieldInfo field = new FieldInfo(TestDataGenerator.randomFieldName(namePart), columnType); + field.setNamePart(namePart); + return field; } /** @@ -165,9 +168,18 @@ private FieldDefinition getFieldDefinition(ColumnType columnType) { _fieldDefinitionMutator.accept(fieldDefinition); } + if (_namePart != null) + { + fieldDefinition.setNamePart(_namePart); + } return fieldDefinition; } + private void setNamePart(String namePart) + { + _namePart = namePart; + } + @Override public int length() { diff --git a/src/org/labkey/test/tests/AbstractKnitrReportTest.java b/src/org/labkey/test/tests/AbstractKnitrReportTest.java index 562b775d94..5236ed6dce 100644 --- a/src/org/labkey/test/tests/AbstractKnitrReportTest.java +++ b/src/org/labkey/test/tests/AbstractKnitrReportTest.java @@ -245,7 +245,6 @@ protected void moduleReportDependencies() public void testEmbeddedReportNonce() { CspConfigHelper.debugCspWarnings(); - new CspConfigHelper(this).setEnforceCsp(false); String name = "rhtml nonce check"; Locator[] reportContains = {nonceCheckSuccessLoc}; diff --git a/src/org/labkey/test/tests/AttachmentFieldTest.java b/src/org/labkey/test/tests/AttachmentFieldTest.java index 6570daf0f7..48e14a7046 100644 --- a/src/org/labkey/test/tests/AttachmentFieldTest.java +++ b/src/org/labkey/test/tests/AttachmentFieldTest.java @@ -19,6 +19,7 @@ import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.SampleTypeHelper; +import org.labkey.test.util.TestDataGenerator; import java.io.File; import java.util.List; @@ -96,8 +97,8 @@ public void testFileFieldInSampleType() @Test public void testAttachmentFieldInLists() { - String listName = "List with attachment field"; - String fieldName = "testFile"; + String listName = TestDataGenerator.randomDomainName("List with attachment field"); + String fieldName = TestDataGenerator.randomFieldName("Test File"); goToProjectHome(); log("Creating the list"); _listHelper.createList(getProjectName(), listName, "id"); diff --git a/src/org/labkey/test/tests/FileAttachmentColumnTest.java b/src/org/labkey/test/tests/FileAttachmentColumnTest.java index 482eb65087..ca935d5f8c 100644 --- a/src/org/labkey/test/tests/FileAttachmentColumnTest.java +++ b/src/org/labkey/test/tests/FileAttachmentColumnTest.java @@ -36,6 +36,7 @@ import org.labkey.test.pages.study.CreateStudyPage; import org.labkey.test.params.FieldDefinition; import org.labkey.test.params.FieldDefinition.ColumnType; +import org.labkey.test.params.FieldInfo; import org.labkey.test.params.assay.GeneralAssayDesign; import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.util.DataRegionTable; @@ -43,6 +44,7 @@ import org.labkey.test.util.PortalHelper; import org.labkey.test.util.SampleTypeHelper; import org.labkey.test.util.StudyHelper; +import org.labkey.test.util.TestDataGenerator; import org.labkey.test.util.core.webdav.WebDavUploadHelper; import org.labkey.test.util.exp.SampleTypeAPIHelper; import org.openqa.selenium.By; @@ -91,6 +93,7 @@ public class FileAttachmentColumnTest extends BaseWebDriverTest private final String RESULT_FILE_COL = "resultFile"; private final String OTHER_RESULT_FILE_COL = "otherResultFile"; private final String STUDY_DATASET_NAME = "ogreSpiteLevels"; + private static final FieldInfo LIST_ATTACHMENT_FIELD = new FieldInfo(TestDataGenerator.randomFieldName("File / Attachment"), ColumnType.Attachment); @Override protected void doCleanup(boolean afterTest) throws TestTimeoutException @@ -181,8 +184,8 @@ public void verifyFileDownloadOnClick() for (File testFile : downloadTestFiles) { int rowIndex = testListRegion.getRowIndex("Name", testFile.getName()); - var downloadLink = testListRegion.link(rowIndex, "File"); - doAndWaitForDownload(()-> downloadLink.click()); + var downloadLink = testListRegion.link(rowIndex, LIST_ATTACHMENT_FIELD.getName()); + doAndWaitForDownload(downloadLink::click); } // verify popup/sprite for jpeg @@ -429,13 +432,13 @@ private void createListWithData(String containerPath) String LIST_KEY = "TestListId"; listHelper.createList(getProjectName() + "/" + EXPORT_FOLDER_NAME, LIST_NAME, LIST_KEY, new FieldDefinition("Name", ColumnType.String), - new FieldDefinition("File", ColumnType.Attachment)); + LIST_ATTACHMENT_FIELD.getFieldDefinition()); goToManageLists(); listHelper.click(Locator.linkContainingText(LIST_NAME)); for (File file : SAMPLE_FILES) { - Map fileRow = Map.of("Name", file.getName(), "File", file.getAbsolutePath()); + Map fileRow = Map.of("Name", file.getName(), LIST_ATTACHMENT_FIELD.getName(), file.getAbsolutePath()); listHelper.insertNewRow(fileRow, false); } } @@ -449,7 +452,7 @@ private void importSampleDataUI(String sampleTypeName, String containerPath, Lis for (File file : files) { sampleFileData.add(Map.of("Name", file.getName(), "Color", "green", - "File", file.getName())); + "file", file.getName())); } helper.bulkImport(sampleFileData); } @@ -514,7 +517,7 @@ private void validateListData(String listName, String folderPath, List exp else { int rowIndex = testListRegion.getRowIndex("Name", testFile.getName()); - var downloadLink = testListRegion.link(rowIndex, "File"); + var downloadLink = testListRegion.link(rowIndex, LIST_ATTACHMENT_FIELD.getName()); doAndWaitForDownload(() -> downloadLink.click()); } } @@ -551,9 +554,9 @@ private void validateSampleData(String sampleType, String folderPath, List { // verify fie download behavior File downloadedFile = doAndWaitForDownload(() -> optionalFileLink.get().click()); - checker().wrapAssertion(() -> Assertions.assertThat(TestFileUtils.getMD5Hash(downloadedFile.toPath())) + checker().wrapAssertion(() -> Assertions.assertThat(downloadedFile) .as("expect the downloaded file to be the expected file") - .isEqualTo(TestFileUtils.getMD5Hash(file.toPath()))); // guard against renames like file2.xyz + .hasSameBinaryContentAs(file)); // guard against renames like file2.xyz } } } @@ -577,9 +580,9 @@ private void validateAssayRun(String assayName, String folderPath, String runNam if (optionalFileLink.isPresent()) { var file = doAndWaitForDownload(()-> optionalFileLink.get().click()); - checker().wrapAssertion(()-> Assertions.assertThat(TestFileUtils.getMD5Hash(file.toPath())) + checker().wrapAssertion(()-> Assertions.assertThat(file) .as("expect the downloaded file to have equivalent content") - .isEqualTo(TestFileUtils.getMD5Hash(runFile.toPath()))); + .hasSameBinaryContentAs(runFile)); } var resultsPage = runsPage.clickAssayIdLink(runName); @@ -645,9 +648,9 @@ private void validateDatasetData(String datasetName, String folderPath, List optionalFileLink.get().click()); - checker().wrapAssertion(() -> Assertions.assertThat(TestFileUtils.getMD5Hash(downloadedFile.toPath())) + checker().wrapAssertion(() -> Assertions.assertThat(downloadedFile) .as("expect the downloaded file to be the expected file") - .isEqualTo(TestFileUtils.getMD5Hash(file.toPath()))); // guard against renames like file2.xyz + .hasSameBinaryContentAs(file)); // guard against renames like file2.xyz } } } diff --git a/src/org/labkey/test/tests/InlineImagesListTest.java b/src/org/labkey/test/tests/InlineImagesListTest.java index 3a95c0c4b7..05c82e15d2 100644 --- a/src/org/labkey/test/tests/InlineImagesListTest.java +++ b/src/org/labkey/test/tests/InlineImagesListTest.java @@ -34,6 +34,7 @@ import org.labkey.test.util.DataRegionExportHelper; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.ExcelHelper; +import org.labkey.test.util.TestDataGenerator; import org.openqa.selenium.By; import org.openqa.selenium.support.ui.ExpectedConditions; @@ -51,16 +52,16 @@ @BaseWebDriverTest.ClassTimeout(minutes = 5) public class InlineImagesListTest extends BaseWebDriverTest { - protected final static String LIST_NAME = "InlineImagesList"; - protected final static String LIST_KEY_NAME = "Key"; + protected final static String LIST_NAME = TestDataGenerator.randomDomainName("InlineImagesList"); + protected final static String LIST_KEY_NAME = TestDataGenerator.randomFieldName("Key"); protected final static ColumnType LIST_KEY_TYPE = ColumnType.Integer; - protected final static String LIST_ATTACHMENT01_NAME = "Attachment01"; - protected final static String LIST_ATTACHMENT01_LABEL = "Attachment Column 01"; + protected final static String LIST_ATTACHMENT01_NAME = TestDataGenerator.randomFieldName("Attachment01"); + protected final static String LIST_ATTACHMENT01_LABEL = TestDataGenerator.randomFieldName("Attachment Column 01"); protected final static String LIST_ATTACHMENT01_DESC = "An 1st attachment column."; - protected final static String LIST_ATTACHMENT02_NAME = "Attachment02"; - protected final static String LIST_ATTACHMENT02_LABEL = "Attachment Column 02"; + protected final static String LIST_ATTACHMENT02_NAME = TestDataGenerator.randomFieldName("Attachment02"); + protected final static String LIST_ATTACHMENT02_LABEL = TestDataGenerator.randomFieldName("Attachment Column 02"); protected final static String LIST_ATTACHMENT02_DESC = "An 2nd attachment column."; protected final static ColumnType LIST_ATTACHMENT_TYPE = ColumnType.Attachment; @@ -132,7 +133,7 @@ private void doInit() } @Test - public final void ListTest() throws Exception + public final void testList() throws Exception { DataRegionTable list; DataRegionExportHelper exportHelper; diff --git a/src/org/labkey/test/tests/SampleTypeFolderExportImportTest.java b/src/org/labkey/test/tests/SampleTypeFolderExportImportTest.java index da65a2a33a..ae784c8077 100644 --- a/src/org/labkey/test/tests/SampleTypeFolderExportImportTest.java +++ b/src/org/labkey/test/tests/SampleTypeFolderExportImportTest.java @@ -45,6 +45,7 @@ import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.util.ArtifactCollector; import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.EscapeUtil; import org.labkey.test.util.LogMethod; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.SampleTypeHelper; @@ -436,6 +437,9 @@ public void testExportImportDerivedSamples() throws Exception // arrange - 2 sample types, one with samples derived from parents in the other (and also parents in the same one) List testFields = SampleTypeAPIHelper.sampleTypeTestFields(false); + FieldDefinition intColumn = getFieldByNamePart(testFields, "int,./Column"); + FieldDefinition stringColumn = getFieldByNamePart(testFields, "stringColumn"); + FieldDefinition decimalColumn = getFieldByNamePart(testFields, "decimalColumn"); DataClassDefinition dataClassType = new DataClassDefinition(dataClass).setFields(DataClassAPIHelper.dataClassTestFields()); SampleTypeDefinition parentType = new SampleTypeDefinition(parentSampleType).setFields(testFields); SampleTypeDefinition testSampleType = new SampleTypeDefinition(testSamples).setFields(testFields) @@ -450,25 +454,25 @@ public void testExportImportDerivedSamples() throws Exception dataClassDgen.insertRows(); TestDataGenerator parentDgen = SampleTypeAPIHelper.createEmptySampleType(subfolderPath, parentType); - parentDgen.addCustomRow(Map.of("Name", "Parent1", "intColumn", 1, "floatColumn", 1.1, "stringColumn", "one")); - parentDgen.addCustomRow(Map.of("Name", "Parent2", "intColumn", 2, "floatColumn", 2.2, "stringColumn", "two")); - parentDgen.addCustomRow(Map.of("Name", "Parent3", "intColumn", 3, "floatColumn", 3.3, "stringColumn", "three")); + parentDgen.addCustomRow(Map.of("Name", "Parent1", intColumn.getName(), 1, decimalColumn.getName(), 1.1, stringColumn.getName(), "one")); + parentDgen.addCustomRow(Map.of("Name", "Parent2", intColumn.getName(), 2, decimalColumn.getName(), 2.2, stringColumn.getName(), "two")); + parentDgen.addCustomRow(Map.of("Name", "Parent3", intColumn.getName(), 3, decimalColumn.getName(), 3.3, stringColumn.getName(), "three")); parentDgen.insertRows(); TestDataGenerator testDgen = SampleTypeAPIHelper.createEmptySampleType(subfolderPath, testSampleType); - testDgen.addCustomRow(Map.of("Name", "Child1", "intColumn", 1, "decimalColumn", 1.1, "stringColumn", "one", + testDgen.addCustomRow(Map.of("Name", "Child1", intColumn.getName(), 1, decimalColumn.getName(), 1.1, stringColumn.getName(), "one", "Parent", "Parent1")); - testDgen.addCustomRow(Map.of("Name", "Child2", "intColumn", 2, "decimalColumn", 2.2, "stringColumn", "two", + testDgen.addCustomRow(Map.of("Name", "Child2", intColumn.getName(), 2, decimalColumn.getName(), 2.2, stringColumn.getName(), "two", "Parent", "Parent2")); - testDgen.addCustomRow(Map.of("Name", "Child3", "intColumn", 3, "decimalColumn", 3.3, "stringColumn", "three", + testDgen.addCustomRow(Map.of("Name", "Child3", intColumn.getName(), 3, decimalColumn.getName(), 3.3, stringColumn.getName(), "three", "Parent", "Parent3", "DataClassParent", "data1")); - testDgen.addCustomRow(Map.of("Name", "Child4", "intColumn", 4, "decimalColumn", 4.4, "stringColumn", "four", + testDgen.addCustomRow(Map.of("Name", "Child4", intColumn.getName(), 4, decimalColumn.getName(), 4.4, stringColumn.getName(), "four", "Parent", "Parent3, Parent2")); - testDgen.addCustomRow(Map.of("Name", "Child5", "intColumn", 5, "decimalColumn", 5.5, "stringColumn", "five", + testDgen.addCustomRow(Map.of("Name", "Child5", intColumn.getName(), 5, decimalColumn.getName(), 5.5, stringColumn.getName(), "five", "Parent", "Parent1, Parent2")); - testDgen.addCustomRow(Map.of("Name", "Child6", "intColumn", 6, "decimalColumn", 6.6, "stringColumn", "six", + testDgen.addCustomRow(Map.of("Name", "Child6", intColumn.getName(), 6, decimalColumn.getName(), 6.6, stringColumn.getName(), "six", "Parent", "Parent3, Parent2", "SelfParent", "Child5")); - testDgen.addCustomRow(Map.of("Name", "Child7", "intColumn", 7, "decimalColumn", 7.7, "stringColumn", "seven", + testDgen.addCustomRow(Map.of("Name", "Child7", intColumn.getName(), 7, decimalColumn.getName(), 7.7, stringColumn.getName(), "seven", "Parent", "Parent3, Parent2", "SelfParent", "Child5", "DataClassParent", "data2, data3")); testDgen.insertRows(); @@ -524,12 +528,18 @@ public void testExportImportDerivedSamples() throws Exception .findFirst().orElse(null); assertNotNull("expect all matching rows to come through", matchingMap); + String intColFieldKey = EscapeUtil.fieldKeyEncodePart(intColumn.getName()); + assertNotNull("expect intColumn to be present in exported row", exportedRow.get(intColFieldKey)); assertThat("expect export and import values to be equivalent", - exportedRow.get("intColumn"), equalTo(matchingMap.get("intColumn"))); + exportedRow.get(intColFieldKey), equalTo(matchingMap.get(intColFieldKey))); + String stringColFieldKey = EscapeUtil.fieldKeyEncodePart(stringColumn.getName()); + assertNotNull("expect stringColumn to be present in exported row", exportedRow.get(stringColFieldKey)); assertThat("expect export and import values to be equivalent", - exportedRow.get("stringColumn"), equalTo(matchingMap.get("stringColumn"))); + exportedRow.get(stringColFieldKey), equalTo(matchingMap.get(stringColFieldKey))); + String decimalColFieldKey = EscapeUtil.fieldKeyEncodePart(decimalColumn.getName()); + assertNotNull("expect decimalColumn to be present in exported row", exportedRow.get(decimalColFieldKey)); assertThat("expect export and import values to be equivalent", - exportedRow.get("decimalColumn"), equalTo(matchingMap.get("decimalColumn"))); + exportedRow.get(decimalColFieldKey), equalTo(matchingMap.get(decimalColFieldKey))); List sourceParents = Arrays.asList(exportedRow.get("Inputs/Materials/parentSamples").toString() .replace(" ", "").split(",")); @@ -557,13 +567,16 @@ public void testExportImportSampleTypesWithAssayRuns() throws Exception // create a test sampleType List testFields = SampleTypeAPIHelper.sampleTypeTestFields(true); + FieldDefinition intColumn = getFieldByNamePart(testFields, "int,./Column"); + FieldDefinition stringColumn = getFieldByNamePart(testFields, "stringColumn"); + FieldDefinition decimalColumn = getFieldByNamePart(testFields, "decimalColumn"); SampleTypeDefinition testSampleType = new SampleTypeDefinition(testSamples).setFields(testFields) .addParentAlias("SelfParent"); // to derive from samples in the current type TestDataGenerator parentDgen = SampleTypeAPIHelper.createEmptySampleType(subfolderPath, testSampleType); - parentDgen.addCustomRow(Map.of("Name", "sample1", "intColumn", 1, "decimalColumn", 1.1, "stringColumn", "one")); - parentDgen.addCustomRow(Map.of("Name", "sample2", "intColumn", 2, "decimalColumn", 2.2, "stringColumn", "two")); - parentDgen.addCustomRow(Map.of("Name", "sample3", "intColumn", 3, "decimalColumn", 3.3, "stringColumn", "three")); + parentDgen.addCustomRow(Map.of("Name", "sample1", intColumn.getName(), 1, decimalColumn.getName(), 1.1, stringColumn.getName(), "one")); + parentDgen.addCustomRow(Map.of("Name", "sample2", intColumn.getName(), 2, decimalColumn.getName(), 2.2, stringColumn.getName(), "two")); + parentDgen.addCustomRow(Map.of("Name", "sample3", intColumn.getName(), 3, decimalColumn.getName(), 3.3, stringColumn.getName(), "three")); parentDgen.insertRows(); goToProjectFolder(getProjectName(), subfolder); @@ -707,8 +720,28 @@ public void testExportImportSampleTypesWithAssayRuns() throws Exception File downloadedFile = doAndWaitForDownload(() -> waitAndClick(WAIT_FOR_JAVASCRIPT, Locator.tagWithAttribute("a", "title", "Download attached file"), 0)); assertElementPresent("Did not find the expected number of icons for " + SAMPLE_TXT_FILE.getName() + " from the imported samples.", Locator.tagContainingText("a", "sample.txt"), 1); checker().verifyTrue("Incorrect file content for sample.txt after folder import", FileUtils.contentEquals(downloadedFile, SAMPLE_TXT_FILE)); + + // verify the other sample type data is round-tripped as expected + importedDataTable = DataRegionTable.DataRegion(getDriver()).withName("Material").waitFor(); + checker().verifyEquals("Name column data not as expected", List.of("sample3", "sample2", "sample1"), + importedDataTable.getColumnDataAsText("Name")); + checker().verifyEquals("intColumn column data not as expected", List.of("3", "2", "1"), + importedDataTable.getColumnDataAsText(intColumn.getName())); + checker().verifyEquals("decimalColumn column data not as expected", List.of("3.3", "2.2", "1.1"), + importedDataTable.getColumnDataAsText(decimalColumn.getName())); + checker().verifyEquals("stringColumn column data not as expected", List.of("three", "two", "one"), + importedDataTable.getColumnDataAsText(stringColumn.getName())); } + private FieldDefinition getFieldByNamePart(List fields, String namePart) + { + for (FieldDefinition field : fields) + { + if (field.isNamePartMatch(namePart)) + return field; + } + return null; + } private StringBuilder checkDisplayFields(String displayField, List columnLabels) { diff --git a/src/org/labkey/test/tests/SimpleModuleTest.java b/src/org/labkey/test/tests/SimpleModuleTest.java index 0e21664401..6b55b56fa7 100644 --- a/src/org/labkey/test/tests/SimpleModuleTest.java +++ b/src/org/labkey/test/tests/SimpleModuleTest.java @@ -16,8 +16,8 @@ package org.labkey.test.tests; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.similarity.LevenshteinDistance; import org.apache.hc.core5.http.HttpStatus; +import org.assertj.core.api.Assertions; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.Nullable; import org.junit.Assert; @@ -62,6 +62,7 @@ import org.labkey.test.util.Maps; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.RReportHelper; +import org.labkey.test.util.SimpleHttpRequest; import org.labkey.test.util.TestLogger; import org.labkey.test.util.WikiHelper; import org.labkey.test.util.ext4cmp.Ext4FieldRef; @@ -1125,7 +1126,7 @@ private void doTestQueryViews() } @LogMethod - private void doTestReports() + private void doTestReports() throws IOException { RReportHelper _rReportHelper = new RReportHelper(this); WikiHelper wikiHelper = new WikiHelper(this); @@ -1177,7 +1178,7 @@ private void doTestReports() } @LogMethod - private void doTestReportThumbnails() + private void doTestReportThumbnails() throws IOException { goToProjectHome(); log("Verify custom module report thumbnail images"); @@ -1187,14 +1188,13 @@ private void doTestReportThumbnails() } @LogMethod - private void doTestReportIcon() + private void doTestReportIcon() throws IOException { log("Verify custom module report icon image"); setFormElement(Locator.xpath("//table[contains(@class, 'dataset-search')]//input"), KNITR_PEOPLE); waitForElementToDisappear(Locator.tag("tr").withClass("x4-grid-row").containing(WANT_TO_BE_COOL).notHidden()); File expectedIconFile = TestFileUtils.getSampleData(THUMBNAIL_FOLDER + KNITR_PEOPLE + ICON_FILENAME); - String expectedIcon = TestFileUtils.getFileContents(expectedIconFile); WebElement img = waitForElement(Locator.tag("img").withClass("dataview-icon").withoutClass("x4-tree-icon-parent").notHidden()); String backgroundImage = StringUtils.trimToEmpty(img.getCssValue("background-image")); @@ -1204,12 +1204,10 @@ private void doTestReportIcon() Assert.fail("Module report icon style is not as expected: " + img.getDomAttribute("style")); } String iconUrl = matcher.group(1); - String iconData = WebTestHelper.getHttpResponse(iconUrl).getResponseBody(); + File downloadedIcon = new SimpleHttpRequest(iconUrl).getResponseAsFile(TestFileUtils.ensureTestTempFile(KNITR_PEOPLE + ICON_FILENAME)); - int lengthToCompare = 3000; - int diff = LevenshteinDistance.getDefaultInstance().apply(expectedIcon.substring(0, lengthToCompare), iconData.substring(0, lengthToCompare)); - assertTrue("Module report icon is not as expected, diff is " + diff, expectedIcon.equals(iconData) || - diff <= lengthToCompare * 0.03); // Might be slightly different due to indentations, etc + Assertions.assertThat(downloadedIcon).as("Module report icon is not as expected") + .hasSameBinaryContentAs(expectedIconFile); } @LogMethod @@ -1222,20 +1220,18 @@ private void doTestReportCreatedDate() } @LogMethod - private void verifyReportThumbnail(@LoggedParam String reportTitle) + private void verifyReportThumbnail(@LoggedParam String reportTitle) throws IOException { File expectedThumbnailFile = TestFileUtils.getSampleData(THUMBNAIL_FOLDER + reportTitle + THUMBNAIL_FILENAME); - String expectedThumbnail = TestFileUtils.getFileContents(expectedThumbnailFile); WebElement reportLink = waitForElement(Locator.xpath("//a[text()='" + reportTitle + "']")); mouseOver(reportLink); WebElement thumbnail = waitForElement(Locator.xpath("//div[@class='thumbnail']/img").notHidden()); - String thumbnailData = WebTestHelper.getHttpResponse(thumbnail.getDomProperty("src")).getResponseBody(); + File downloadedThumbnail = new SimpleHttpRequest(thumbnail.getDomProperty("src")) + .getResponseAsFile(TestFileUtils.ensureTestTempFile(reportTitle + THUMBNAIL_FILENAME)); - int lengthToCompare = 5000; - int diff = LevenshteinDistance.getDefaultInstance().apply(expectedThumbnail.substring(0, lengthToCompare), thumbnailData.substring(0, lengthToCompare)); - assertTrue("Module report thumbnail is not as expected, diff is " + diff, expectedThumbnail.equals(thumbnailData) || - diff <= lengthToCompare * 0.03); // Might be slightly different due to indentations, etc + Assertions.assertThat(downloadedThumbnail).as("Module report thumbnail is not as expected") + .hasSameBinaryContentAs(expectedThumbnailFile); } @LogMethod diff --git a/src/org/labkey/test/util/Crawler.java b/src/org/labkey/test/util/Crawler.java index 49963a2c74..fc3717aea6 100644 --- a/src/org/labkey/test/util/Crawler.java +++ b/src/org/labkey/test/util/Crawler.java @@ -997,6 +997,13 @@ private List crawlLink(final UrlToCheck urlToCheck) actualUrl = _test.getURL(); if (!actualUrl.toString().endsWith(relativeURL)) { + try + { + String actualRelativeUrl = WebTestHelper.makeRelativeUrl(actualUrl.toString()); + _actionsVisited.add(new ControllerActionId(actualRelativeUrl)); + _urlsVisited.add(actualRelativeUrl); + _urlsChecked.add(EscapeUtil.decodeUriPath(stripQueryParams(actualRelativeUrl))); + } catch (IllegalArgumentException ignored) {} originMessage = originMessage + "\nRedirected to: " + actualUrl; } diff --git a/src/org/labkey/test/util/SimpleHttpRequest.java b/src/org/labkey/test/util/SimpleHttpRequest.java index ec3962a0b4..38bafa0140 100644 --- a/src/org/labkey/test/util/SimpleHttpRequest.java +++ b/src/org/labkey/test/util/SimpleHttpRequest.java @@ -19,6 +19,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.labkey.remoteapi.Connection; +import org.labkey.test.TestFileUtils; import org.openqa.selenium.Cookie; import org.openqa.selenium.WebDriver; @@ -219,16 +220,21 @@ else if (responseFilename != null && fileName.contains(".")) } } + public File getResponseAsFile() throws IOException + { + return getResponseAsFile(TestFileUtils.ensureTestTempDir()); + } + private void useCopiedSession(HttpURLConnection con) { StringBuilder cookieString = new StringBuilder(); - for (Map.Entry cookie : _cookies.entrySet()) + for (Map.Entry cookie : _cookies.entrySet()) { if (cookie.getKey().equals(Connection.X_LABKEY_CSRF)) - con.setRequestProperty((String)cookie.getKey(), (String)cookie.getValue()); + con.setRequestProperty(cookie.getKey(), cookie.getValue()); if (cookie.getKey().equals(Connection.JSESSIONID)) - con.setRequestProperty((String)cookie.getKey(), (String)cookie.getValue()); + con.setRequestProperty(cookie.getKey(), cookie.getValue()); if (!cookieString.isEmpty()) cookieString.append("; "); diff --git a/src/org/labkey/test/util/TestDataGenerator.java b/src/org/labkey/test/util/TestDataGenerator.java index a35446b0fc..5d745d4dd8 100644 --- a/src/org/labkey/test/util/TestDataGenerator.java +++ b/src/org/labkey/test/util/TestDataGenerator.java @@ -582,7 +582,7 @@ public static String randomFieldName(@NotNull String part, int numStartChars, in String randomFieldName = randomName(part, numStartChars, numEndChars, chars, exclusion); // Avoid generating fields names with reserved substitution format patterns. e.g. ":Date" or ":First" - if (numStartChars > 0 && randomFieldName.charAt(numStartChars - 1) == ':' && + if (numStartChars > 0 && part.length() >= 4 && randomFieldName.charAt(numStartChars - 1) == ':' && StringUtils.isAlpha(part.substring(0, 4))) // The shortest pattern is four characters (see org.labkey.api.util.SubstitutionFormat.getFormatNames) { String regenExclusion = Objects.requireNonNullElse(exclusion, "") + ":"; diff --git a/src/org/labkey/test/util/exp/SampleTypeAPIHelper.java b/src/org/labkey/test/util/exp/SampleTypeAPIHelper.java index a506f039a5..8a5bcf1a81 100644 --- a/src/org/labkey/test/util/exp/SampleTypeAPIHelper.java +++ b/src/org/labkey/test/util/exp/SampleTypeAPIHelper.java @@ -9,6 +9,7 @@ import org.labkey.remoteapi.query.Sort; import org.labkey.test.WebTestHelper; import org.labkey.test.params.FieldDefinition; +import org.labkey.test.params.FieldInfo; import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.util.DomainUtils; import org.labkey.test.util.TestDataGenerator; @@ -58,13 +59,13 @@ public static TestDataGenerator createEmptySampleType(String containerPath, Samp public static List sampleTypeTestFields(boolean withFileField) { List fields = new ArrayList<>(Arrays.asList( - 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))); + FieldInfo.random("int,./Column", FieldDefinition.ColumnType.Integer).getFieldDefinition(), // Issue 53431: include special chars that are fieldKey encoded + FieldInfo.random("decimalColumn", FieldDefinition.ColumnType.Decimal).getFieldDefinition(), + FieldInfo.random("stringColumn", FieldDefinition.ColumnType.String).getFieldDefinition(), + FieldInfo.random("sampleDate", FieldDefinition.ColumnType.DateAndTime).getFieldDefinition(), + FieldInfo.random("boolColumn", FieldDefinition.ColumnType.Boolean).getFieldDefinition())); if (withFileField) - fields.add(new FieldDefinition(TestDataGenerator.randomFieldName("fileColumn"), FieldDefinition.ColumnType.File)); + fields.add(FieldInfo.random("file,./Column", FieldDefinition.ColumnType.File).getFieldDefinition()); return fields; }