diff --git a/data/reports/knitr_no_scriptpad.rhtml b/data/reports/knitr_no_scriptpad.rhtml index 1442cd2065..1b3d5c69a1 100644 --- a/data/reports/knitr_no_scriptpad.rhtml +++ b/data/reports/knitr_no_scriptpad.rhtml @@ -93,5 +93,11 @@ end.rcode-->

Well, everything seems to be working. Let's ask R what is the value of π? Of course it is .

+Nonce check: FAIL + + + \ No newline at end of file diff --git a/data/reports/knitr_no_scriptpad.rmd b/data/reports/knitr_no_scriptpad.rmd index 691a947b9c..78ba8eb048 100644 --- a/data/reports/knitr_no_scriptpad.rmd +++ b/data/reports/knitr_no_scriptpad.rmd @@ -113,6 +113,12 @@ library(knitr) knit('knitr-minimal.Rmd') ``` +Nonce check: FAIL + + + ## Conclusion Markdown is super easy to write. Go to **knitr** [homepage](http://yihui.name/knitr) for details. \ No newline at end of file diff --git a/data/reports/nonce_check.rhtml b/data/reports/nonce_check.rhtml new file mode 100644 index 0000000000..7efb90bd76 --- /dev/null +++ b/data/reports/nonce_check.rhtml @@ -0,0 +1,15 @@ + + + + Test script nonce in Knitr HTML + + + +Nonce check: FAIL + + + + + \ No newline at end of file diff --git a/modules/scriptpad/resources/reports/schemas/script_rhtml.rhtml b/modules/scriptpad/resources/reports/schemas/script_rhtml.rhtml index 77e8cfec5d..f9bc5fc814 100644 --- a/modules/scriptpad/resources/reports/schemas/script_rhtml.rhtml +++ b/modules/scriptpad/resources/reports/schemas/script_rhtml.rhtml @@ -87,5 +87,11 @@ end.rcode-->

Well, everything seems to be working. Let's ask R what is the value of π? Of course it is .

+Nonce check: FAIL + + + \ No newline at end of file diff --git a/modules/scriptpad/resources/reports/schemas/script_rmd.rmd b/modules/scriptpad/resources/reports/schemas/script_rmd.rmd index 5729f20b1c..f136b7616b 100644 --- a/modules/scriptpad/resources/reports/schemas/script_rmd.rmd +++ b/modules/scriptpad/resources/reports/schemas/script_rmd.rmd @@ -108,6 +108,12 @@ library(knitr) knit('knitr-minimal.Rmd') ``` +Nonce check: FAIL + + + ## Conclusion Markdown is super easy to write. Go to **knitr** [homepage](http://yihui.name/knitr) for details. \ No newline at end of file diff --git a/src/org/labkey/test/TestFileUtils.java b/src/org/labkey/test/TestFileUtils.java index 6a4a3ae4eb..b9a1228ceb 100644 --- a/src/org/labkey/test/TestFileUtils.java +++ b/src/org/labkey/test/TestFileUtils.java @@ -109,7 +109,7 @@ public static String getFileContents(Path path) { try { - return new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + return Files.readString(path); } catch (IOException fail) { @@ -306,7 +306,7 @@ public static Set getSampleDataDirs() if (sampledataDirsFile.exists()) { String path = getFileContents(sampledataDirsFile); - _sampledataDirs.addAll(Arrays.stream(path.split(";")).map(File::new).collect(Collectors.toList())); + _sampledataDirs.addAll(Arrays.stream(path.split(";")).map(File::new).toList()); } else { @@ -317,7 +317,7 @@ public static Set getSampleDataDirs() // We know where the modules live; no reason to insist that sampledata.dirs exists. Files.walkFileTree(modulesDir, Collections.emptySet(), 2, new SimpleFileVisitor<>(){ @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException + public @NotNull FileVisitResult preVisitDirectory(@NotNull Path dir, @NotNull BasicFileAttributes attrs) { if (dir.equals(modulesDir)) { diff --git a/src/org/labkey/test/components/CustomizeView.java b/src/org/labkey/test/components/CustomizeView.java index e2e801c044..4e688abdc5 100644 --- a/src/org/labkey/test/components/CustomizeView.java +++ b/src/org/labkey/test/components/CustomizeView.java @@ -28,7 +28,7 @@ import org.labkey.test.components.ext4.ComboBox; import org.labkey.test.components.ext4.RadioButton; import org.labkey.test.components.ext4.Window; -import org.labkey.test.selenium.LazyWebElement; +import org.labkey.test.params.FieldKey; import org.labkey.test.selenium.RefindingWebElement; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.Ext4Helper; @@ -44,9 +44,9 @@ import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.support.ui.ExpectedConditions; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import java.util.Objects; import static org.labkey.test.components.ext4.Checkbox.Ext4Checkbox; import static org.labkey.test.components.ext4.RadioButton.RadioButton; @@ -233,14 +233,14 @@ public void saveCustomView(String name, @Nullable Boolean shared, @Nullable Bool saveWindow.save(); } - public static class SaveWindow extends Window + public static class SaveWindow extends Window.ElementCache> { public final Checkbox shareCheckbox = Ext4Checkbox().withLabel("Make this grid view available to all users").findWhenNeeded(this); public final Checkbox inheritCheckbox = Ext4Checkbox().withLabel("Make this grid view available in child folders").findWhenNeeded(this); public final RadioButton defaultViewRadio = RadioButton().withLabelContaining("Default").findWhenNeeded(this); public final RadioButton namedViewRadio = RadioButton().withLabelContaining("Named").findWhenNeeded(this); - private final WebElement viewNameInput = new LazyWebElement(Locator.xpath("//input[@name='saveCustomView_name']"), this); - private final WebElement targetContainerInput = new LazyWebElement(Locator.xpath("//input[@name='saveCustomView_targetContainer']"), this); + private final WebElement viewNameInput = Locator.input("saveCustomView_name").findWhenNeeded(this); + private final WebElement targetContainerInput = Locator.input("saveCustomView_targetContainer").findWhenNeeded(this); protected SaveWindow(WebDriver driver) { @@ -258,10 +258,10 @@ public void setTargetContainer(String container) getWrapper().setFormElement(targetContainerInput, container); } - public Window saveError() + public Window saveError() { clickButton("Save", 0); - return new Window("Error saving grid view", getWrapper().getDriver()); + return new Window<>("Error saving grid view", getWrapper().getDriver()); } public void save() @@ -279,7 +279,7 @@ public void cancel() public void deleteView() { elements().deleteButton.click(); - Window confirm = Window(getDriver()).withTitleContaining("Delete").find(); + Window confirm = Window(getDriver()).withTitleContaining("Delete").find(); confirm.clickButton("Yes"); } @@ -308,19 +308,11 @@ public void revertUnsavedView() /** * add a column to an already open customize view grid * - * @param column_name Name of the column. If your column is nested, should be of the form - * "nodename/nodename/lastnodename", where nodename is not the displayed text of a node - * but the name included in the span containing the checkbox. It will often be the same name, - * but with less whitespace + * @param fieldKey fieldKey of the column */ - public void addColumn(String column_name) + public void addColumn(CharSequence fieldKey) { - addColumn(column_name, column_name); - } - - public void addColumn(String[] fieldKeyParts) - { - addColumn(fieldKeyParts, StringUtils.join(fieldKeyParts, "/")); + addItem(fieldKey, ViewItemType.Columns); } public void changeTab(ViewItemType tab) @@ -344,87 +336,69 @@ public enum ViewItemType } /** - * expand customize view menu to all but the last of fieldKeyParts - * @return The data-recordid property of the <tr> element for the specified field in the "Available Fields" column tree. + * expand customize view fields tree to expose the specified column + * @return The row element for the specified column */ - private WebElement expandPivots(String[] fieldKeyParts) + private WebElement expandPivots(CharSequence fieldKey) { - String nodePath = ""; - String fieldKey = StringUtils.join(fieldKeyParts, "/").toUpperCase(); + Iterator fieldKeyIterator = Objects.requireNonNull(FieldKey.fromFieldKey(fieldKey), "Invalid fieldKey: " + fieldKey).getIterator(); + FieldKey currentFieldKey = fieldKeyIterator.next(); + String dataRecordId = currentFieldKey.toString().toUpperCase(); - for (int i = 0; i < fieldKeyParts.length - 1; i ++ ) + while (fieldKeyIterator.hasNext()) { - nodePath += fieldKeyParts[i].toUpperCase(); - WebElement fieldRow = Locator.tag("tr").withClass("x4-grid-data-row").withAttribute("data-recordid", nodePath).waitForElement(getComponentElement(), 10000); + WebElement fieldRow = Locator.tag("tr").withClass("x4-grid-data-row").withAttribute("data-recordid", dataRecordId).waitForElement(getComponentElement(), 10000); _driver.scrollIntoView(fieldRow, false); - if (!fieldRow.getAttribute("class").contains("expanded")) + if (!StringUtils.trimToEmpty(fieldRow.getAttribute("class")).contains("expanded")) { Locator.css(".x4-tree-expander").findElement(fieldRow).click(); } - Locator.tag("tr").withClass("x4-grid-tree-node-expanded").withAttribute("data-recordid", nodePath).waitForElement(getComponentElement(), 10000); + Locator.tag("tr").withClass("x4-grid-tree-node-expanded").withAttribute("data-recordid", dataRecordId).waitForElement(getComponentElement(), 10000); WebDriverWrapper.waitFor(() -> Locator.css("tr[data-recordid] + tr:not(.x4-grid-row)").findElements(getComponentElement()).isEmpty(), 2000); // Spacer row appears during expansion animation - nodePath += "/"; + + currentFieldKey = fieldKeyIterator.next(); + dataRecordId = currentFieldKey.toString().toUpperCase(); } - return Locator.tag("tr").withClass("x4-grid-data-row").withAttribute("data-recordid", fieldKey).findElement(getComponentElement()); + return Locator.tag("tr").withClass("x4-grid-data-row").withAttribute("data-recordid", dataRecordId).findElement(getComponentElement()); } - private void addItem(String[] fieldKeyParts, String columnName, ViewItemType type) + private void addItem(CharSequence fieldKey, ViewItemType type) { // fieldKey is the value contained in @fieldkey - _driver.log("Adding " + columnName + " " + type.toString()); + _driver.log("Adding " + fieldKey + " " + type); changeTab(type); // Expand all nodes necessary to reveal the desired node. - WebElement fieldRow = expandPivots(fieldKeyParts); + WebElement fieldRow = expandPivots(fieldKey); WebElement checkbox = Locator.css("input[type=button]").findElement(fieldRow); WebElement rowLabel = Locator.byClass("x4-tree-node-text").findElement(fieldRow); rowLabel.click(); new Checkbox(checkbox).check(); - itemXPath(type, fieldKeyParts).waitForElement(this, 2_000); + itemXPath(type, fieldKey).waitForElement(this, 2_000); } - public void addColumn(String[] fieldKeyParts, String label) + public void addFilter(CharSequence fieldKey, String filter_type) { - addItem(fieldKeyParts, label, ViewItemType.Columns); + addFilter(fieldKey, filter_type, ""); } - public void addColumn(String fieldKey, String column_name) - { - addItem(fieldKey.split("/"), column_name, ViewItemType.Columns); - } - - public void addFilter(String fieldKey, String filter_type) - { - addFilter(fieldKey, fieldKey, filter_type, ""); - } - - public void addFilter(String fieldKey, String filter_type, String filter) - { - addFilter(fieldKey, fieldKey, filter_type, filter); - } - - public void addFilter(String fieldKey, String column_name, String filter_type, String filter) - { - addFilter(fieldKey.split("/"), column_name, filter_type, filter); - } - - public void addFilter(String[] fieldKeyParts, String column_name, String filter_type, String filter) + public void addFilter(CharSequence fieldKey, String filter_type, String filter) { if (filter.isEmpty()) - _driver.log("Adding " + column_name + " filter of " + filter_type); + _driver.log("Adding " + fieldKey + " filter of " + filter_type); else - _driver.log("Adding " + column_name + " filter of " + filter_type + " " + filter); + _driver.log("Adding " + fieldKey + " filter of " + filter_type + " " + filter); changeTab(ViewItemType.Filter); - Locator.XPathLocator itemXPath = itemXPath(ViewItemType.Filter, fieldKeyParts); + Locator.XPathLocator itemXPath = itemXPath(ViewItemType.Filter, fieldKey); if (!_driver.isElementPresent(itemXPath)) { // Add filter if it doesn't exist - addItem(fieldKeyParts, column_name, ViewItemType.Filter); + addItem(fieldKey, ViewItemType.Filter); _driver.assertElementPresent(itemXPath); } else @@ -459,15 +433,9 @@ private Locator.XPathLocator tabContentXPath(ViewItemType type) return Locator.tagWithClass("div", "test-" + type.toString().toLowerCase() + "-tab"); } - private Locator.XPathLocator itemXPath(ViewItemType type, String[] fieldKeyParts) + private Locator.XPathLocator itemXPath(ViewItemType type, CharSequence fieldKey) { - return itemXPath(type, StringUtils.join(fieldKeyParts, "/")); - } - - private Locator.XPathLocator itemXPath(ViewItemType type, String fieldKey) - { - FieldKey parsedFieldKey = new FieldKey(fieldKey); - return itemXPath(type).withPredicate("@fieldkey=" + Locator.xq(fieldKey) + " or @fieldkey=" + Locator.xq(parsedFieldKey.toString())); + return itemXPath(type).withAttribute("fieldkey", fieldKey.toString()); } private Locator.XPathLocator itemXPath(ViewItemType type, int item_index) @@ -480,7 +448,7 @@ private Locator.XPathLocator itemXPath(ViewItemType type) return Locator.tagWithClass("table", "labkey-customview-" + type.toString().toLowerCase() + "-item"); } - private void removeItem(String fieldKey, ViewItemType type) + private void removeItem(CharSequence fieldKey, ViewItemType type) { changeTab(type); @@ -510,43 +478,6 @@ private void removeItem(String fieldKey, ViewItemType type) } } - public static class FieldKey - { - public static final String SEPARATOR = "/"; - private final String fieldName; - private final String fieldKey; - private final List lookupParts; - - public FieldKey(String fieldKey) - { - List allParts = Arrays.asList(fieldKey.split(SEPARATOR)); - lookupParts = allParts.subList(0, allParts.size() - 1); - for (int i = 0; i < lookupParts.size(); i++) - { - lookupParts.set(i, lookupParts.get(i));//.toUpperCase()); - } - fieldName = allParts.get(allParts.size() - 1); - allParts = new ArrayList<>(lookupParts); - allParts.add(fieldName); - this.fieldKey = String.join(SEPARATOR, allParts); - } - - public String getFieldName() - { - return fieldName; - } - - public List getLookupParts() - { - return lookupParts; - } - - public String toString() - { - return fieldKey; - } - } - //enable customize view grid to show hidden fields public void showHiddenItems() { @@ -558,40 +489,30 @@ public void showHiddenItems() BaseWebDriverTest.sleep(250); // wait for columns to display } - public void addSort(String column_name, SortDirection order) - { - addSort(column_name, column_name, order); - } - - public void addSort(String fieldKey, String column_name, SortDirection order) - { - addSort(fieldKey.split("/"), column_name, order); - } - - public void addSort(String[] fieldKeyParts, String column_name, SortDirection order) + public void addSort(CharSequence fieldKey, SortDirection order) { - Locator.XPathLocator itemXPath = itemXPath(ViewItemType.Sort, fieldKeyParts); + Locator.XPathLocator itemXPath = itemXPath(ViewItemType.Sort, fieldKey); _driver.assertElementNotPresent(itemXPath); - addItem(fieldKeyParts, column_name, ViewItemType.Sort); + addItem(fieldKey, ViewItemType.Sort); _driver._ext4Helper.selectComboBoxItem(itemXPath, order.toString()); itemXPath.append("//tr").findElement(this).click(); // Sort direction doesn't stick without this } - public void removeColumn(String fieldKey) + public void removeColumn(CharSequence fieldKey) { _driver.log("Removing " + fieldKey + " column"); removeItem(fieldKey, ViewItemType.Columns); } - public void removeFilter(String fieldKey) + public void removeFilter(CharSequence fieldKey) { _driver.log("Removing " + fieldKey + " filter"); removeItem(fieldKey, ViewItemType.Filter); } - public void removeSort(String fieldKey) + public void removeSort(CharSequence fieldKey) { _driver.log("Removing " + fieldKey + " sort"); removeItem(fieldKey, ViewItemType.Sort); @@ -643,25 +564,25 @@ public void setFolderFilter(@LoggedParam String folderFilter) _driver._ext4Helper.selectComboBoxItem("Folder Filter:", folderFilter); } - public void moveColumn(String fieldKey, boolean moveUp) + public void moveColumn(CharSequence fieldKey, boolean moveUp) { _driver.log("Moving filter, " + fieldKey + " " + (moveUp ? "up." : "down.")); moveItem(fieldKey, moveUp, ViewItemType.Columns); } - public void moveFilter(String fieldKey, boolean moveUp) + public void moveFilter(CharSequence fieldKey, boolean moveUp) { _driver.log("Moving filter, " + fieldKey + " " + (moveUp ? "up." : "down.")); moveItem(fieldKey, moveUp, ViewItemType.Filter); } - public void moveSort(String fieldKey, boolean moveUp) + public void moveSort(CharSequence fieldKey, boolean moveUp) { _driver.log("Moving sort, " + fieldKey + " " + (moveUp ? "up." : "down.")); moveItem(fieldKey, moveUp, ViewItemType.Sort); } - private void moveItem(String fieldKey, boolean moveUp, ViewItemType type) + private void moveItem(CharSequence fieldKey, boolean moveUp, ViewItemType type) { changeTab(type); final int itemIndex = _driver.getElementIndex(itemXPath(type, fieldKey).findElement(this)); @@ -683,7 +604,7 @@ private void moveItem(int field_index, boolean moveUp, ViewItemType type) builder.dragAndDrop(fromItem, toItem).build().perform(); } - public void removeColumnTitle(String fieldKey) + public void removeColumnTitle(CharSequence fieldKey) { setColumnTitle(fieldKey, null); } @@ -693,7 +614,7 @@ public void removeColumnTitle(String fieldKey) * @param fieldKey The field key of the column to change. Note that the column should already be in the selected column list. * @param caption The caption value or null to unset the column caption. */ - public void setColumnTitle(String fieldKey, String caption) + public void setColumnTitle(CharSequence fieldKey, String caption) { String msg = "Setting column " + fieldKey; if (caption != null) @@ -702,23 +623,22 @@ public void setColumnTitle(String fieldKey, String caption) changeTab(ViewItemType.Columns); - Window window = new SelectedColumnRow(fieldKey).clickEdit(); + Window window = new SelectedColumnRow(fieldKey).clickEdit(); if (caption == null) caption = ""; _driver.setFormElement(Locator.name("title").findElement(window), caption); Locator.xpath("//label").findElement(window).click(); - window.clickButton("OK", 0); - window.waitForClose(); + window.clickButton("OK", true); } /** Check that a column is present. */ - public boolean isColumnPresent(String fieldKey) + public boolean isColumnPresent(CharSequence fieldKey) { try { - expandPivots(fieldKey.split("/")); + expandPivots(fieldKey); return true; } catch (NoSuchElementException no) @@ -728,23 +648,23 @@ public boolean isColumnPresent(String fieldKey) } /** Check that a column is present and is not selectable. */ - public boolean isColumnUnselectable(String fieldKey) + public boolean isColumnUnselectable(CharSequence fieldKey) { - WebElement fieldRow = expandPivots(fieldKey.split("/")); + WebElement fieldRow = expandPivots(fieldKey); return "on".equals(fieldRow.getAttribute("unselectable")); } - public WebElement getColumn(String fieldKey) + public WebElement getColumn(CharSequence fieldKey) { - return expandPivots(fieldKey.split("/")); + return expandPivots(fieldKey); } /** Check that a column is present and not hidden. Assumes that the 'show hidden columns' is unchecked. */ - public boolean isColumnVisible(String fieldKey) + public boolean isColumnVisible(CharSequence fieldKey) { try { - WebElement fieldRow = expandPivots(fieldKey.split("/")); + WebElement fieldRow = expandPivots(fieldKey); return fieldRow.isDisplayed(); } catch (NoSuchElementException no) @@ -754,9 +674,9 @@ public boolean isColumnVisible(String fieldKey) } /** Check that a column is present and is a lookup column. */ - public boolean isLookupColumn(String fieldKey) + public boolean isLookupColumn(CharSequence fieldKey) { - WebElement fieldRow = expandPivots(fieldKey.split("/")); + WebElement fieldRow = expandPivots(fieldKey); return !Locator.css("img.x4-tree-expander").findElements(fieldRow).isEmpty(); } @@ -766,7 +686,7 @@ protected Elements newElementCache() return new Elements(); } - protected class Elements extends Component.ElementCache + protected class Elements extends Component.ElementCache { protected final WebElement deleteButton = new RefindingWebElement(Ext4Helper.Locators.ext4Button("Delete"), this); protected final WebElement revertButton = new RefindingWebElement(Ext4Helper.Locators.ext4Button("Revert"), this); @@ -774,14 +694,14 @@ protected class Elements extends Component.ElementCache protected final WebElement saveButton = new RefindingWebElement(Ext4Helper.Locators.ext4Button("Save"), this); } - private class SelectedItemRow extends Component + private class SelectedItemRow extends Component.ElementCache> { private final WebElement _element; - private final String _fieldKey; + private final FieldKey _fieldKey; - protected SelectedItemRow(ViewItemType itemType, String fieldkey) + protected SelectedItemRow(ViewItemType itemType, CharSequence fieldkey) { - _fieldKey = fieldkey; + _fieldKey = FieldKey.fromFieldKey(fieldkey); _element = itemXPath(itemType, fieldkey).findElement(CustomizeView.this); } @@ -791,7 +711,7 @@ public WebElement getComponentElement() return _element; } - protected String getFieldKey() + protected FieldKey getFieldKey() { return _fieldKey; } @@ -805,12 +725,12 @@ public void clickDelete() private class SelectedColumnRow extends SelectedItemRow { - public SelectedColumnRow(String fieldkey) + public SelectedColumnRow(CharSequence fieldkey) { super(ViewItemType.Columns, fieldkey); } - public Window clickEdit() + public Window clickEdit() { WebElement gear = Locator.css("div.labkey-tool-gear").findElement(getComponentElement()); _driver.scrollIntoView(gear, true); @@ -821,7 +741,7 @@ public Window clickEdit() private class SelectedFilterRow extends SelectedItemRow { - public SelectedFilterRow(String fieldkey) + public SelectedFilterRow(CharSequence fieldkey) { super(ViewItemType.Filter, fieldkey); } @@ -834,7 +754,7 @@ public void setFilter(Filter filter) private class SelectedSortRow extends SelectedItemRow { - public SelectedSortRow(String fieldkey) + public SelectedSortRow(CharSequence fieldkey) { super(ViewItemType.Sort, fieldkey); } diff --git a/src/org/labkey/test/components/ui/entities/EntityBulkUpdateDialog.java b/src/org/labkey/test/components/ui/entities/EntityBulkUpdateDialog.java index a5300dca78..09aee4dcc9 100644 --- a/src/org/labkey/test/components/ui/entities/EntityBulkUpdateDialog.java +++ b/src/org/labkey/test/components/ui/entities/EntityBulkUpdateDialog.java @@ -1,6 +1,7 @@ package org.labkey.test.components.ui.entities; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.labkey.remoteapi.CommandException; import org.labkey.test.BootstrapLocators; import org.labkey.test.Locator; @@ -132,6 +133,15 @@ public EntityBulkUpdateDialog setSelectionField(CharSequence fieldIdentifier, St return setSelectionField(fieldIdentifier, List.of(selectValue)); } + /** + * @param fieldIdentifier Identifier for the field; name ({@link String}) or fieldKey ({@link FieldKey}) + * @return text displayed in the help block, if any, for the selection field + */ + public @Nullable String getSelectionFieldHelpBlockText(CharSequence fieldIdentifier) + { + return elementCache().getSelect(fieldIdentifier).getHelpBlockText(); + } + /** * @param fieldIdentifier Identifier for the field; name ({@link String}) or fieldKey ({@link FieldKey}) * @return available options for the specified field @@ -243,9 +253,10 @@ public String getDateField(CharSequence fieldIdentifier) * @param fieldIdentifier Identifier for the field; name ({@link String}) or fieldKey ({@link FieldKey}) * @return file attachment component */ - public FileAttachmentContainer getFileField(CharSequence fieldIdentifier) + private FileAttachmentContainer getFileField(CharSequence fieldIdentifier) { - return enableAndWait(fieldIdentifier, elementCache().fileUploadField(fieldIdentifier)); + FieldKey identifier = FileAttachmentContainer.fileUploadFieldKey(fieldIdentifier); + return enableAndWait(identifier, elementCache().fileUploadField(identifier)); } /** @@ -269,9 +280,10 @@ public EntityBulkUpdateDialog removeFile(CharSequence fieldIdentifier) return this; } - public FileUploadField getExistingFileField(String fieldIdentifier) + public FileUploadField getExistingFileCard(CharSequence fieldIdentifier) { - return enableAndWait(fieldIdentifier, elementCache().fileField(fieldIdentifier)); + FieldKey identifier = FileAttachmentContainer.fileUploadFieldKey(fieldIdentifier); + return enableAndWait(identifier, elementCache().fileField(identifier)); } /** diff --git a/src/org/labkey/test/components/ui/files/FileAttachmentContainer.java b/src/org/labkey/test/components/ui/files/FileAttachmentContainer.java index bb6acebe86..01cbeb7946 100644 --- a/src/org/labkey/test/components/ui/files/FileAttachmentContainer.java +++ b/src/org/labkey/test/components/ui/files/FileAttachmentContainer.java @@ -6,6 +6,7 @@ import org.labkey.test.components.WebDriverComponent; import org.labkey.test.components.html.FileInput; import org.labkey.test.components.html.Input; +import org.labkey.test.params.FieldKey; import org.openqa.selenium.ElementNotInteractableException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; @@ -195,6 +196,15 @@ protected class ElementCache extends Component.ElementCache public Locator fileUploadScrollFooterLoc = Locator.tagWithClass("div", "file-upload__scroll-footer"); } + /** + * File upload fields append "-fileUpload" to the field's fieldKey + * @param fieldIdentifier Identifier for the field; name ({@link String}) or fieldKey ({@link FieldKey}) + * @return FieldKey with expected suffix + */ + public static FieldKey fileUploadFieldKey(CharSequence fieldIdentifier) + { + return FieldKey.fromFieldKey(FieldKey.fromName(fieldIdentifier) + "-fileUpload"); // Issue 53394 + } public static class FileAttachmentContainerFinder extends WebDriverComponentFinder { diff --git a/src/org/labkey/test/pages/admin/ExternalSourcesPage.java b/src/org/labkey/test/pages/admin/ExternalSourcesPage.java index 25d1da4db3..25d3c41075 100644 --- a/src/org/labkey/test/pages/admin/ExternalSourcesPage.java +++ b/src/org/labkey/test/pages/admin/ExternalSourcesPage.java @@ -207,6 +207,7 @@ public enum Directive implements OptionSelect.SelectOption Frame("frame-src"), Image("image-src"), Style("style-src"), + Object("object-src"), ; private final String directiveId; diff --git a/src/org/labkey/test/pages/announcements/EmailPrefsPage.java b/src/org/labkey/test/pages/announcements/EmailPrefsPage.java index 178af551f9..02b74c319d 100644 --- a/src/org/labkey/test/pages/announcements/EmailPrefsPage.java +++ b/src/org/labkey/test/pages/announcements/EmailPrefsPage.java @@ -43,36 +43,92 @@ public static EmailPrefsPage beginAt(WebDriverWrapper driver, String containerPa return new EmailPrefsPage(driver.getDriver()); } + @Deprecated // Inconsistent name, but leave for backward compatibility public EmailPrefsPage setNoNotify() + { + return setNotifyNone(); + } + + public EmailPrefsPage setNotifyNone() { elementCache().notifyNone.click(); return new EmailPrefsPage(getDriver()); } + public boolean isNotifyNoneSelected() + { + return elementCache().notifyNone.isSelected(); + } + + public boolean isNotifyNoneDisabled() + { + return !elementCache().notifyNone.isEnabled(); + } + public EmailPrefsPage setNotifyOnMine() { elementCache().notifyMine.click(); return new EmailPrefsPage(getDriver()); } + public boolean isNotifyOnMineSelected() + { + return elementCache().notifyMine.isSelected(); + } + + public boolean isNotifyOnMineDisabled() + { + return !elementCache().notifyMine.isEnabled(); + } + public EmailPrefsPage setNotifyOnAll() { elementCache().notifyAll.click(); return new EmailPrefsPage(getDriver()); } + public boolean isNotifyOnAllSelected() + { + return elementCache().notifyAll.isSelected(); + } + + public boolean isNotifyOnAllDisabled() + { + return !elementCache().notifyAll.isEnabled(); + } + public EmailPrefsPage setTypeIndividual() { - elementCache().notifyAll.click(); + elementCache().typeIndividual.click(); return new EmailPrefsPage(getDriver()); } + public boolean isTypeIndividualSelected() + { + return elementCache().typeIndividual.isSelected(); + } + + public boolean isTypeIndividualDisabled() + { + return !elementCache().typeIndividual.isEnabled(); + } + public EmailPrefsPage setTypeDigest() { - elementCache().notifyAll.click(); + elementCache().typeDigest.click(); return new EmailPrefsPage(getDriver()); } + public boolean isTypeDigestSelected() + { + return elementCache().typeDigest.isSelected(); + } + + public boolean isTypeDigestDisabled() + { + return !elementCache().typeDigest.isEnabled(); + } + public EmailPrefsPage reset(boolean reset) { if (reset) @@ -114,10 +170,10 @@ protected class ElementCache extends LabKeyPage.ElementCache protected WebElement notifyAll = notify.withAttribute("value", "1").findWhenNeeded(this); private final Locator.XPathLocator type = Locator.radioButtonByName("notificationType"); - protected WebElement typeIndividual = notify.withAttribute("value", "3").findWhenNeeded(this); - protected WebElement typeDigest = notify.withAttribute("value", "4").findWhenNeeded(this); + protected WebElement typeIndividual = type.withAttribute("value", "3").findWhenNeeded(this); + protected WebElement typeDigest = type.withAttribute("value", "4").findWhenNeeded(this); - Checkbox resetCheckbox = Checkbox(Locator.tagWithName("input", "emailPreference").withAttribute("value", "1")).findWhenNeeded(this); + Checkbox resetCheckbox = Checkbox(Locator.tagWithId("input", "resetFolderDefault")).findWhenNeeded(this); WebElement updateButton = Locator.lkButton("Update").findWhenNeeded(this); WebElement cancelButton = Locator.lkButton("Cancel").findWhenNeeded(this); diff --git a/src/org/labkey/test/pages/assay/AssayRunsPage.java b/src/org/labkey/test/pages/assay/AssayRunsPage.java index 79fd09df05..8da9a93c4a 100644 --- a/src/org/labkey/test/pages/assay/AssayRunsPage.java +++ b/src/org/labkey/test/pages/assay/AssayRunsPage.java @@ -21,6 +21,7 @@ import org.labkey.test.WebTestHelper; import org.labkey.test.components.bootstrap.ModalDialog; import org.labkey.test.components.labkey.LabKeyAlert; +import org.labkey.test.components.react.MultiMenu; import org.labkey.test.pages.LabKeyPage; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.ExperimentRunTable; @@ -107,6 +108,11 @@ public AssayRunsPage setRowQcStatus(String state, String comment, boolean expect return updatePage.clickUpdate(); } + public void clickEditAssayDesign() + { + elementCache().manageMenu.doMenuAction("Edit Assay Design"); + } + @Override protected ElementCache newElementCache() { @@ -115,6 +121,6 @@ protected ElementCache newElementCache() protected class ElementCache extends LabKeyPage.ElementCache { - + MultiMenu manageMenu = new MultiMenu.MultiMenuFinder(getDriver()).withText("Manage").findWhenNeeded(getDriver()); } } diff --git a/src/org/labkey/test/pages/reports/ScriptReportPage.java b/src/org/labkey/test/pages/reports/ScriptReportPage.java index 2d57bc293d..31b0db491e 100644 --- a/src/org/labkey/test/pages/reports/ScriptReportPage.java +++ b/src/org/labkey/test/pages/reports/ScriptReportPage.java @@ -11,12 +11,14 @@ import org.labkey.test.pages.LabKeyPage; import org.labkey.test.util.CodeMirrorHelper; import org.labkey.test.util.Ext4Helper; +import org.labkey.test.util.PipelineStatusTable; import org.labkey.test.util.TestLogger; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; import java.util.Map; +import java.util.Objects; import java.util.Optional; import static org.labkey.test.components.ext4.Checkbox.Ext4Checkbox; @@ -79,6 +81,7 @@ public CodeMirrorHelper getEditor() public String saveReport(String name, boolean isSaveAs, int wait) { + String reportIdBeforeSave = getReportId(); WebElement saveButton = Ext4Helper.Locators.ext4Button(isSaveAs ? "Save As" : "Save").findElement(getDriver()); scrollIntoView(saveButton, true); clickAndWait(saveButton, wait); @@ -86,6 +89,11 @@ public String saveReport(String name, boolean isSaveAs, int wait) { saveReportWithName(name, isSaveAs); } + return Objects.requireNonNullElse(getReportId(), reportIdBeforeSave); + } + + public String getReportId() + { return getUrlParam("reportId", true); } @@ -141,6 +149,7 @@ public void clearOption(ReportOption option) private void _selectOption(ReportOption option, boolean checked) { + clickSourceTab(); ensureFieldSetExpanded(option.getSection()); Checkbox checkbox; if (option.isCheckbox()) @@ -202,6 +211,17 @@ public WebElement findReportElement() return Locator.byClass("reportView").findElement(getDriver()); } + public void startPipelineJobAndWait() + { + clickReportTab(); + + waitAndClick(Locator.lkButton("Start Job")); + waitAndClickAndWait(Locator.linkWithText("click here")); + new PipelineStatusTable(this) + .clickStatusLink(0) + .waitForComplete(); + } + @Override protected ElementCache newElementCache() { diff --git a/src/org/labkey/test/pages/wiki/EditPage.java b/src/org/labkey/test/pages/wiki/EditPage.java index 032f0c2734..9e4d76a535 100644 --- a/src/org/labkey/test/pages/wiki/EditPage.java +++ b/src/org/labkey/test/pages/wiki/EditPage.java @@ -100,10 +100,48 @@ public EditPage setTitle(String title) public EditPage setBody(String body) { + switchWikiToSourceView(); elementCache().bodyTextArea.set(body); return this; } + /** + * Switches the wiki edit page to source view when the format type is HTML. + */ + public void switchWikiToSourceView() + { + String curFormat = executeScript("return LABKEY._wiki.getProps().rendererType;", String.class); + if (curFormat.equalsIgnoreCase("HTML")) + { + if (isElementPresent(Locator.css("#wiki-tab-source.labkey-tab-inactive"))) + { + Locator tab = Locator.css("#wiki-tab-source > a"); + waitForElementToBeVisible(tab); + click(tab); + waitForElement(Locator.css("#wiki-tab-source.labkey-tab-active")); + } + } + } + + public void switchWikiToVisualView() + { + String curFormat = (String) executeScript("return LABKEY._wiki.getProps().rendererType;"); + if (curFormat.equalsIgnoreCase("HTML")) + { + if (isElementPresent(Locator.css("#wiki-tab-visual.labkey-tab-inactive"))) + { + Locator tab = Locator.css("#wiki-tab-visual > a"); + waitForElementToBeVisible(tab); + click(tab); + + Locator yesButton = Locator.tagWithText("span","Yes"); + waitForElementToBeVisible(yesButton); + waitAndClick(yesButton); + waitForElement(Locator.css("#wiki-tab-visual.labkey-tab-active")); + } + } + } + public EditPage setShouldIndex(boolean shouldIndex) { elementCache().shouldIndexCheckbox.set(shouldIndex); diff --git a/src/org/labkey/test/params/FieldKey.java b/src/org/labkey/test/params/FieldKey.java index eb3100a331..6ccc37387d 100644 --- a/src/org/labkey/test/params/FieldKey.java +++ b/src/org/labkey/test/params/FieldKey.java @@ -15,9 +15,9 @@ public final class FieldKey implements CharSequence, WrapsFieldKey private static final String[] ILLEGAL = {"$", "/", "&", "}", "~", ",", "."}; private static final String[] REPLACEMENT = {"$D", "$S", "$A", "$B", "$T", "$C", "$P"}; - public static final FieldKey EMPTY = new FieldKey(""); // Useful as a sort of FieldKey builder starting point - public static final FieldKey SOURCES_FK = new FieldKey("DataInputs"); - public static final FieldKey PARENTS_FK = new FieldKey("MaterialInputs"); + public static final FieldKey EMPTY = new FieldKey(null, ""); // Useful as a sort of FieldKey builder starting point + public static final FieldKey SOURCES_FK = FieldKey.fromParts("DataInputs"); + public static final FieldKey PARENTS_FK = FieldKey.fromParts("MaterialInputs"); private static final String SEPARATOR = "/"; @@ -25,18 +25,20 @@ public final class FieldKey implements CharSequence, WrapsFieldKey private final String _name; private final String _fieldKey; - private FieldKey(String name) - { - _parent = null; - _name = name; - _fieldKey = encodePart(name); - } - private FieldKey(FieldKey parent, String child) { - _parent = parent; - _name = parent.getName() + SEPARATOR + child; - _fieldKey = parent + SEPARATOR + encodePart(child); + if (parent != null && !parent.isEmpty()) + { + _parent = parent; + _name = parent.getName() + SEPARATOR + child; + _fieldKey = parent + SEPARATOR + encodePart(child); + } + else + { + _parent = null; + _name = child; + _fieldKey = encodePart(child); + } } public static List getIllegalChars() @@ -46,14 +48,7 @@ public static List getIllegalChars() public static FieldKey fromParts(List parts) { - FieldKey fieldKey = EMPTY; - - for (String part : parts) - { - fieldKey = fieldKey.child(part); - } - - return fieldKey; + return EMPTY.child(parts); } public static FieldKey fromParts(String... parts) @@ -113,19 +108,23 @@ public FieldKey getParent() return _parent; } - public FieldKey child(String part) + public FieldKey child(String... parts) { - if (StringUtils.isBlank(part)) - throw new IllegalArgumentException("FieldKey can't have blank part(s): " + this); + return child(Arrays.asList(parts)); + } - if (StringUtils.isBlank(getName())) - { - return new FieldKey(part); - } - else + public FieldKey child(List parts) + { + FieldKey child = this; + + for (String part : parts) { - return new FieldKey(this, part); + if (StringUtils.isBlank(part)) + throw new IllegalArgumentException("FieldKey can't have blank part(s): " + parts); + + child = new FieldKey(child, part); } + return child; } public Iterator getIterator() diff --git a/src/org/labkey/test/tests/AbstractKnitrReportTest.java b/src/org/labkey/test/tests/AbstractKnitrReportTest.java index d40ece66e7..562b775d94 100644 --- a/src/org/labkey/test/tests/AbstractKnitrReportTest.java +++ b/src/org/labkey/test/tests/AbstractKnitrReportTest.java @@ -17,6 +17,7 @@ import org.junit.Assume; import org.junit.BeforeClass; +import org.junit.Test; import org.labkey.remoteapi.CommandException; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; @@ -25,12 +26,16 @@ import org.labkey.test.pages.admin.ExternalSourcesPage; import org.labkey.test.pages.core.admin.logger.ManagerPage; import org.labkey.test.pages.reports.ManageViewsPage; +import org.labkey.test.pages.reports.ScriptReportPage; +import org.labkey.test.pages.reports.ScriptReportPage.StandardReportOption; import org.labkey.test.util.CodeMirrorHelper; +import org.labkey.test.util.CspLogUtil; import org.labkey.test.util.Log4jUtils; import org.labkey.test.util.LogMethod; import org.labkey.test.util.LoggedParam; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.RReportHelper; +import org.labkey.test.util.WikiHelper; import org.labkey.test.util.core.admin.CspConfigHelper; import org.openqa.selenium.WebElement; @@ -54,6 +59,10 @@ public abstract class AbstractKnitrReportTest extends BaseWebDriverTest protected static final Path rmdReport_no_scriptpad = TestFileUtils.getSampleData("reports/knitr_no_scriptpad.rmd").toPath(); private static final Path rhtmlReport = scriptpadReports.resolve("script_rhtml.rhtml"); private static final Path rhtmlReport_no_scriptpad = TestFileUtils.getSampleData("reports/knitr_no_scriptpad.rhtml").toPath(); + private static final Path rhtmlNonceCheck = TestFileUtils.getSampleData("reports/nonce_check.rhtml").toPath(); + private static final Locator.XPathLocator nonceCheckLoc = Locator.id("nonce-check-result"); + private static final Locator.XPathLocator nonceCheckSuccessLoc = nonceCheckLoc.withText("SUCCESS"); + protected final RReportHelper _rReportHelper = new RReportHelper(this); private static String readReport(final Path reportFile) @@ -81,6 +90,7 @@ protected void setupProject() try { new CspConfigHelper(this).setAllowedHosts(Map.of( + ExternalSourcesPage.Directive.Object, List.of("'self'"), // Issue 53226: reports-streamFile is blocked by object-src CSP directive ExternalSourcesPage.Directive.Style, List.of("https://cdn.datatables.net"), ExternalSourcesPage.Directive.Font, List.of("https://mathjax.rstudio.com"))); } @@ -166,7 +176,9 @@ protected void htmlFormat() Locator.tag("pre").containing("## \"1\",249318596,\"2008-05-17\",86,36,129,76,64"), Locator.tag("pre").withText("## knitr says hello to HTML!"), Locator.tag("pre").startsWith("## Error").containing(": non-numeric argument to binary operator"), - Locator.tag("p").startsWith("Well, everything seems to be working. Let's ask R what is the value of \u03C0? Of course it is 3.141")}; + Locator.tag("p").startsWith("Well, everything seems to be working. Let's ask R what is the value of \u03C0? Of course it is 3.141"), + nonceCheckSuccessLoc // Inline script should run + }; String[] reportNotContains = {"", // Uninterpreted html "