Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into fb_stressSampleFinder
Browse files Browse the repository at this point in the history
  • Loading branch information
labkey-tchad committed Feb 3, 2025
2 parents 22c8c5b + c8db5b2 commit 1556c52
Show file tree
Hide file tree
Showing 15 changed files with 424 additions and 142 deletions.
5 changes: 5 additions & 0 deletions src/org/labkey/test/BaseWebDriverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ public abstract class BaseWebDriverTest extends LabKeySiteWrapper implements Cle

public static final double DELTA = 10E-10;

// See QueryKey.ILLEGAL TODO make that array public so it can be used here
public static final String[] ILLEGAL_QUERY_KEY_CHARACTERS = {"$", "/", "&", "}", "~", ",", "."};
// 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
public static final String[] TRICKY_IMPORT_FIELD_CHARACTERS = {"\\", "\"", "\\t", ",", "\\n", "\\r"};

public static final String TRICKY_CHARACTERS = "><&/%\\' \"1\u00E4\u00F6\u00FC\u00C5";
public static final String TRICKY_CHARACTERS_NO_QUOTES = "></% 1\u00E4\u00F6\u00FC\u00C5";
public static final String TRICKY_CHARACTERS_FOR_PROJECT_NAMES = "\u2603~!@$&()_+{}-=[],.#\u00E4\u00F6\u00FC\u00C5"; // No slash or space
Expand Down
4 changes: 4 additions & 0 deletions src/org/labkey/test/TestFileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ public abstract class TestFileUtils
{
private static final Logger LOG = LogManager.getLogger(TestFileUtils.class);

private static final char _chQuote = '"';
// here we quote for both tab and comma, even though
private static final String _escapedCharsString = "\r\n\\" + _chQuote;

private static File _labkeyRoot = null;
private static File _buildDir = null;
private static File _testRoot = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.labkey.test.components.ui.entities;

import org.jetbrains.annotations.Nullable;
import org.labkey.test.BootstrapLocators;
import org.labkey.test.Locator;
import org.labkey.test.WebDriverWrapper;
Expand Down Expand Up @@ -221,20 +222,24 @@ public String getFieldWithId(String id)
* object to use the picker to set the field. If a text value is passed in it is used as a literal and jut typed
* into the textbox.
*
* @param fieldKey Field to update.
* @param fieldName Field to update.
* @param dateTime A LocalDateTime, LocalDate, LocalTime or String.
* @return A reference to this page.
*/
public EntityBulkInsertDialog setDateTimeField(String fieldKey, Object dateTime)
public EntityBulkInsertDialog setDateTimeField(String fieldName, Object dateTime)
{
ReactDateTimePicker dateTimePicker = elementCache().dateInput(fieldKey);
return setDateTimeField(fieldName, dateTime, null);
}
public EntityBulkInsertDialog setDateTimeField(String fieldName, Object dateTime, @Nullable String fieldKey)
{
ReactDateTimePicker dateTimePicker = elementCache().dateInput(fieldName, fieldKey);
dateTimePicker.select(dateTime);
return this;
}

public String getDateTimeField(String fieldKey)
public String getDateTimeField(String fieldName, @Nullable String fieldKey)
{
return elementCache().dateInput(fieldKey).get();
return elementCache().dateInput(fieldName, fieldKey).get();
}

public EntityBulkInsertDialog setBooleanField(String fieldKey, boolean checked)
Expand Down Expand Up @@ -381,10 +386,13 @@ public Input numericInput(String fieldKey)
return new Input(inputEl, getDriver());
}

public ReactDateTimePicker dateInput(String fieldKey)
public ReactDateTimePicker dateInput(String fieldName, @Nullable String fieldKey)
{
if (fieldKey == null)
fieldKey = fieldName;

return new ReactDateTimePicker.ReactDateTimeInputFinder(getDriver())
.withInputId(fieldKey).find(formRow(fieldKey));
.withInputId(fieldKey).find(formRow(fieldName));
}

public List<WebElement> fieldNames()
Expand Down
18 changes: 9 additions & 9 deletions src/org/labkey/test/components/ui/grids/DetailTableEdit.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,16 @@ public DetailTableEdit setTextareaByFieldName(String fieldName, String value)
/**
* Get the value of a boolean field.
*
* @param fieldCaption The caption/label of the field to get.
* @param inputName The caption/label of the field to get.
* @return The value of the field.
**/
public boolean getBooleanField(String fieldCaption)
public boolean getBooleanField(String inputName)
{
// The text used in the field caption and the value of the name attribute in the checkbox don't always have the same case.
WebElement editableElement = Locator.tagWithAttributeIgnoreCase("input", "name", fieldCaption).findElement(getComponentElement());
WebElement editableElement = Locator.tagWithAttributeIgnoreCase("input", "name", inputName).findElement(getComponentElement());
String elementType = editableElement.getAttribute("type").toLowerCase().trim();

Assert.assertEquals(String.format("Field '%s' is not a checkbox. Cannot be get true/false value.", fieldCaption), "checkbox", elementType);
Assert.assertEquals(String.format("Field '%s' is not a checkbox. Cannot be get true/false value.", inputName), "checkbox", elementType);

return new Checkbox(editableElement).isChecked();
}
Expand All @@ -200,9 +200,9 @@ public DetailTableEdit setBooleanField(String fieldCaption, boolean value)

WebElement fieldValueElement = elementCache().fieldValue(fieldCaption);
Assert.assertTrue(String.format("Field '%s' is not editable and cannot be set.", fieldCaption), isEditableField(fieldValueElement));
getWrapper().scrollIntoView(fieldValueElement);

// The text used in the field caption and the value of the name attribute in the checkbox don't always have the same case.
WebElement editableElement = fieldValueElement.findElement(Locator.tagWithAttributeIgnoreCase("input", "name", fieldCaption));
WebElement editableElement = fieldValueElement.findElement(By.xpath("./div/div/input"));
String elementType = editableElement.getAttribute("type").toLowerCase().trim();

Assert.assertEquals(String.format("Field '%s' is not a checkbox. Cannot be set to true/false.", fieldCaption), "checkbox", elementType);
Expand Down Expand Up @@ -357,17 +357,17 @@ public DetailTableEdit clearSelectValue(String fieldCaption, boolean waitForSele

/**
* Set a DateTime, Date or Time field.
* @param fieldCaption The caption of the field to set.
* @param fieldKey The encoded fieldKey of the field to set.
* @param dateTime Will be used to determine what kind of field is being set and how to set it. If the parameter
* is a LocalDateTime object then it is assumed that field is a DateTime field. If the parameter is
* a LocalDate object then it is assumed to be a date-only field. And I think you can guess what
* happens with a LocalTime object type. If the type is a string it is used as a literal value that
* is typed into the field (no picker is used).
* @return A reference to this DetailTableEdit object.
*/
public DetailTableEdit setDateTimeField(String fieldCaption, Object dateTime)
public DetailTableEdit setDateTimeField(String fieldKey, Object dateTime)
{
ReactDateTimePicker dateTimePicker = getDateTimePicker(fieldCaption);
ReactDateTimePicker dateTimePicker = getDateTimePicker(fieldKey);
if(dateTime instanceof LocalDateTime localDateTime)
{
dateTimePicker.select(localDateTime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.labkey.test.components.UpdatingComponent;
import org.labkey.test.components.bootstrap.ModalDialog;
import org.labkey.test.components.html.Checkbox;
import org.labkey.test.util.EscapeUtil;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
Expand Down Expand Up @@ -163,9 +162,7 @@ private String expandAvailableFields(String... fields)

while(iterator.hasNext())
{
String field = iterator.next().trim();
String fieldEncoded = field.contains("$D") ? field : EscapeUtil.fieldKeyEncodePart(field);
fieldKey.append(fieldEncoded);
fieldKey.append(iterator.next().trim());

// If this isn't the last item in the collection keep expanding and building the expected data-fieldkey value.
if(iterator.hasNext())
Expand Down Expand Up @@ -393,6 +390,8 @@ public FieldSelectionDialog removeAllSelectedFields()

for (WebElement listItem : allItems)
{
getWrapper().log(String.format("Removing field '%s' from selected fields.", listItem.getText()));

WebElement removeIcon = Locator.tagWithClass("span", "view-field__action").findWhenNeeded(listItem);

// In some usages there may be fields that are not removable.
Expand All @@ -407,7 +406,10 @@ public FieldSelectionDialog removeAllSelectedFields()

// If a non-removable field is encountered, then skip check to see if all fields are removed.
if (removedAll)
WebDriverWrapper.waitFor(()-> getSelectedFields().isEmpty(), "Did not remove all of the selected fields.", 500);
{
WebDriverWrapper.sleep(500);
WebDriverWrapper.waitFor(() -> getSelectedFields().isEmpty(), "Did not remove all of the selected fields.", 1_500);
}

return this;
}
Expand Down
20 changes: 9 additions & 11 deletions src/org/labkey/test/components/ui/navigation/NavBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

import static org.labkey.test.WebDriverWrapper.WAIT_FOR_JAVASCRIPT;

public abstract class NavBar extends WebDriverComponent<NavBar.ElementCache>
{
private final WebDriver _driver;
Expand Down Expand Up @@ -113,14 +111,14 @@ public WebDriver getDriver()

protected abstract class ElementCache extends Component<ElementCache>.ElementCache
{
public WebElement headerLogo = Locator.tagWithClass("a", "header-logo__link").findWhenNeeded(this);
public WebElement headerLogoImage = Locator.tagWithClass("img", "header-logo__image").findWhenNeeded(this);
public WebElement userMenuButton = Locator.tagWithId("a", "user-menu-dropdown").findWhenNeeded(this).withTimeout(WAIT_FOR_JAVASCRIPT);
public WebElement userIcon = Locator.tagWithAttribute("img", "alt", "User Avatar").findWhenNeeded(this);
public WebElement projectNameDisplay = Locator.tagWithClass("span", "project-name").findWhenNeeded(this);
public Input searchBox = Input.Input(Locator.tagWithClass("input", "navbar__search-input"), getDriver()).findWhenNeeded(this);
public MultiMenu searchMenu = new MultiMenu.MultiMenuFinder(getDriver()).withButtonClass("navbar__find-and-search-button").findWhenNeeded(this);
public final ProductMenu productMenu = ProductMenu.finder(getDriver()).timeout(1000).findWhenNeeded(this);
public final ServerNotificationMenu notificationsMenu = ServerNotificationMenu.finder(getDriver()).timeout(1000).findWhenNeeded(this);
public WebElement headerLogo = Locator.tagWithClass("a", "header-logo__link").refindWhenNeeded(this);
public WebElement headerLogoImage = Locator.tagWithClass("img", "header-logo__image").refindWhenNeeded(this);
public WebElement userMenuButton = Locator.tagWithId("a", "user-menu-dropdown").refindWhenNeeded(this);
public WebElement userIcon = Locator.tagWithAttribute("img", "alt", "User Avatar").refindWhenNeeded(this);
public WebElement projectNameDisplay = Locator.tagWithClass("span", "project-name").refindWhenNeeded(this);
public Input searchBox = Input.Input(Locator.tagWithClass("input", "navbar__search-input"), getDriver()).refindWhenNeeded(this);
public MultiMenu searchMenu = new MultiMenu.MultiMenuFinder(getDriver()).withButtonClass("navbar__find-and-search-button").refindWhenNeeded(this);
public final ProductMenu productMenu = ProductMenu.finder(getDriver()).timeout(1000).refindWhenNeeded(this);
public final ServerNotificationMenu notificationsMenu = ServerNotificationMenu.finder(getDriver()).timeout(1000).refindWhenNeeded(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ public enum XFrameOption
public enum KeyExpirationOptions implements OptionSelect.SelectOption
{
UNLIMITED(-1),
TEN_SECONDS(10),
ONE_WEEK(7*SECONDS_PER_DAY),
ONE_MONTH(30*SECONDS_PER_DAY),
THREE_MONTHS(90*SECONDS_PER_DAY),
Expand Down
16 changes: 4 additions & 12 deletions src/org/labkey/test/pages/study/ConfigureImporterPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
import java.util.Optional;

/**
* If the current folder has 0 specimen importers (implemented in Professional, and FreezerPro) enabled in it,
* If the current folder has 0 specimen importers (implemented in Professional) enabled in it,
* but the modules are available on the server, the user will be shown call to action to enable the modules.
* If 0 importer modules are available, the user will be shown an upsell banner.
*
* If the user has >1, this page will show a configuration selection (pick which one- QueryBased, FreezerPro)
* If the user has >1, this page will show a configuration selection
*
* If the user has only 1, this page will show options for configuring it.
*
Expand All @@ -38,8 +38,7 @@ protected void waitForPage()
{
waitFor(()-> elementCache().EnableModuleBanner().isPresent() ||
elementCache().importOptionPickerBanner().isPresent() ||
elementCache().configureQueryBasedConnectionPane().isPresent() ||
elementCache().freezerProBanner().isPresent(),
elementCache().configureQueryBasedConnectionPane().isPresent(),
"the page did not initialize in time", WAIT_FOR_JAVASCRIPT);
}

Expand All @@ -63,10 +62,7 @@ public boolean isQueryConfigurationShown()
}

/**
* this action is only available if there are more than 1 options to pick from- querybased, freezerpro (or others?)
* If either Professional(query-based) or Freezerpro are enabled on the folder, this page will not be shown
* at the end of the "configure specimen import" link on the Manage Study page, instead you will be directed to views
* from those modules so you can configure their importers accordingly
* This action is only available if there are more than 1 options to pick from
*
* @param option Use the text label next to the radio button
* @return The current page
Expand Down Expand Up @@ -111,9 +107,5 @@ Optional<WebElement> configureQueryBasedConnectionPane()
.findOptionalElement(getDriver());
}

Optional<WebElement> freezerProBanner()
{
return Locator.tagWithText("h3", "FreezerPro Configuration").findOptionalElement(getDriver());
}
}
}
12 changes: 12 additions & 0 deletions src/org/labkey/test/pages/user/UserDetailsPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import org.labkey.test.Locator;
import org.labkey.test.WebDriverWrapper;
import org.labkey.test.WebTestHelper;
import org.labkey.test.components.core.login.SetPasswordForm;
import org.labkey.test.pages.LabKeyPage;
import org.labkey.test.util.PasswordUtil;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

Expand Down Expand Up @@ -34,6 +36,15 @@ public ClonePermissionsPage clickClonePermission()
return new ClonePermissionsPage(getDriver());
}

public SetPasswordForm clickChangePassword()
{
if (PasswordUtil.getUsername().equals(getCurrentUser()))
throw new IllegalArgumentException("Don't change the primary site admin user's password");

clickAndWait(elementCache().changePwdButton);
return new SetPasswordForm(getDriver());
}

@Override
protected ElementCache newElementCache()
{
Expand All @@ -44,5 +55,6 @@ protected class ElementCache extends LabKeyPage<?>.ElementCache
{
WebElement editButton = Locator.lkButton("Edit").findWhenNeeded(this);
WebElement cloneButton = Locator.lkButton("Clone Permissions").findWhenNeeded(this);
WebElement changePwdButton = Locator.lkButton("Change Password").findWhenNeeded(this);
}
}
Loading

0 comments on commit 1556c52

Please sign in to comment.