Skip to content

Commit 044fafc

Browse files
Merge branch 'develop' into fb_raceConditionKillingConnection
2 parents 381d57c + 36cdfd7 commit 044fafc

21 files changed

Lines changed: 965 additions & 134 deletions

src/org/labkey/test/BaseWebDriverTest.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2477,12 +2477,6 @@ public void waitForComplete()
24772477
}
24782478
}
24792479

2480-
public void ensureSignedOut()
2481-
{
2482-
if(isElementPresent(Locator.id("userMenuPopupLink")))
2483-
signOut();
2484-
}
2485-
24862480
protected void reloadStudyFromZip(File folderArchive)
24872481
{
24882482
reloadStudyFromZip(folderArchive, true, 2);

src/org/labkey/test/LabKeySiteWrapper.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,20 @@ public void attemptSignIn(String email, String password)
416416
public void signInShouldFail(String email, String password, String... expectedMessages)
417417
{
418418
attemptSignIn(email, password);
419-
String errorText = waitForElement(Locator.id("errors").withText()).getText();
420-
assertElementPresent(Locator.tagWithName("form", "login"));
419+
420+
WebElement element = waitForElement(Locator.tagWithClass("div", "auth-header"));
421+
final String errorText;
422+
423+
// Could be on the "Sign In" page or the "Change Password" page
424+
if (element.getText().equals("Sign In"))
425+
{
426+
errorText = getText(Locator.id("errors"));
427+
}
428+
else
429+
{
430+
assertEquals("Change Password", element.getText());
431+
errorText = getText(Locator.tagWithClass("div", "auth-form-body").childTag("p"));
432+
}
421433

422434
List<String> missingErrors = getMissingTexts(new TextSearcher(errorText), expectedMessages);
423435
assertTrue(String.format("Wrong errors.\nExpected: ['%s']\nActual: '%s'", String.join("',\n'", expectedMessages), errorText), missingErrors.isEmpty());

src/org/labkey/test/TestProperties.java

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.apache.commons.lang3.StringUtils;
1919
import org.apache.commons.lang3.SystemUtils;
2020
import org.labkey.serverapi.reader.Readers;
21+
import org.labkey.test.util.CspLogUtil;
2122
import org.labkey.test.util.TestLogger;
2223
import org.openqa.selenium.Dimension;
2324

@@ -30,6 +31,7 @@
3031
import java.time.LocalDateTime;
3132
import java.time.ZoneId;
3233
import java.util.Arrays;
34+
import java.util.Collections;
3335
import java.util.HashMap;
3436
import java.util.Iterator;
3537
import java.util.List;
@@ -78,6 +80,7 @@ public abstract class TestProperties
7880
public static void load()
7981
{
8082
/* Force static block to run */
83+
CspLogUtil.init();
8184
}
8285

8386
public static boolean isTestCleanupSkipped()
@@ -129,7 +132,7 @@ public static boolean isQueryCheckSkipped()
129132

130133
public static boolean isCspCheckSkipped()
131134
{
132-
return !getBooleanProperty("webtest.cspCheck", false);
135+
return !getBooleanProperty("webtest.cspCheck", true);
133136
}
134137

135138
public static boolean isNewWebDriverForEachTest()
@@ -264,6 +267,10 @@ public static boolean isAssayProductFeatureAvailable()
264267
return isProductFeatureAvailable("assay");
265268
}
266269

270+
/**
271+
* Product features are assumed to be available unless the test environment (usually TeamCity) explicitly specifies
272+
* that it is not.
273+
*/
267274
public static boolean isProductFeatureAvailable(String feature)
268275
{
269276
return "true".equals(System.getProperty("webtest.productFeature." + feature.toLowerCase(), "true"));
@@ -276,7 +283,7 @@ public static boolean ignoreDatabaseNotSupportedException()
276283

277284
/**
278285
* Parses system property 'webtest.server.startup.timeout' to determine maximum allowed server startup time.
279-
* If property is not defined or is not an integer, it defaults to 60 seconds.
286+
* If property is not defined or is not an integer, it defaults to 120 seconds.
280287
* @return Maximum number of seconds to wait for server startup
281288
*/
282289
public static int getServerStartupTimeout()
@@ -289,34 +296,48 @@ public static String getAdditionalPipelineTools()
289296
return System.getProperty("additional.pipeline.tools");
290297
}
291298

299+
private static Map<String, Boolean> _optionalFeatures = null;
292300
public static Map<String, Boolean> getOptionalFeatures()
293301
{
294-
Map<String, Boolean> features = new HashMap<>();
295-
296-
Properties props = System.getProperties();
297-
for (Map.Entry<Object, Object> entry : props.entrySet())
302+
if (_optionalFeatures == null)
298303
{
299-
String key = String.valueOf(entry.getKey());
300-
Boolean value = (entry.getValue() instanceof Boolean)
301-
? (Boolean)entry.getValue()
302-
: Boolean.valueOf(String.valueOf(entry.getValue()));
304+
Map<String, Boolean> features = new HashMap<>();
303305

304-
String prefix = "webtest.experimental."; // Can be used with any optional feature flags; "experimental" is used for backward-compatibility purposes.
305-
if (key.startsWith(prefix))
306+
Properties props = System.getProperties();
307+
for (Map.Entry<Object, Object> entry : props.entrySet())
306308
{
307-
String feature = key.substring(prefix.length());
308-
features.put(feature, value);
309+
String key = String.valueOf(entry.getKey());
310+
311+
String expPrefix = "webtest.experimental."; // "experimental" is accepted for backward-compatibility purposes.
312+
String optPrefix = "webtest.optional."; // Preferred prefix
313+
for (String prefix : List.of(expPrefix, optPrefix))
314+
{
315+
if (key.startsWith(prefix))
316+
{
317+
Boolean value = (entry.getValue() instanceof Boolean)
318+
? (Boolean) entry.getValue()
319+
: Boolean.valueOf(String.valueOf(entry.getValue()));
320+
String feature = key.substring(prefix.length());
321+
features.put(feature, value);
322+
}
323+
}
309324
}
310-
}
311325

312-
return features;
326+
_optionalFeatures = Collections.unmodifiableMap(features);
327+
}
328+
return _optionalFeatures;
313329
}
314330

331+
private static List<String> debugLoggingPackages = null;
315332
public static List<String> getDebugLoggingPackages()
316333
{
317-
String prop = System.getProperty("webtest.debug.server.packages", "");
318-
String[] packages = prop.split("\\s*,\\s*");
319-
return Arrays.stream(packages).map(String::trim).filter(s -> !s.isEmpty()).collect(Collectors.toList());
334+
if (debugLoggingPackages == null)
335+
{
336+
String prop = System.getProperty("webtest.debug.server.packages", "");
337+
String[] packages = prop.split(",");
338+
debugLoggingPackages = Arrays.stream(packages).map(String::trim).filter(s -> !s.isEmpty()).collect(Collectors.toList());
339+
}
340+
return debugLoggingPackages;
320341
}
321342

322343
private static File dumpDir = null;

src/org/labkey/test/WebDriverWrapper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,6 +2738,12 @@ public String getSelectedOptionText(WebElement el)
27382738
return select.getFirstSelectedOption().getText();
27392739
}
27402740

2741+
public List<String> getSelectedOptionTexts(Locator loc)
2742+
{
2743+
Select select = new Select(loc.findElement(getDriver()));
2744+
return select.getAllSelectedOptions().stream().map(WebElement::getText).collect(Collectors.toList());
2745+
}
2746+
27412747
public String getSelectedOptionValue(Locator loc)
27422748
{
27432749
return getSelectedOptionValue(loc.findElement(getDriver()));

src/org/labkey/test/components/html/Table.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ public int getRowCount()
8888
return elementCache().getRows().size();
8989
}
9090

91+
public List<WebElement> getRows()
92+
{
93+
return elementCache().getRows();
94+
}
95+
9196
/**
9297
* For a well formed html table get the text from the th elements in the thead.
9398
*

src/org/labkey/test/pages/admin/ExternalSourcesPage.java

Lines changed: 114 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.labkey.test.pages.admin;
22

3+
import org.junit.Assert;
34
import org.labkey.test.Locator;
5+
import org.labkey.test.Locators;
46
import org.labkey.test.WebDriverWrapper;
57
import org.labkey.test.WebTestHelper;
68
import org.labkey.test.components.html.Input;
@@ -9,18 +11,17 @@
911
import org.labkey.test.pages.LabKeyPage;
1012
import org.labkey.test.util.LogMethod;
1113
import org.labkey.test.util.LoggedParam;
14+
import org.labkey.test.util.core.admin.CspConfigHelper.AllowedHost;
15+
import org.openqa.selenium.NotFoundException;
1216
import org.openqa.selenium.WebDriver;
1317
import org.openqa.selenium.WebElement;
1418

1519
import java.util.ArrayList;
16-
import java.util.Collections;
17-
import java.util.HashMap;
1820
import java.util.List;
19-
import java.util.Map;
2021
import java.util.stream.Stream;
2122

2223
/**
23-
* Wraps `AdminController.ExternalSourceAction`
24+
* Wraps `AdminController.ExternalSourcesAction`
2425
*/
2526
public class ExternalSourcesPage extends LabKeyPage<ExternalSourcesPage.ElementCache>
2627
{
@@ -38,7 +39,7 @@ public static ExternalSourcesPage beginAt(WebDriverWrapper webDriverWrapper)
3839
@LogMethod
3940
public ExternalSourcesPage ensureHost(Directive directive, String host)
4041
{
41-
if (!getExistingHosts().getOrDefault(directive, Collections.emptyList()).contains(host))
42+
if (!getExistingHosts().contains(new AllowedHost(directive, host)))
4243
{
4344
return addHost(directive, host);
4445
}
@@ -57,34 +58,73 @@ public ExternalSourcesPage addHost(@LoggedParam Directive directive, @LoggedPara
5758

5859
clickAndWait(elementCache().addButton);
5960
clearCache();
61+
assertNoLabKeyErrors();
6062
return this;
6163
}
6264

63-
public Map<Directive, List<String>> getExistingHosts()
65+
@LogMethod (quiet = true)
66+
public List<String> addHostExpectingError(@LoggedParam Directive directive, @LoggedParam String host)
6467
{
65-
Map<Directive, List<String>> existingHosts = new HashMap<>();
68+
elementCache().directiveSelect.selectOption(directive);
69+
elementCache().hostInput.set(host);
6670

67-
for (Map.Entry<Directive, List<Input>> entry : getExistingHostInputs().entrySet())
68-
{
69-
existingHosts.put(entry.getKey(), entry.getValue().stream().map(Input::getValue).toList());
70-
}
71+
clickAndWait(elementCache().addButton);
72+
clearCache();
7173

72-
return existingHosts;
74+
List<WebElement> errorEls = Locators.labkeyError.findElements(elementCache());
75+
Assert.assertFalse("No errors found", errorEls.isEmpty());
76+
return getTexts(errorEls);
7377
}
7478

75-
public Map<Directive, List<Input>> getExistingHostInputs()
79+
public ExternalSourcesPage editHost(Directive directive, String host, String newHost)
80+
{
81+
getExistingSourceRow(directive, host).getHostInput().set(newHost);
82+
return this;
83+
}
84+
85+
public ExternalSourcesPage deleteHost(Directive directive, String host)
86+
{
87+
getExistingSourceRow(directive, host).clickDelete();
88+
return this;
89+
}
90+
91+
private ExistingSourceRow getExistingSourceRow(Directive directive, String host)
92+
{
93+
return elementCache().getExistingSourceRows().stream()
94+
.filter(row -> row.getDirective().equals(directive) && row.getHost().equalsIgnoreCase(host))
95+
.findFirst().orElseThrow(() -> new NotFoundException("Host " + host + " does not exist for directive " + directive));
96+
}
97+
98+
@LogMethod (quiet = true)
99+
public ExternalSourcesPage saveChanges()
100+
{
101+
clickAndWait(elementCache().saveButton);
102+
clearCache();
103+
104+
assertNoLabKeyErrors();
105+
return this;
106+
}
107+
108+
@LogMethod (quiet = true)
109+
public List<String> saveChangesExpectingError()
76110
{
77-
List<WebElement> directiveColumn = elementCache().existingValuesTable.getColumnAsElement(1);
78-
List<WebElement> hostsColumn = elementCache().existingValuesTable.getColumnAsElement(2);
111+
clickAndWait(elementCache().saveButton);
112+
clearCache();
113+
114+
List<WebElement> errorEls = Locators.labkeyError.findElements(elementCache());
115+
Assert.assertFalse("No errors found", errorEls.isEmpty());
116+
return getTexts(errorEls);
117+
}
79118

80-
Map<Directive, List<Input>> existingHosts = new HashMap<>();
119+
public List<AllowedHost> getExistingHosts()
120+
{
121+
List<AllowedHost> existingHosts = new ArrayList<>();
81122

82-
for (int i = 0; i < hostsColumn.size(); i++)
123+
for (ExistingSourceRow row : elementCache().getExistingSourceRows())
83124
{
84-
WebElement directiveInput = Locator.tag("input").findElement(directiveColumn.get(i));
85-
Directive directive = Directive.valueOf(directiveInput.getDomAttribute("data-directive"));
86-
Input hostInput = Input.Input(Locator.tag("input"), getDriver()).find(hostsColumn.get(i));
87-
existingHosts.computeIfAbsent(directive, d -> new ArrayList<>()).add(hostInput);
125+
Directive directive = row.getDirective();
126+
String host = row.getHost();
127+
existingHosts.add(new AllowedHost(directive, host));
88128
}
89129

90130
return existingHosts;
@@ -105,6 +145,59 @@ protected class ElementCache extends LabKeyPage<ElementCache>.ElementCache
105145

106146
final WebElement existingValuesForm = Locator.name("existingValues").findWhenNeeded(this);
107147
final Table existingValuesTable = new Table(getDriver(), Locator.byClass("labkey-data-region-legacy").findWhenNeeded(existingValuesForm), 0);
148+
private List<ExistingSourceRow> existingSourceRows;
149+
150+
protected List<ExistingSourceRow> getExistingSourceRows()
151+
{
152+
if (existingSourceRows == null)
153+
{
154+
existingSourceRows = existingValuesTable.getRows().stream().map(ExistingSourceRow::new).toList();
155+
}
156+
return existingSourceRows;
157+
}
158+
159+
final WebElement saveButton = Locator.lkButton("Save").findWhenNeeded(existingValuesForm);
160+
}
161+
162+
protected class ExistingSourceRow
163+
{
164+
private final WebElement directiveInput;
165+
private final Input hostInput;
166+
private final WebElement deleteButton;
167+
168+
private Directive directive;
169+
170+
ExistingSourceRow(WebElement row)
171+
{
172+
directiveInput = Locator.tag("input").withAttributeContaining("name", "directive").findWhenNeeded(row);
173+
hostInput = Input.Input(Locator.tag("input").withAttributeContaining("name", "host"), getDriver()).findWhenNeeded(row);
174+
deleteButton = Locator.lkButton("Delete").findWhenNeeded(row);
175+
}
176+
177+
Directive getDirective()
178+
{
179+
if (directive == null)
180+
{
181+
directive = Directive.valueOf(directiveInput.getDomAttribute("data-directive"));
182+
}
183+
return directive;
184+
}
185+
186+
public Input getHostInput()
187+
{
188+
return hostInput;
189+
}
190+
191+
public String getHost()
192+
{
193+
return hostInput.get();
194+
}
195+
196+
public void clickDelete()
197+
{
198+
clickAndWait(deleteButton);
199+
clearCache();
200+
}
108201
}
109202

110203
public enum Directive implements OptionSelect.SelectOption

0 commit comments

Comments
 (0)