Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ function doTest()

if (contextPath.length > 0) {
var baseUrl = LABKEY.ActionURL.getBaseURL();
if (!baseUrl.endsWith(contextPath + "/"))
var suffix = contextPath + "/";
var idx = baseUrl.indexOf(suffix);
if (idx === -1 || idx !== baseUrl.length - suffix.length)
errors[errors.length] = new Error("ActionURL.getBaseURL() = " + baseUrl);
}

Expand Down
13 changes: 8 additions & 5 deletions src/org/labkey/test/LabKeySiteWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,14 @@ public void attemptSignIn(String email, String password)
setFormElement(Locator.id("email"), email);
setFormElement(Locator.id("password"), password);
WebElement signInButton = Locator.lkButton("Sign In").findElement(getDriver());
signInButton.click();
shortWait().until(ExpectedConditions.invisibilityOfElementLocated(Locator.byClass("signing-in-msg")));
shortWait().until(ExpectedConditions.or(
ExpectedConditions.stalenessOf(signInButton), // Successful login
ExpectedConditions.presenceOfElementLocated(Locators.labkeyError.withText()))); // Error during sign-in
doAndMaybeWaitForPageToLoad(10_000, () -> {
signInButton.click();
shortWait().until(ExpectedConditions.invisibilityOfElementLocated(Locator.byClass("signing-in-msg")));
shortWait().until(ExpectedConditions.or(
ExpectedConditions.stalenessOf(signInButton), // Successful login
ExpectedConditions.presenceOfElementLocated(Locators.labkeyError.withText()))); // Error during sign-in
return ExpectedConditions.stalenessOf(signInButton).apply(null);
});
}

public void signInShouldFail(String email, String password, String... expectedMessages)
Expand Down
1 change: 1 addition & 0 deletions src/org/labkey/test/Locators.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ private Locators() { }
public static final Locator.XPathLocator folderTab = Locator.tagWithClass("div", "lk-nav-tabs-ct").append(Locator.tagWithClass("ul", "lk-nav-tabs")).childTag("li");
public static final Locator.XPathLocator panelWebpartTitle = Locator.byClass("labkey-wp-title-text");
public static final Locator.XPathLocator folderTitle = Locator.tagWithClass("a", "lk-body-title-folder");
public static final Locator.XPathLocator loadingSpinner = Locator.byClass("fa-spinner");

public static Locator.XPathLocator headerContainer()
{
Expand Down
11 changes: 11 additions & 0 deletions src/org/labkey/test/TestScrubber.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.labkey.remoteapi.Connection;
import org.labkey.remoteapi.SimplePostCommand;
import org.labkey.test.components.html.Checkbox;
import org.labkey.test.components.pipeline.PipelineTriggerWizard;
import org.labkey.test.pages.core.admin.AllowedFileExtensionAdminPage;
import org.labkey.test.pages.core.admin.BaseSettingsPage;
import org.labkey.test.pages.core.admin.ConfigureFileSystemAccessPage;
Expand Down Expand Up @@ -160,6 +161,16 @@ public void cleanSiteSettings()
TestLogger.error("Failed to reset site look and feel properties after test.", e);
}

try
{
// Disable all pipeline triggers so that they don't show up as memory leaks in subsequent tests.
PipelineTriggerWizard.disableAllPipelineTriggers(createDefaultConnection());
}
catch (Exception e)
{
TestLogger.error("Failed to disable pipeline triggers after test", e);
}

}

@LogMethod(quiet = true)
Expand Down
39 changes: 36 additions & 3 deletions src/org/labkey/test/WebDriverWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -3000,14 +3000,17 @@ public void selectFolderTreeItem(String folderName)

/**
* Move mouse to the upper left corner of the document to dismiss tooltips and the like
* Will scroll page if necessary
*/
public void mouseOut()
{
try
{
scrollToTop();
new Actions(getDriver()).moveToLocation(0, 0).perform();
new Actions(getDriver())
.moveToLocation(0, 0)
// Add a little wiggle to make sure tooltips notice
.moveByOffset(4, 4)
.moveByOffset(-2, -2)
.perform();
}
catch (WebDriverException ignore) { }
}
Expand Down Expand Up @@ -3709,6 +3712,9 @@ private void setHtml5Input(WebElement input, String inputType, String value)
case "date":
setHtml5DateInput(input, value);
break;
case "datetime-local":
setHtml5DateTimeInput(input, value);
break;
case "password":
case "search":
setInput(input, value); // These don't require special handling, don't output warning
Expand Down Expand Up @@ -3737,6 +3743,21 @@ private void setHtml5DateInput(WebElement el, String text)
}
}

private void setHtml5DateTimeInput(WebElement el, String text)
{
String inputFormat = "yyyy-MM-dd'T'HH:mm";
SimpleDateFormat inputFormatter = new SimpleDateFormat(inputFormat);

try
{
setHtml5DateTimeInput(el, inputFormatter.parse(text));
}
catch (ParseException e)
{
throw new IllegalArgumentException("Unable to parse date " + text + ". Format should be " + inputFormat);
}
}

private void setHtml5DateInput(WebElement el, Date date)
{
// Firefox requires ISO date format (yyyy-MM-dd)
Expand All @@ -3749,6 +3770,18 @@ private void setHtml5DateInput(WebElement el, Date date)
el.sendKeys(formDate);
}

private void setHtml5DateTimeInput(WebElement el, Date date)
{
// Firefox and Chrome want different formats, neither of which align with all online guidance to use ISO-style
String formFormat = isFirefox() ? "MMddyyyy hh:mm a" : "MM-dd-yyyy'\t'hh:mma";
SimpleDateFormat formFormatter = new SimpleDateFormat(formFormat);
String formDate = formFormatter.format(date);

fireEvent(el, SeleniumEvent.focus);
executeScript("arguments[0].value = ''", el);
el.sendKeys(formDate);
}

private void setHtml5NumberInput(WebElement el, String text)
{

Expand Down
2 changes: 1 addition & 1 deletion src/org/labkey/test/components/domain/DomainDesigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class ElementCache extends BaseDomainDesigner<?>.ElementCache
protected final DomainPanel<?, ?> propertiesPanel = new DomainPanel.DomainPanelFinder(getDriver()).index(0)
.timeout(WAIT_FOR_JAVASCRIPT).findWhenNeeded(this);
protected final DomainFormPanel fieldsPanel = new DomainFormPanel.DomainFormPanelFinder(getDriver())
.index(getFieldPanelIndex()).timeout(1000).findWhenNeeded();
.index(getFieldPanelIndex()).timeout(2_000).findWhenNeeded();

protected int getFieldPanelIndex()
{
Expand Down
2 changes: 1 addition & 1 deletion src/org/labkey/test/components/domain/DomainFormPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private DomainFormPanel(WebElement element, WebDriver driver)
@Override
protected void waitForReady()
{
waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 2_000);
waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 10_000);
}

public static List<AdvancedFieldSetting> advancedSettingsFromFieldDefinition(FieldDefinition def)
Expand Down
19 changes: 19 additions & 0 deletions src/org/labkey/test/components/pipeline/PipelineTriggerWizard.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
package org.labkey.test.components.pipeline;

import org.jetbrains.annotations.NotNull;
import org.labkey.remoteapi.CommandException;
import org.labkey.remoteapi.Connection;
import org.labkey.remoteapi.query.ContainerFilter;
import org.labkey.remoteapi.query.Filter;
import org.labkey.test.Locator;
import org.labkey.test.WebDriverWrapper;
import org.labkey.test.WebTestHelper;
Expand All @@ -24,11 +28,14 @@
import org.labkey.test.components.html.Checkbox;
import org.labkey.test.components.html.Input;
import org.labkey.test.components.html.OptionSelect;
import org.labkey.test.util.query.QueryApiHelper;
import org.openqa.selenium.Alert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertTrue;
Expand Down Expand Up @@ -61,6 +68,18 @@ public static PipelineTriggerWizard beginAt(WebDriverWrapper driver, String cont
return new PipelineTriggerWizard(driver.getDriver());
}

public static void disableAllPipelineTriggers(Connection connection) throws IOException, CommandException
{
QueryApiHelper queryApiHelper = new QueryApiHelper(connection, "/", "pipeline", "TriggerConfigurations");
List<Map<String, Object>> triggers = queryApiHelper.selectRows(List.of("rowId", "enabled"),
List.of(new Filter("enabled", true)), List.of(), ContainerFilter.AllFolders).getRows();
if (!triggers.isEmpty())
{
triggers.forEach(trigger -> trigger.put("enabled", false));
queryApiHelper.updateRows(triggers);
}
}

@Override
public WebElement getComponentElement()
{
Expand Down
34 changes: 25 additions & 9 deletions src/org/labkey/test/components/react/QueryChartPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import org.labkey.test.Locator;
import org.labkey.test.components.Component;
import org.labkey.test.components.WebDriverComponent;
import org.labkey.test.components.html.BootstrapMenu;
import org.labkey.test.components.ui.grids.QueryGrid;
import org.labkey.test.components.ui.grids.ResponsiveGrid;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
Expand Down Expand Up @@ -38,11 +38,7 @@ public QueryChartDialog clickEdit()

public File clickExport(String subMenuText)
{
elementCache().exportMenu.expand();
return getWrapper().doAndWaitForDownload(() ->
Locator.tagWithClass("li", "lk-menu-item")
.descendant(Locator.tagContainingText("a", subMenuText))
.findElement(elementCache().headingEl).click());
return getWrapper().doAndWaitForDownload(() -> elementCache().exportMenu.doMenuAction(subMenuText));
}

public String getTitle()
Expand All @@ -55,13 +51,27 @@ public WebElement getSvgChart()
return Locator.byClass("svg-chart__chart").childTag("svg").waitForElement(this, WAIT_FOR_JAVASCRIPT);
}

public QueryGrid clickClose()
public ResponsiveGrid<?> getCurveStatsGrid()
{
return new ResponsiveGrid.ResponsiveGridFinder(getDriver()).waitFor(elementCache().curveStatsPanel);
}

public boolean isCurveStatsPanelPresent()
{
return ElementCache.curveStatsPanelLoc.findOptionalElement(this).isPresent();
}

public File exportCurveStats(String type)
{
return getWrapper().doAndWaitForDownload(() -> elementCache().exportStatsMenu.doMenuAction(type));
}

public void clickClose()
{
var btn = elementCache().closeButton;
getWrapper().shortWait().until(ExpectedConditions.elementToBeClickable(btn));
btn.click();
getWrapper().shortWait().until(ExpectedConditions.stalenessOf(btn));
return _queryGrid;
}

@Override
Expand All @@ -88,11 +98,17 @@ protected class ElementCache extends Component<?>.ElementCache
.findWhenNeeded(this).withTimeout(2000);
public final WebElement editButton = Locator.tagWithAttribute("button", "title", "Edit chart")
.findWhenNeeded(headingEl);
public final BootstrapMenu exportMenu = new MultiMenu.MultiMenuFinder(getDriver()).withButtonClass("chart-panel-export-btn").findWhenNeeded(headingEl);
public final MultiMenu exportMenu = new MultiMenu.MultiMenuFinder(getDriver())
.withButtonClass("chart-panel-export-btn")
.findWhenNeeded(headingEl);
public final WebElement closeButton = Locator.tagWithAttribute("button", "title", "Hide chart")
.findWhenNeeded(headingEl);
public final WebElement titleElement= Locator.tagWithClass("div", "chart-panel__heading-title")
.findWhenNeeded(headingEl);
public static final Locator curveStatsPanelLoc = Locator.byClass("curve-fit-statistics");
public final WebElement curveStatsPanel = curveStatsPanelLoc.findWhenNeeded(this).withTimeout(WAIT_FOR_JAVASCRIPT);
public final WebElement curveStatsHeader = Locator.byClass("curve-fit-statistics__header").findWhenNeeded(curveStatsPanel);
public final MultiMenu exportStatsMenu = new MultiMenu.MultiMenuFinder(getDriver()).findWhenNeeded(curveStatsHeader);
}


Expand Down
4 changes: 4 additions & 0 deletions src/org/labkey/test/components/ui/grids/EditableGrid.java
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,8 @@ public String getErrorPopoverText(int row, CharSequence columnIdentifier)
*/
public String getCellPopoverText(int row, CharSequence columnIdentifier)
{
dismissPopover(); // Other popovers can block the target cell
getWrapper().mouseOver(Locator.tag("td").findElement(getRow(row))); // Avoid passing over any header cells on the way to the target cell
WebElement cellDiv = Locator.tagWithClass("div", "cellular-display").findElement(getCell(row, columnIdentifier));
getWrapper().mouseOver(cellDiv); // cause the tooltip to be present
return Optional.ofNullable(WebDriverWrapper.waitFor(()-> Locators.popover.findElementOrNull(getDriver()), 1000))
Expand All @@ -1170,7 +1172,9 @@ public String getCellPopoverText(int row, CharSequence columnIdentifier)
public void dismissPopover()
{
Locators.popover.findOptionalElement(getDriver()).ifPresent(popover -> {
getWrapper().mouseOver(popover);
getWrapper().mouseOut();
getWrapper().mouseOver(elementCache().getGridHeaderManager().getColumnHeader(0).getElement());
getWrapper().shortWait().until(ExpectedConditions.invisibilityOf(popover));
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ public FieldSelectionDialog removeAllSelectedFields()
continue;
}

getWrapper().mouseOver(removeIcon);
removeIcon.click();
}

Expand Down
35 changes: 20 additions & 15 deletions src/org/labkey/test/components/ui/grids/QueryGrid.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,18 +221,18 @@ public QueryGrid waitForRecordCount(int expectedCount, int milliseconds)
@Override
public void doAndWaitForUpdate(Runnable func)
{
waitForLoaded();
Optional<WebElement> optionalStatus = elementCache().selectionStatusContainerLoc.findOptionalElement(elementCache());
super.doAndWaitForUpdate(() ->
{
WebElement status = hasSelectColumn() ? Locators.selectionStatusContainerLoc.waitForElement(this, 5_000) : null;

func.run();
func.run();

optionalStatus.ifPresent(el -> {
getWrapper().shortWait().until(ExpectedConditions.stalenessOf(el));
elementCache().selectionStatusContainerLoc.waitForElement(this, 5_000);
if (status != null)
{
getWrapper().shortWait().until(ExpectedConditions.stalenessOf(status));
Locators.selectionStatusContainerLoc.waitForElement(this, 5_000);
}
});

waitForLoaded();
clearElementCache();
}


Expand Down Expand Up @@ -789,19 +789,24 @@ protected ElementCache elementCache()
return (ElementCache) super.elementCache();
}

protected static class Locators
{
static final Locator.XPathLocator selectionStatusContainerLoc = Locator.byClass("selection-status");
}

protected class ElementCache extends ResponsiveGrid<QueryGrid>.ElementCache
{

final GridBar gridBar = new GridBar.GridBarFinder().findWhenNeeded(QueryGrid.this);

WebElement saveViewButton = Locator.button("Save").findWhenNeeded(getDriver());
final WebElement saveViewButton = Locator.button("Save").findWhenNeeded(getDriver());

final BootstrapMenu viewMenu = new MultiMenu.MultiMenuFinder(getDriver()).withText("Views").findWhenNeeded(this);

final Locator.XPathLocator selectionStatusContainerLoc = Locator.tagWithClass("div", "selection-status");
final Locator selectAllBtnLoc = selectionStatusContainerLoc.append(Locator.tagWithClass("span", "selection-status__select-all")
.child(Locator.buttonContainingText("Select")));
final Locator clearBtnLoc = selectionStatusContainerLoc.append(Locator.byClass("selection-status__clear-all")
.child(Locator.tag("button")));
final Locator selectAllBtnLoc = Locators.selectionStatusContainerLoc.append(Locator.byClass("selection-status__select-all")
.childTag("button"));
final Locator clearBtnLoc = Locators.selectionStatusContainerLoc.append(Locator.byClass("selection-status__clear-all")
.childTag("button"));

final WebElement filterStatusPanel = Locator.css("div.grid-panel__filter-status").findWhenNeeded(this);

Expand Down
14 changes: 11 additions & 3 deletions src/org/labkey/test/components/ui/grids/ResponsiveGrid.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ public Boolean isLoaded()
return getComponentElement().isDisplayed() &&
!Locators.loadingGrid.existsIn(this) &&
!Locators.spinner.existsIn(this) &&
(Locator.tag("td").existsIn(this) ||
getGridEmptyMessage().isPresent());
(Locator.tag("td").existsIn(this) ||
getGridEmptyMessage().isPresent());
}

protected void waitForLoaded()
Expand All @@ -82,6 +82,8 @@ protected void waitForLoaded()
@Override
public void doAndWaitForUpdate(Runnable func)
{
waitForLoaded();

// Look at WebDriverWrapper.doAndWaitForElementToRefresh for an example.
func.run();

Expand Down Expand Up @@ -838,7 +840,7 @@ protected boolean hasSelectColumn()
return hasSelectColumn;
}

ReactCheckBox selectAllCheckbox = new ReactCheckBox(Locator.xpath("//th/input[@type='checkbox']").findWhenNeeded(this))
final ReactCheckBox selectAllCheckbox = new ReactCheckBox(Locator.xpath("//th/input[@type='checkbox']").findWhenNeeded(this))
{
@Override
public void toggle()
Expand Down Expand Up @@ -995,6 +997,12 @@ public ResponsiveGridFinder inParentWithId(String id)
return this;
}

public ResponsiveGridFinder inParentWithClass(String className)
{
_locator = Locator.byClass(className).child(Locators.responsiveGrid());
return this;
}

public ResponsiveGridFinder withGridId(String id)
{
_locator = Locators.responsiveGrid(id);
Expand Down
Loading
Loading