From 6c15be5270c84e15a02f0d2f768a3935fca373e5 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 3 Mar 2025 14:28:43 -0800 Subject: [PATCH 1/2] Update WikiTest to prevent CSP warnings --- .../test/pages/admin/ExternalSourcesPage.java | 143 ++++++++++++++++++ src/org/labkey/test/tests/wiki/WikiTest.java | 12 +- 2 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 src/org/labkey/test/pages/admin/ExternalSourcesPage.java diff --git a/src/org/labkey/test/pages/admin/ExternalSourcesPage.java b/src/org/labkey/test/pages/admin/ExternalSourcesPage.java new file mode 100644 index 0000000000..f0b18068cf --- /dev/null +++ b/src/org/labkey/test/pages/admin/ExternalSourcesPage.java @@ -0,0 +1,143 @@ +package org.labkey.test.pages.admin; + +import org.labkey.test.Locator; +import org.labkey.test.WebDriverWrapper; +import org.labkey.test.WebTestHelper; +import org.labkey.test.components.html.Input; +import org.labkey.test.components.html.OptionSelect; +import org.labkey.test.components.html.Table; +import org.labkey.test.pages.LabKeyPage; +import org.labkey.test.util.LogMethod; +import org.labkey.test.util.LoggedParam; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +/** + * Wraps `AdminController.ExternalSourceAction` + */ +public class ExternalSourcesPage extends LabKeyPage +{ + public ExternalSourcesPage(WebDriver driver) + { + super(driver); + } + + public static ExternalSourcesPage beginAt(WebDriverWrapper webDriverWrapper) + { + webDriverWrapper.beginAt(WebTestHelper.buildURL("admin", "externalSources")); + return new ExternalSourcesPage(webDriverWrapper.getDriver()); + } + + @LogMethod + public ExternalSourcesPage ensureHost(Directive directive, String host) + { + if (!getExistingHosts().getOrDefault(directive, Collections.emptyList()).contains(host)) + { + return addHost(directive, host); + } + else + { + log("Host for CSP " + directive.getDirectiveId() + " already registered: " + host); + return this; + } + } + + @LogMethod (quiet = true) + public ExternalSourcesPage addHost(@LoggedParam Directive directive, @LoggedParam String host) + { + elementCache().directiveSelect.selectOption(directive); + elementCache().hostInput.set(host); + + clickAndWait(elementCache().addButton); + clearCache(); + return this; + } + + public Map> getExistingHosts() + { + Map> existingHosts = new HashMap<>(); + + for (Map.Entry> entry : getExistingHostInputs().entrySet()) + { + existingHosts.put(entry.getKey(), entry.getValue().stream().map(Input::getValue).toList()); + } + + return existingHosts; + } + + public Map> getExistingHostInputs() + { + List directiveColumn = elementCache().existingValuesTable.getColumnAsElement(1, 0); + List hostsColumn = elementCache().existingValuesTable.getColumnAsElement(2, 0); + + Map> existingHosts = new HashMap<>(); + + for (int i = 0; i < hostsColumn.size(); i++) + { + WebElement directiveInput = Locator.tag("input").findElement(directiveColumn.get(i)); + Directive directive = Directive.valueOf(directiveInput.getDomAttribute("data-directive")); + Input hostInput = Input.Input(Locator.tag("input"), getDriver()).find(hostsColumn.get(i)); + existingHosts.computeIfAbsent(directive, d -> new ArrayList<>()).add(hostInput); + } + + return existingHosts; + } + + @Override + protected ElementCache newElementCache() + { + return new ElementCache(); + } + + protected class ElementCache extends LabKeyPage.ElementCache + { + final WebElement addHostForm = Locator.name("addNewHost").findWhenNeeded(this); + final OptionSelect directiveSelect = OptionSelect.finder(Locator.id("newDirective"), Directive.class).findWhenNeeded(addHostForm); + final Input hostInput = Input.Input(Locator.id("newHostTextField"), getDriver()).findWhenNeeded(addHostForm); + final WebElement addButton = Locator.lkButton("Add").findWhenNeeded(addHostForm); + + final WebElement existingValuesForm = Locator.name("existingValues").findWhenNeeded(this); + final Table existingValuesTable = new Table(getDriver(), Locator.byClass("labkey-data-region-legacy").findWhenNeeded(existingValuesForm)); + } + + public enum Directive implements OptionSelect.SelectOption + { + Connection("connect-src"), + Font("font-src"), + Frame("frame-src"), + Image("image-src"), + Style("style-src"), + ; + + private final String directiveId; + + Directive(String directiveId) + { + this.directiveId = directiveId; + } + + public String getDirectiveId() + { + return directiveId; + } + + @Override + public String getValue() + { + return name(); + } + + public static Directive fromId(String directiveId) + { + return Stream.of(values()).filter(d -> d.getDirectiveId().equals(directiveId)).findAny() + .orElseThrow(() -> new IllegalArgumentException("Unknown CSP directive: " + directiveId)); + } + } +} diff --git a/src/org/labkey/test/tests/wiki/WikiTest.java b/src/org/labkey/test/tests/wiki/WikiTest.java index 43adddc06a..328f044fae 100644 --- a/src/org/labkey/test/tests/wiki/WikiTest.java +++ b/src/org/labkey/test/tests/wiki/WikiTest.java @@ -28,6 +28,8 @@ import org.labkey.test.Locator; import org.labkey.test.categories.Daily; import org.labkey.test.categories.Wiki; +import org.labkey.test.pages.admin.ExternalSourcesPage; +import org.labkey.test.pages.admin.ExternalSourcesPage.Directive; import org.labkey.test.pages.search.SearchResultsPage; import org.labkey.test.pages.wiki.EditPage; import org.labkey.test.util.DataRegionTable; @@ -159,13 +161,17 @@ public void testSteps() @Test public void testEmbeddedVideoInWiki() { + String videoHost = "https://www.youtube.com"; + String videoUrl = videoHost + "/embed/JEE4807UHN4"; String wikiName = "Wiki with video"; String wikiTitle = "Sample finder video"; String wikiContent = """ Some random content start : Have fun watching video below - {video:https://www.youtube.com/embed/JEE4807UHN4|height:350|width:500} + {video:%s|height:350|width:500} Hope you had fun watching the video..! - """; + """.formatted(videoUrl); + + ExternalSourcesPage.beginAt(this).ensureHost(Directive.Frame, videoHost); goToProjectHome(); log("Creating the wiki with video"); @@ -177,7 +183,7 @@ public void testEmbeddedVideoInWiki() wikiHelper.setWikiBody(wikiContent); wikiHelper.saveWikiPage(); - Assert.assertEquals("Video is missing", "https://www.youtube.com/embed/JEE4807UHN4", + Assert.assertEquals("Video is missing", videoUrl, getAttribute(Locator.tag("iframe"), "src")); } From 7a46aa1fda137960c523c406d57108518c5d26b6 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 3 Mar 2025 16:42:42 -0800 Subject: [PATCH 2/2] Update Table component to make header row index inherent --- .../components/dumbster/EmailRecordTable.java | 13 ++-- .../labkey/test/components/html/Table.java | 65 +++++-------------- .../test/pages/admin/ExternalSourcesPage.java | 6 +- 3 files changed, 25 insertions(+), 59 deletions(-) diff --git a/src/org/labkey/test/components/dumbster/EmailRecordTable.java b/src/org/labkey/test/components/dumbster/EmailRecordTable.java index 51efc249c3..60854c6de7 100644 --- a/src/org/labkey/test/components/dumbster/EmailRecordTable.java +++ b/src/org/labkey/test/components/dumbster/EmailRecordTable.java @@ -42,7 +42,7 @@ public class EmailRecordTable extends Table public EmailRecordTable(WebDriver driver) { - super(driver, new RefindingWebElement(gridLocator, driver).withTimeout(WAIT_FOR_JAVASCRIPT)); + super(driver, new RefindingWebElement(gridLocator, driver).withTimeout(WAIT_FOR_JAVASCRIPT), _headerRows); ((RefindingWebElement) getComponentElement()).withRefindListener(el -> clearElementCache()); } @@ -67,7 +67,7 @@ public int getColumnIndex(String headerLabel) } catch (IllegalArgumentException fallback) { - return super.getColumnIndex(headerLabel, _headerRows); + return super.getColumnIndex(headerLabel); } } @@ -171,7 +171,7 @@ public List getMessagesByHeaderAndText(String header, String text) public List getTableIndexesWhereTextAppears(String header, String text) { - List columnText = getColumnAsText(header, _headerRows); + List columnText = getColumnAsText(header); List colsWithString = new ArrayList<>(); for(int i = 1; i <= columnText.size(); i++) { @@ -231,12 +231,7 @@ private void parseViewCell(EmailMessage emailMessage) public List getColumnDataAsText(String column) { - return getColumnAsText(column , _headerRows); - } - - public int getHeaderRowCount() - { - return _headerRows; + return getColumnAsText(column); } public static class EmailMessage diff --git a/src/org/labkey/test/components/html/Table.java b/src/org/labkey/test/components/html/Table.java index b7dfdfaf3c..fca62fc405 100644 --- a/src/org/labkey/test/components/html/Table.java +++ b/src/org/labkey/test/components/html/Table.java @@ -34,11 +34,18 @@ public class Table extends WebDriverComponent { private final WebDriver _driver; private final WebElement _componentElement; + private final int _bodyHeaderRowIndex; // '0' indicates that the table uses a 'thead' - public Table(WebDriver driver, WebElement componentElement) + public Table(WebDriver driver, WebElement componentElement, int headerIndex) { _componentElement = componentElement; _driver = driver; + _bodyHeaderRowIndex = headerIndex; + } + + public Table(WebDriver driver, WebElement componentElement) + { + this(driver, componentElement, 1); } public Table(WebDriverWrapper driverWrapper, WebElement componentElement) @@ -141,14 +148,14 @@ public List getTableHeaderColumnData(String headerText) { List columnData = new ArrayList<>(); int columnIndex = getTableHeaderIndex(headerText); - for(int i = 1; i <= getRowCount(); i++) + for(int i = _bodyHeaderRowIndex; i <= getRowCount(); i++) columnData.add(Locator.xpath("//tbody//tr[" + i + "]/td[" + columnIndex + "]").findElement(getDriver()).getText()); return columnData; } - public List getColumnHeaders(int headerRow) + public List getColumnHeaders() { - List headerEls = getColumnHeaderElements(headerRow); + List headerEls = getColumnHeaderElements(); List columnHeaders = new ArrayList<>(); for(WebElement headerEl : headerEls){columnHeaders.add(headerEl.getText());} return columnHeaders; @@ -156,14 +163,9 @@ public List getColumnHeaders(int headerRow) // TODO: This finder makes an assumption that the column headers will be in a tr in the tbody, that is not always the case. // Maybe a possible solution would be to remove "./tbody" from the locator, but that is a thread I am not willing to pull at this time. - private List getColumnHeaderElements(int headerRow) - { - return getComponentElement().findElements(By.xpath("./tbody/tr["+ headerRow +"]/*[(name()='TH' or name()='TD' or name()='th' or name()='td')]")); - } - public List getColumnHeaderElements() { - return getColumnHeaderElements(1); + return getComponentElement().findElements(By.xpath("./tbody/tr["+ _bodyHeaderRowIndex +"]/*[(name()='TH' or name()='TD' or name()='th' or name()='td')]")); } public List getColumnHeaderElementsByTag() @@ -171,15 +173,10 @@ public List getColumnHeaderElementsByTag() return getComponentElement().findElements(By.xpath(".//tr/th")); } - protected int getColumnIndex(String headerLabel, int headerIndex) - { - //List is zero based, locators that are going to depend on this are 1 - return getColumnHeaders(headerIndex).indexOf(headerLabel) + 1; - } - public int getColumnIndex(String headerLabel) { - return getColumnIndex(headerLabel, 1); + //List is zero based, locators that are going to depend on this are 1 + return getColumnHeaders().indexOf(headerLabel) + 1; } public String getDataAsText(int row, int col) @@ -215,9 +212,9 @@ public WebElement getDataAsElement(int row, int column) return _getDataAsElement(row, column); } - public List getColumnAsText(int col, int headerIndex) + public List getColumnAsText(int col) { - List columnElements = getColumnAsElement(col, headerIndex); + List columnElements = getColumnAsElement(col); List columnText = new ArrayList<>(); if (columnElements.size() > 0) @@ -233,12 +230,7 @@ public List getColumnAsText(int col, int headerIndex) public List getColumnAsText(String col) { - return getColumnAsText(getColumnIndex(col), 1); - } - - public List getColumnAsText(String col, int colIndex) - { - return getColumnAsText(getColumnIndex(col, colIndex), colIndex); + return getColumnAsText(getColumnIndex(col)); } public List getRowAsText(int row) @@ -247,29 +239,13 @@ public List getRowAsText(int row) return getWrapper().getTexts(cells); } - public List getColumnAsElement(String name, int columnIndex) - { - int col = getColumnIndex(name, columnIndex); - return getColumnAsElement(col); - } - - public List getColumnAsElement(String name) - { - return getColumnAsElement(name, 1); - } - public List getColumnAsElement(int col) - { - return getColumnAsElement(col,1); - } - - public List getColumnAsElement(int col, int headerIndex) { int rowCount = getRowCount(); List columnElements = new ArrayList<>(); if (rowCount > 0) { - for (int row = headerIndex + 1; row < rowCount; row++) + for (int row = _bodyHeaderRowIndex + 1; row <= rowCount; row++) { columnElements.add(getDataAsElement(row, col)); } @@ -278,11 +254,6 @@ public List getColumnAsElement(int col, int headerIndex) return columnElements; } - public int getRowIndex(String columnLabel, String value, int headerIndex) - { - return getRowIndex(getColumnIndex(columnLabel, headerIndex), value); - } - public int getRowIndex(String columnLabel, String value) { return getRowIndex(getColumnIndex(columnLabel), value); diff --git a/src/org/labkey/test/pages/admin/ExternalSourcesPage.java b/src/org/labkey/test/pages/admin/ExternalSourcesPage.java index f0b18068cf..4bcbf201cb 100644 --- a/src/org/labkey/test/pages/admin/ExternalSourcesPage.java +++ b/src/org/labkey/test/pages/admin/ExternalSourcesPage.java @@ -74,8 +74,8 @@ public Map> getExistingHosts() public Map> getExistingHostInputs() { - List directiveColumn = elementCache().existingValuesTable.getColumnAsElement(1, 0); - List hostsColumn = elementCache().existingValuesTable.getColumnAsElement(2, 0); + List directiveColumn = elementCache().existingValuesTable.getColumnAsElement(1); + List hostsColumn = elementCache().existingValuesTable.getColumnAsElement(2); Map> existingHosts = new HashMap<>(); @@ -104,7 +104,7 @@ protected class ElementCache extends LabKeyPage.ElementCache final WebElement addButton = Locator.lkButton("Add").findWhenNeeded(addHostForm); final WebElement existingValuesForm = Locator.name("existingValues").findWhenNeeded(this); - final Table existingValuesTable = new Table(getDriver(), Locator.byClass("labkey-data-region-legacy").findWhenNeeded(existingValuesForm)); + final Table existingValuesTable = new Table(getDriver(), Locator.byClass("labkey-data-region-legacy").findWhenNeeded(existingValuesForm), 0); } public enum Directive implements OptionSelect.SelectOption