From 92b596c53d1f1f88d31a86c341181b2975f68ef3 Mon Sep 17 00:00:00 2001 From: Chris Joosse Date: Fri, 24 Jun 2022 14:50:21 -0700 Subject: [PATCH 1/2] add test coverage for cross-folder list scenarios --- .../labkey/test/pages/LabkeyErrorPage.java | 3 +- .../test/tests/list/CrossfolderListTest.java | 208 ++++++++++++++++++ 2 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 src/org/labkey/test/tests/list/CrossfolderListTest.java diff --git a/src/org/labkey/test/pages/LabkeyErrorPage.java b/src/org/labkey/test/pages/LabkeyErrorPage.java index 28d7ce1ce1..da9223e7b2 100644 --- a/src/org/labkey/test/pages/LabkeyErrorPage.java +++ b/src/org/labkey/test/pages/LabkeyErrorPage.java @@ -53,7 +53,8 @@ protected ElementCache newElementCache() protected class ElementCache extends LabKeyPage.ElementCache { - WebElement errorHeading = Locator.tagWithClass("div", "labkey-error-heading").findWhenNeeded(this); + WebElement errorHeading = Locator.tagWithClass("div", "labkey-error-heading") + .findWhenNeeded(this).withTimeout(WAIT_FOR_JAVASCRIPT); WebElement errorSubHeading = Locator.tagWithClass("div", "labkey-error-subheading").findWhenNeeded(this); WebElement errorInstruction = Locator.tagWithClass("div", " labkey-error-instruction").index(1).findWhenNeeded(this); WebElement errorImage = Locator.tagWithAttributeContaining("*","alt","LabKey Error").findWhenNeeded(this); diff --git a/src/org/labkey/test/tests/list/CrossfolderListTest.java b/src/org/labkey/test/tests/list/CrossfolderListTest.java new file mode 100644 index 0000000000..a8e19bc338 --- /dev/null +++ b/src/org/labkey/test/tests/list/CrossfolderListTest.java @@ -0,0 +1,208 @@ +package org.labkey.test.tests.list; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.labkey.data.xml.queryCustomView.FilterType; +import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.pages.LabkeyErrorPage; +import org.labkey.test.pages.list.GridPage; +import org.labkey.test.params.FieldDefinition; +import org.labkey.test.params.list.IntListDefinition; +import org.labkey.test.params.list.ListDefinition; +import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.TestUser; +import org.labkey.test.util.query.QueryApiHelper; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + + +@Category({}) +public class CrossfolderListTest extends BaseWebDriverTest +{ + private static final TestUser FOLDER_A_ADMIN = new TestUser("folder_admin@crossfolderlisttest.test"); + private static final TestUser FOLDER_A_READER = new TestUser("folder_reader@crossfolderlisttest.test"); + private static final String SUBFOLDER_A = "subA"; + private static String SUBFOLDER_A_PATH; + + @Override + protected void doCleanup(boolean afterTest) + { + _containerHelper.deleteProject(getProjectName(), afterTest); + _userHelper.deleteUsers(afterTest, FOLDER_A_ADMIN.getEmail(), FOLDER_A_READER.getEmail()); + } + + @BeforeClass + public static void setupProject() + { + CrossfolderListTest init = (CrossfolderListTest) getCurrentTest(); + init.doSetup(); + } + + private void doSetup() + { + _containerHelper.createProject(getProjectName(), null); + _containerHelper.createSubfolder(getProjectName(), SUBFOLDER_A); + SUBFOLDER_A_PATH = getProjectName() + "/" + SUBFOLDER_A; + FOLDER_A_ADMIN.create(this); + FOLDER_A_READER.create(this); + } + + + @Test + public void testAddDataInSubfolderToTopLevelList() throws Exception + { + // define a list in the top folder, give it some random data + String listName = "top_folder_list_for_subfolder_data"; + ListDefinition listDef = makeListDef(listName, testFields()); + var dGen = listDef.create(createDefaultConnection(), getProjectName()); + dGen.withGeneratedRows(3); + dGen.insertRows(); + + // make a couple rows of data for the subfolder + List> rowsToInsert = List.of( + Map.of("intColumn", 1, "decimalColumn", 1.1, + "stringColumn", "stringy", "dateColumn", "11/11/2023", "boolColumn", true), + Map.of("intColumn", 2, "decimalColumn", 2.2, + "stringColumn", "chewy", "dateColumn", "11/12/2023", "boolColumn", false) + ); + + // insert 2 records into the list, in the subfolder + var qah = new QueryApiHelper(createDefaultConnection(), SUBFOLDER_A_PATH, "lists", listName); + qah.insertRows(rowsToInsert); + var subFolderListPage = GridPage.beginAt(this, SUBFOLDER_A_PATH, listName); + + var displayedData = subFolderListPage.getGrid().getTableData(); + assertEquals("expect only subfolder data to be shown here by default", + rowsToInsert.size(), displayedData.size()); + assertThat("expect only the data inserted here to be shown", + subFolderListPage.getGrid().getColumnDataAsText("String Column"), hasItems("stringy", "chewy")); + } + + @Test + public void testListMetadataIsAvailableInSubfolder() throws Exception + { + // define a list in the top folder, give it some random data + String listName = "top_folder_list_for_metadata"; + ListDefinition listDef = makeListDef(listName, testFields()); + var dGen = listDef.create(createDefaultConnection(), getProjectName()); + dGen.withGeneratedRows(2); + dGen.insertRows(); + + // make a row of data for the subfolder + List> rowToInsert = List.of( + Map.of("intColumn", 3, "decimalColumn", 3.3, + "stringColumn", "meta", "dateColumn", "11/14/2023", "boolColumn", true)); + + // insert 1 record into the list, in the subfolder + var qah = new QueryApiHelper(createDefaultConnection(), SUBFOLDER_A_PATH, "lists", listName); + qah.insertRows(rowToInsert); + var subFolderListPage = GridPage.beginAt(this, SUBFOLDER_A_PATH, listName); + + // modify the default view to include key, folder + var customizeView = subFolderListPage.getGrid().openCustomizeGrid(); + customizeView.showHiddenItems(); + customizeView.addColumn("Key"); + customizeView.addColumn("Container"); // fun fact: the label is 'Folder' + customizeView.saveDefaultView(); + + // now ensure we got the metadata + var displayedData = subFolderListPage.getGrid().getTableData(); + assertEquals("expect only 1 record to be visible here", + 1, displayedData.size()); + assertThat("expect to be shown folder and key columns", + subFolderListPage.getGrid().getColumnNames(), hasItems("container", "Key")); + + var displayedRecord = displayedData.get(0); + assertEquals("expect subfolder name metadata to be available here", + SUBFOLDER_A, displayedRecord.get("container")); + assertNotNull("expect list metadata like key to be accessible in a subfolder", + displayedRecord.get("Key")); + } + + @Test + public void testSubfolderDefinedListCannotBeQueriedFromTopLevel() throws Exception + { + // define a list in a subfolder, give it some random data + String listName = "subfolder_list_for_metadata"; + ListDefinition listDef = makeListDef(listName, testFields()); + listDef.create(createDefaultConnection(), SUBFOLDER_A_PATH); + + // attempt to navigate to the list but in the project level + GridPage.beginAt(this, getProjectName(), listName); + // expect to land on an error page instead + var errPage = new LabkeyErrorPage(getDriver()); + assertEquals("Expect error explaining that the specified list is not here", + "List does not exist in this container", errPage.getErrorHeading()); + } + + @Test + public void testListsWithSameNameInTopAndSubfolder() throws Exception + { + String listName = "test_name_collision_list"; + + // create a simple list in subfolder, insert some values, and grab a column's worth of data from it + ListDefinition listDef = makeListDef(listName, testFields()); + var dGen = listDef.create(createDefaultConnection(), getProjectName()); + dGen.withGeneratedRows(2); + dGen.insertRows(); + var topStrings = dGen.getRows().stream().map(a-> a.get("stringColumn").toString()).collect(Collectors.toList()); + + // create another list with the same name at the project level, insert a little different data and capture the string values + ListDefinition listDef2 = makeListDef(listName, testFields()); + var dgen2 = listDef2.create(createDefaultConnection(), SUBFOLDER_A_PATH); + dgen2.withGeneratedRows(3); + dgen2.insertRows(); + var bottomStrings = dgen2.getRows().stream().map(a-> a.get("stringColumn").toString()).collect(Collectors.toList()); + + // navigate to the top folder, open the container filter to include the subfolder and ensure only data from this list is shown + var topPage = GridPage.beginAt(this, getProjectName(), listName); + topPage.getGrid().setContainerFilter(DataRegionTable.ContainerFilterType.CURRENT_AND_SUBFOLDERS); // include subfolder in filter + assertEquals("expect the view in the top folder to only show current list data even with different subfolder list by the same name", + new HashSet(topStrings), new HashSet(topPage.getGrid().getColumnDataAsText("String Column"))); + + // now view the list from the subfolder and ensure the list contents aren't mixed despite name ambiguity + var subfolderPage = GridPage.beginAt(this, SUBFOLDER_A_PATH, listName); + subfolderPage.getGrid().setContainerFilter(DataRegionTable.ContainerFilterType.ALL_FOLDERS); + assertEquals("expect the view in the top folder to only show current list data even with different subfolder list by the same name", + new HashSet(bottomStrings), new HashSet(subfolderPage.getGrid().getColumnDataAsText("String Column"))); + } + + private ListDefinition makeListDef(String listName, List listColumns) + { + ListDefinition listDef = new IntListDefinition(listName, "Key"); + listDef.setFields(listColumns); + return listDef; + } + + private List testFields() + { + return Arrays.asList( + new FieldDefinition("intColumn", FieldDefinition.ColumnType.Integer), + new FieldDefinition("decimalColumn", FieldDefinition.ColumnType.Decimal), + new FieldDefinition("stringColumn", FieldDefinition.ColumnType.String), + new FieldDefinition("dateColumn", FieldDefinition.ColumnType.DateAndTime), + new FieldDefinition("boolColumn", FieldDefinition.ColumnType.Boolean)); + } + + @Override + protected String getProjectName() + { + return "CrossfolderListTest Project"; + } + + @Override + public List getAssociatedModules() + { + return Arrays.asList(); + } +} From eb7dc76fc30c2549b53c4c833958f8b1173024fb Mon Sep 17 00:00:00 2001 From: Chris Joosse Date: Mon, 27 Jun 2022 11:53:39 -0700 Subject: [PATCH 2/2] clean up code/review feedback --- ...ListTest.java => CrossFolderListTest.java} | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) rename src/org/labkey/test/tests/list/{CrossfolderListTest.java => CrossFolderListTest.java} (88%) diff --git a/src/org/labkey/test/tests/list/CrossfolderListTest.java b/src/org/labkey/test/tests/list/CrossFolderListTest.java similarity index 88% rename from src/org/labkey/test/tests/list/CrossfolderListTest.java rename to src/org/labkey/test/tests/list/CrossFolderListTest.java index a8e19bc338..7dc093a564 100644 --- a/src/org/labkey/test/tests/list/CrossfolderListTest.java +++ b/src/org/labkey/test/tests/list/CrossFolderListTest.java @@ -3,8 +3,10 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.labkey.data.xml.queryCustomView.FilterType; import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.categories.Daily; +import org.labkey.test.categories.Data; +import org.labkey.test.categories.Hosting; import org.labkey.test.pages.LabkeyErrorPage; import org.labkey.test.pages.list.GridPage; import org.labkey.test.params.FieldDefinition; @@ -26,11 +28,9 @@ import static org.junit.Assert.assertNotNull; -@Category({}) -public class CrossfolderListTest extends BaseWebDriverTest +@Category({Daily.class, Data.class, Hosting.class}) +public class CrossFolderListTest extends BaseWebDriverTest { - private static final TestUser FOLDER_A_ADMIN = new TestUser("folder_admin@crossfolderlisttest.test"); - private static final TestUser FOLDER_A_READER = new TestUser("folder_reader@crossfolderlisttest.test"); private static final String SUBFOLDER_A = "subA"; private static String SUBFOLDER_A_PATH; @@ -38,13 +38,12 @@ public class CrossfolderListTest extends BaseWebDriverTest protected void doCleanup(boolean afterTest) { _containerHelper.deleteProject(getProjectName(), afterTest); - _userHelper.deleteUsers(afterTest, FOLDER_A_ADMIN.getEmail(), FOLDER_A_READER.getEmail()); } @BeforeClass public static void setupProject() { - CrossfolderListTest init = (CrossfolderListTest) getCurrentTest(); + CrossFolderListTest init = (CrossFolderListTest) getCurrentTest(); init.doSetup(); } @@ -53,8 +52,6 @@ private void doSetup() _containerHelper.createProject(getProjectName(), null); _containerHelper.createSubfolder(getProjectName(), SUBFOLDER_A); SUBFOLDER_A_PATH = getProjectName() + "/" + SUBFOLDER_A; - FOLDER_A_ADMIN.create(this); - FOLDER_A_READER.create(this); } @@ -63,7 +60,7 @@ public void testAddDataInSubfolderToTopLevelList() throws Exception { // define a list in the top folder, give it some random data String listName = "top_folder_list_for_subfolder_data"; - ListDefinition listDef = makeListDef(listName, testFields()); + ListDefinition listDef = createListDef(listName, testFields()); var dGen = listDef.create(createDefaultConnection(), getProjectName()); dGen.withGeneratedRows(3); dGen.insertRows(); @@ -93,7 +90,7 @@ public void testListMetadataIsAvailableInSubfolder() throws Exception { // define a list in the top folder, give it some random data String listName = "top_folder_list_for_metadata"; - ListDefinition listDef = makeListDef(listName, testFields()); + ListDefinition listDef = createListDef(listName, testFields()); var dGen = listDef.create(createDefaultConnection(), getProjectName()); dGen.withGeneratedRows(2); dGen.insertRows(); @@ -134,7 +131,7 @@ public void testSubfolderDefinedListCannotBeQueriedFromTopLevel() throws Excepti { // define a list in a subfolder, give it some random data String listName = "subfolder_list_for_metadata"; - ListDefinition listDef = makeListDef(listName, testFields()); + ListDefinition listDef = createListDef(listName, testFields()); listDef.create(createDefaultConnection(), SUBFOLDER_A_PATH); // attempt to navigate to the list but in the project level @@ -151,14 +148,14 @@ public void testListsWithSameNameInTopAndSubfolder() throws Exception String listName = "test_name_collision_list"; // create a simple list in subfolder, insert some values, and grab a column's worth of data from it - ListDefinition listDef = makeListDef(listName, testFields()); + ListDefinition listDef = createListDef(listName, testFields()); var dGen = listDef.create(createDefaultConnection(), getProjectName()); dGen.withGeneratedRows(2); dGen.insertRows(); var topStrings = dGen.getRows().stream().map(a-> a.get("stringColumn").toString()).collect(Collectors.toList()); // create another list with the same name at the project level, insert a little different data and capture the string values - ListDefinition listDef2 = makeListDef(listName, testFields()); + ListDefinition listDef2 = createListDef(listName, testFields()); var dgen2 = listDef2.create(createDefaultConnection(), SUBFOLDER_A_PATH); dgen2.withGeneratedRows(3); dgen2.insertRows(); @@ -177,7 +174,7 @@ public void testListsWithSameNameInTopAndSubfolder() throws Exception new HashSet(bottomStrings), new HashSet(subfolderPage.getGrid().getColumnDataAsText("String Column"))); } - private ListDefinition makeListDef(String listName, List listColumns) + private ListDefinition createListDef(String listName, List listColumns) { ListDefinition listDef = new IntListDefinition(listName, "Key"); listDef.setFields(listColumns); @@ -197,7 +194,7 @@ private List testFields() @Override protected String getProjectName() { - return "CrossfolderListTest Project"; + return "CrossFolderListTest Project"; } @Override