Skip to content

Commit 84564ab

Browse files
Merge branch 'develop' into fb_53306_propertyName
2 parents 8bf93d2 + bd3461c commit 84564ab

13 files changed

Lines changed: 142 additions & 42 deletions

data/api/rlabkey-api-experiment.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@
285285
<![CDATA[
286286
Rows via createAndLoad = 3
287287
Rows via insert = 2
288-
duplicate key
288+
already exists
289289
Rows via merge = 10
290290
Has audit transaction id = TRUE
291291
]]>

src/org/labkey/test/BaseWebDriverTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,6 @@ public abstract class BaseWebDriverTest extends LabKeySiteWrapper implements Cle
216216

217217
public static final double DELTA = 10E-10;
218218

219-
@Deprecated // Going away soon
220-
public static final String[] ILLEGAL_QUERY_KEY_CHARACTERS = FieldKey.getIllegalChars().toArray(new String[0]);
221219
public static final String ALL_ILLEGAL_QUERY_KEY_CHARACTERS = StringUtils.join(FieldKey.getIllegalChars(), "");
222220
// See TSVWriter.shouldQuote. Generally we are not able to use the tab and new line characters when creating field names in the UI, but including here for completeness
223221
public static final String[] TRICKY_IMPORT_FIELD_CHARACTERS = {"\\", "\"", "\\t", ",", "\\n", "\\r"};
@@ -300,7 +298,7 @@ public WebDriver getWrappedDriver()
300298
return SingletonWebDriver.getInstance().getWebDriver();
301299
}
302300

303-
protected abstract @Nullable String getProjectName();
301+
protected abstract String getProjectName();
304302

305303
public final @Nullable String getPrimaryTestProject()
306304
{

src/org/labkey/test/components/ui/grids/EditableGrid.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.LinkedHashSet;
4545
import java.util.List;
4646
import java.util.Map;
47+
import java.util.Optional;
4748
import java.util.Set;
4849
import java.util.TimeZone;
4950
import java.util.function.Function;
@@ -1114,6 +1115,20 @@ public String getCellError(int row, CharSequence columnIdentifier)
11141115
return null;
11151116
}
11161117

1118+
/**
1119+
* @param row row index
1120+
* @param columnIdentifier fieldKey, name, or label of column
1121+
* @return error popover text in the specified cell or 'null' if there is no error
1122+
*/
1123+
public String getErrorPopoverText(int row, CharSequence columnIdentifier)
1124+
{
1125+
WebElement gridCell = getCell(row, columnIdentifier);
1126+
1127+
if (cellHasError(gridCell))
1128+
return getCellPopoverText(row, columnIdentifier);
1129+
return null;
1130+
}
1131+
11171132
/**
11181133
* @param row row index
11191134
* @param columnIdentifier fieldKey, name, or label of column
@@ -1123,11 +1138,17 @@ public String getCellPopoverText(int row, CharSequence columnIdentifier)
11231138
{
11241139
WebElement cellDiv = Locator.tagWithClass("div", "cellular-display").findElement(getCell(row, columnIdentifier));
11251140
getWrapper().mouseOver(cellDiv); // cause the tooltip to be present
1126-
if (WebDriverWrapper.waitFor(()-> null != Locator.byClass("popover").findElementOrNull(getDriver()), 1000))
1127-
{
1128-
return Locator.byClass("popover").findElement(getDriver()).getText();
1129-
}
1130-
return null;
1141+
return Optional.ofNullable(WebDriverWrapper.waitFor(()-> Locators.popover.findElementOrNull(getDriver()), 1000))
1142+
.map(WebElement::getText)
1143+
.orElse(null);
1144+
}
1145+
1146+
public void dismissPopover()
1147+
{
1148+
Locators.popover.findOptionalElement(getDriver()).ifPresent(popover -> {
1149+
getWrapper().mouseOut();
1150+
getWrapper().shortWait().until(ExpectedConditions.invisibilityOf(popover));
1151+
});
11311152
}
11321153

11331154
public List<WebElement> getCellErrors()
@@ -1286,6 +1307,7 @@ private Locators()
12861307
static final Locator.XPathLocator rows = Locator.tag("tbody").childTag("tr").withoutClass("grid-empty").withoutClass("grid-loading");
12871308
static final Locator headerCells = Locator.css("thead tr th");
12881309
static final Locator inputCell = Locator.css(".eg-input-cell");
1310+
static final Locator popover = Locator.byClass("popover");
12891311
}
12901312

12911313
public static class EditableGridFinder extends WebDriverComponent.WebDriverComponentFinder<EditableGrid, EditableGridFinder>

src/org/labkey/test/components/ui/grids/GridFilterModal.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import org.labkey.test.components.react.Tabs;
88
import org.labkey.test.components.ui.search.FilterExpressionPanel;
99
import org.labkey.test.components.ui.search.FilterFacetedPanel;
10+
import org.labkey.test.params.WrapsFieldKey;
1011
import org.labkey.test.util.selenium.WebElementUtils;
12+
import org.openqa.selenium.NoSuchElementException;
1113
import org.openqa.selenium.WebDriver;
1214
import org.openqa.selenium.WebElement;
1315
import org.openqa.selenium.support.ui.ExpectedConditions;
@@ -36,12 +38,13 @@ protected void waitForReady()
3638

3739
/**
3840
* Select field to configure filters for
39-
* @param fieldLabel Field's label
41+
* @param fieldIdentifier fieldKey or field label
4042
* @return this component
4143
*/
42-
public GridFilterModal selectField(String fieldLabel)
44+
public GridFilterModal selectField(CharSequence fieldIdentifier)
4345
{
44-
WebElement fieldItem = elementCache().findFieldOption(fieldLabel);
46+
WebElement fieldItem = elementCache().findFieldOption(fieldIdentifier);
47+
String fieldLabel = WebElementUtils.getTextContent(fieldItem);
4548
fieldItem.click();
4649
Locator.byClass("field-modal__col-sub-title").withText("Find values for " + fieldLabel)
4750
.waitForElement(elementCache().filterPanel, 10_000);
@@ -182,15 +185,25 @@ protected ElementCache newElementCache()
182185

183186
protected class ElementCache extends ModalDialog.ElementCache
184187
{
185-
public final Locator listItemLoc = Locator.byClass("list-group-item");
188+
public final Locator.XPathLocator listItemLoc = Locator.byClass("list-group-item");
186189

187190
// Fields column
188191
public final WebElement fieldsSelectionPanel = Locator.byClass("filter-modal__col_fields")
189192
.findWhenNeeded(this);
190193

191-
protected WebElement findFieldOption(String queryName)
194+
protected WebElement findFieldOption(CharSequence queryName)
192195
{
193-
return listItemLoc.withText(queryName).findElement(elementCache().fieldsSelectionPanel);
196+
try
197+
{
198+
return listItemLoc.withChild(Locator.tagWithAttribute("span", "data-fieldkey", queryName.toString())).findElement(fieldsSelectionPanel);
199+
}
200+
catch (NoSuchElementException nse)
201+
{
202+
if (!(queryName instanceof WrapsFieldKey))
203+
return listItemLoc.withText(queryName.toString()).findElement(elementCache().fieldsSelectionPanel);
204+
else
205+
throw nse;
206+
}
194207
}
195208
protected List<WebElement> findFieldOptions()
196209
{

src/org/labkey/test/components/ui/grids/ResponsiveGrid.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.openqa.selenium.support.ui.ExpectedConditions;
2828

2929
import java.util.ArrayList;
30+
import java.util.Arrays;
3031
import java.util.Collection;
3132
import java.util.Collections;
3233
import java.util.List;
@@ -449,6 +450,17 @@ public T selectRows(CharSequence columnIdentifier, Collection<String> texts, boo
449450
return getThis();
450451
}
451452

453+
/**
454+
* Checks the specified rows' selector checkboxes
455+
* @param columnIdentifier fieldKey, name, or label of column
456+
* @param texts Text to search for in the specified column
457+
* @return this grid
458+
*/
459+
public T selectRows(CharSequence columnIdentifier, String... texts)
460+
{
461+
return selectRows(columnIdentifier, Arrays.asList(texts), true);
462+
}
463+
452464
/**
453465
* Is the row at the selected index selected
454466
* @param index Row index (zero-based)

src/org/labkey/test/tests/AdminConsoleNavigationTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.labkey.test.tests;
22

3+
import org.assertj.core.api.Assertions;
34
import org.jetbrains.annotations.Nullable;
45
import org.junit.Assert;
56
import org.junit.BeforeClass;
@@ -86,6 +87,7 @@ public void testAdminNavTrails()
8687
{
8788
linkHrefs.put(link.getText(), link.getAttribute("href"));
8889
}
90+
Assertions.assertThat(linkHrefs.keySet()).as("Expected links").containsAll(ignoredLinks.stream().map(String::toUpperCase).toList());
8991

9092
List<String> pagesMissingNavTrail = new ArrayList<>();
9193

@@ -121,6 +123,7 @@ public void testTroubleshooterLinkAccess()
121123
impersonate(TROUBLESHOOTER);
122124
Map<String, String> linkHrefs = new LinkedHashMap<>();
123125
List<WebElement> troubleshooterLinks = adminConsole.getAllAdminConsoleLinks();
126+
assertTrue(String.format("Failed sanity check. Only found %s admin links. There should be more.", troubleshooterLinks.size()), troubleshooterLinks.size() > 10);
124127
for (WebElement link : troubleshooterLinks)
125128
linkHrefs.put(link.getText(), link.getAttribute("href"));
126129

@@ -155,6 +158,7 @@ public void testAdminConsoleLinksForAdminAndNonAdmin()
155158
));
156159
ShowAdminPage adminConsole = goToAdminConsole();
157160
List<WebElement> adminLinks = adminConsole.getAllAdminConsoleLinks();
161+
assertTrue(String.format("Failed sanity check. Only found %s admin links. There should be more.", adminLinks.size()), adminLinks.size() > 10);
158162
Map<String, String> linkHrefs = new LinkedHashMap<>();
159163
for (WebElement link : adminLinks)
160164
linkHrefs.put(link.getText(), link.getAttribute("href"));

src/org/labkey/test/tests/CrawlerTest.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package org.labkey.test.tests;
22

3+
import org.assertj.core.api.Assertions;
34
import org.junit.After;
45
import org.junit.Assert;
56
import org.junit.Assume;
67
import org.junit.BeforeClass;
78
import org.junit.Test;
89
import org.junit.experimental.categories.Category;
10+
import org.labkey.remoteapi.SimpleGetCommand;
911
import org.labkey.test.BaseWebDriverTest;
1012
import org.labkey.test.Locator;
1113
import org.labkey.test.Locators;
@@ -117,18 +119,25 @@ public void testEnforceCsp() throws Exception
117119
beginAt(getInjectUrl(Crawler.injectScriptBlock), 10_000);
118120

119121
log("Verify that enforced CSP is also reported");
120-
CspLogUtil.checkNewCspWarnings(getArtifactCollector());
122+
CspLogUtil.checkNewCspWarnings(getArtifactCollector()); // throws CspWarningDetectedException
121123
}
122124

123125
@Test (expected = CspLogUtil.CspWarningDetectedException.class)
124-
public void testCspWarning()
126+
public void testCspWarning() throws Exception
125127
{
126128
Assume.assumeFalse("Can't test for CSP report", TestProperties.isCspCheckSkipped());
127129

128130
_cspConfigHelper.setEnforceCsp(false);
129131

130-
beginAt(WebTestHelper.buildRelativeUrl(MODULE_NAME, getProjectName(), "cspWarning"));
131-
CspLogUtil.checkNewCspWarnings(getArtifactCollector());
132+
int initialLength = getCspReportLog().length();
133+
134+
String cspWarningUrl = WebTestHelper.buildRelativeUrl(MODULE_NAME, getProjectName(), "cspWarning");
135+
beginAt(cspWarningUrl);
136+
137+
// 53261: Provide visibility into CSP reports for cloud clients
138+
Assertions.assertThat(getCspReportLog().substring(initialLength)).as("CSP warning").contains(cspWarningUrl);
139+
140+
CspLogUtil.checkNewCspWarnings(getArtifactCollector()); // throws CspWarningDetectedException
132141
}
133142

134143
// Crawler should flag external links without the correct 'rel' attribute
@@ -188,6 +197,13 @@ protected boolean cspFailFast()
188197
return false;
189198
}
190199

200+
public String getCspReportLog() throws Exception
201+
{
202+
return new SimpleGetCommand("admin", "showCspReportLog")
203+
.execute(createDefaultConnection(), null)
204+
.getText();
205+
}
206+
191207
@After
192208
public void postTest()
193209
{

src/org/labkey/test/tests/SampleTypeFolderExportImportTest.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.labkey.test.params.FieldDefinition;
4444
import org.labkey.test.params.experiment.DataClassDefinition;
4545
import org.labkey.test.params.experiment.SampleTypeDefinition;
46+
import org.labkey.test.util.ArtifactCollector;
4647
import org.labkey.test.util.DataRegionTable;
4748
import org.labkey.test.util.LogMethod;
4849
import org.labkey.test.util.PortalHelper;
@@ -546,7 +547,7 @@ public void testExportImportSampleTypesWithAssayRuns() throws Exception
546547
{
547548
String subfolder = "samplesWithAssayRunsFolder";
548549
String subfolderPath = getProjectName() + "/" + subfolder;
549-
String testSamples = "testSamples";
550+
String testSamples = "testSamplesWithFiles";
550551
String assayName = "testAssay";
551552
String importFolder = "assaySamplesImportFolder";
552553

@@ -557,7 +558,7 @@ public void testExportImportSampleTypesWithAssayRuns() throws Exception
557558
// create a test sampleType
558559
List<FieldDefinition> testFields = SampleTypeAPIHelper.sampleTypeTestFields(true);
559560
SampleTypeDefinition testSampleType = new SampleTypeDefinition(testSamples).setFields(testFields)
560-
.addParentAlias("SelfParent"); // to derive from samles in the current type
561+
.addParentAlias("SelfParent"); // to derive from samples in the current type
561562

562563
TestDataGenerator parentDgen = SampleTypeAPIHelper.createEmptySampleType(subfolderPath, testSampleType);
563564
parentDgen.addCustomRow(Map.of("Name", "sample1", "intColumn", 1, "decimalColumn", 1.1, "stringColumn", "one"));
@@ -572,14 +573,23 @@ public void testExportImportSampleTypesWithAssayRuns() throws Exception
572573
portalHelper.addWebPart("Experiment Runs");
573574
portalHelper.addWebPart("Assay List");
574575

575-
// upload a file for a sample's file field
576+
log(String.format("Upload a file '%s' to a sample's file field.", SAMPLE_TXT_FILE.getName()));
576577
clickAndWait(Locator.linkWithText(testSamples));
577578
DataRegionTable sourceSamplesTable = new SampleTypeHelper(this).getSamplesDataRegionTable();
578579
sourceSamplesTable.clickEditRow(1);
579580
waitForElementToBeVisible(Locator.tagWithAttribute("input", "type", "file"));
580581
setFormElement(Locator.tagWithAttribute("input", "type", "file"), SAMPLE_TXT_FILE);
582+
// setFormElement doesn't check that the form element is set.
583+
// Because this test uses random field names, we should validate that the file was actually uploaded. If the
584+
// file is missing later in the test, we can be sure it was present at this point.
585+
Assert.assertTrue("File not uploaded to 'add new' form.",
586+
waitFor(()->!getFormElement(Locator.tagWithAttribute("input", "type", "file")).isEmpty(), 1_500));
581587
clickAndWait(Locator.lkButton("Submit"));
582588

589+
waitForElementToBeVisible(Locator.linkContainingText(SAMPLE_TXT_FILE.getName()));
590+
591+
new ArtifactCollector(this).dumpPageSnapshot("File_Attached_Proof");
592+
583593
goToProjectFolder(getProjectName(), subfolder);
584594

585595
// now define an assay that references it
@@ -644,14 +654,26 @@ public void testExportImportSampleTypesWithAssayRuns() throws Exception
644654
exportData.add(dataTable.getRowDataAsMap(i));
645655
}
646656

647-
// now export the current folder and import it to importProject
657+
log("Now export the current folder and import it to importProject.");
648658
goToFolderManagement()
649659
.goToExportTab();
650660

651-
Checkbox checkbox = new Checkbox(Locator.tagWithText("label", ExportFolderPage.EXPERIMENTS_AND_RUNS)
652-
.precedingSibling("input").waitForElement(getDriver(), WAIT_FOR_JAVASCRIPT));
653-
new Checkbox(Locator.tagWithText("label", "Files").precedingSibling("input").findElement(getDriver())).check();
654-
checkbox.check();
661+
new Checkbox(Locator.tagWithText("label", ExportFolderPage.EXPERIMENTS_AND_RUNS)
662+
.precedingSibling("input").waitForElement(getDriver(), WAIT_FOR_JAVASCRIPT)).check();
663+
664+
new Checkbox(Locator.tagWithText("label", "Files")
665+
.precedingSibling("input").waitForElement(getDriver(), WAIT_FOR_JAVASCRIPT)).check();
666+
667+
Assert.assertTrue("Experiment and Runs not checked for export.",
668+
new Checkbox(Locator.tagWithText("label", ExportFolderPage.EXPERIMENTS_AND_RUNS)
669+
.precedingSibling("input").waitForElement(getDriver(), WAIT_FOR_JAVASCRIPT)).isChecked());
670+
671+
Assert.assertTrue("Files not checked for export.",
672+
new Checkbox(Locator.tagWithText("label", "Files").precedingSibling("input").findElement(getDriver()))
673+
.isChecked());
674+
675+
log("'Experiment and Runs' & 'Files' are selected for export.");
676+
655677
File exportedFolderFile = doAndWaitForDownload(()->findButton("Export").click());
656678

657679
goToProjectFolder(IMPORT_PROJECT_NAME, importFolder);

src/org/labkey/test/tests/SampleTypeNameExpressionTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ public void testNameExpressionPreview() throws IOException, CommandException
896896
mouseOver(createPage.getComponentElement());
897897

898898
log("Use a name expression using a field from the named parent, with parent type not encoded.");
899-
nameExpressionBad = String.format("SNP_${genId}_${%s/$s}_${materialInputs/%s/%s}", parentAlias, PARENT_FIELD_CURLY_RIGHT_INT.getExpName(), PARENT_SAMPLE_TYPE, PARENT_FIELD_CURLY_LEFT.getName());
899+
nameExpressionBad = String.format("SNP_${genId}_${%s/%s}_${materialInputs/%s/%s}", parentAlias, PARENT_FIELD_CURLY_RIGHT_INT.getExpName(), PARENT_SAMPLE_TYPE, PARENT_FIELD_CURLY_LEFT.getName());
900900
createPage.setNameExpression(nameExpressionBad);
901901
actualMsg = createPage.getNameExpressionPreview();
902902
checker().withScreenshot("Parent_Fields_Preview_Error")

src/org/labkey/test/tests/SampleTypeTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ public void testImportTypeOptions()
450450
String overlap = "Name1\tToBee\n";
451451
String newData = "Name2\tSee\n";
452452
setFormElement(Locator.name("text"), header + overlap + newData);
453-
clickButton("Submit", "duplicate key");
453+
clickButton("Submit", "already exists");
454454

455455
log("Switch to 'Insert and Replace'");
456456
importDataPage.setCopyPasteMerge(true);
@@ -475,7 +475,7 @@ public void testImportTypeOptions()
475475
importDataPage = drt.clickImportBulkData();
476476
importDataPage.setFile(sampleData);
477477
final String errorText = importDataPage.submitExpectingError();
478-
Assert.assertTrue("Wrong error when importing duplicate samples. " + errorText, errorText.contains("duplicate key"));
478+
Assert.assertTrue("Wrong error when importing duplicate samples. " + errorText, errorText.contains("already exists"));
479479
// TODO: Regression check for Issue 44202: Ugly error when data import fails due to duplicate key
480480
// Assert.assertTrue("Wrong error when importing duplicate samples. " + errorText, errorText.length() < 100);
481481

0 commit comments

Comments
 (0)