Skip to content
Snippets Groups Projects
Commit a3e0a9c9 authored by Marius Dumitru Florea's avatar Marius Dumitru Florea Committed by github-actions[bot]
Browse files

XWIKI-21949: Restrict the execution of script macros during a realtime WYSIWYG editing session

* Add functional tests for limiting script right and for editing translations
* Move "multi users" tests to RealtimeWYSIWYGEditorIT and use the new multi user test "framework"
* Add page objects to interact with the macro content field on the Macro Edit modal

(cherry picked from commit 49030bf1)
parent f7cf3cd4
No related branches found
No related tags found
No related merge requests found
Showing
with 494 additions and 230 deletions
......@@ -81,6 +81,49 @@ public WebElement getMacroParameterInput(String name)
By.cssSelector("[class*=-editor-modal] .macro-parameter-field input[name='" + name + "']"));
}
/**
* Set the value of the macro content.
*
* @param content the macro content
* @return this modal
* @since 15.10.12
* @since 16.4.1
* @since 16.6.0RC1
*/
public MacroDialogEditModal setMacroContent(CharSequence... content)
{
WebElement contentInput = getMacroContentInput();
contentInput.clear();
contentInput.sendKeys(content);
return this;
}
/**
* Retrieves the value of the macro content from the macro editor modal.
*
* @return the value of the macro content
* @since 15.10.12
* @since 16.4.1
* @since 16.6.0RC1
*/
public String getMacroContent()
{
return getMacroContentInput().getAttribute("value");
}
/**
* @return the text area used to edit the macro content
* @since 15.10.12
* @since 16.4.1
* @since 16.6.0RC1
*/
public WebElement getMacroContentInput()
{
return getDriver().findElementWithoutWaitingWithoutScrolling(
// We match *-editor-modal so the page object can be used both in Dashboard and CKEditor tests.
By.cssSelector("[class*=-editor-modal] .macro-parameter-field textarea[name='$content']"));
}
/**
* Click on the macro submission button.
*/
......
......@@ -120,6 +120,24 @@ public void click()
}
}
/**
* Clicks somewhere on the edited content.
*
* @param contentSelector specifies an element from the edited content to click on
* @since 15.10.11
* @since 16.4.1
* @since 16.6.0RC1
*/
public void click(By contentSelector)
{
try {
WebElement rootEditableElement = getRootEditableElement();
getDriver().findElementWithoutWaiting(rootEditableElement, contentSelector).click();
} finally {
maybeSwitchToDefaultContent();
}
}
protected void maybeSwitchToEditedContent()
{
if (this.isFrame) {
......
......@@ -27,6 +27,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.xwiki.ckeditor.test.po.CKEditorConfigurationPane;
import org.xwiki.ckeditor.test.ui.AbstractCKEditorIT;
import org.xwiki.test.docker.junit5.MultiUserTestUtils;
import org.xwiki.test.docker.junit5.TestReference;
import org.xwiki.test.ui.TestUtils;
......@@ -40,15 +41,9 @@
@ExtendWith(RealtimeTestDebugger.class)
abstract class AbstractRealtimeWYSIWYGEditorIT extends AbstractCKEditorIT
{
protected static String firstTabHandle;
@BeforeAll
static void beforeAll(TestUtils setup)
{
// Store the ID of the current browser tab because we're going to open new tabs which will have to be closed
// after each test, and we need this to avoid closing all tabs.
firstTabHandle = setup.getDriver().getWindowHandle();
// Enable the real-time WYSIWYG editor.
setup.loginAsSuperAdmin();
CKEditorConfigurationPane ckeditorConfig = CKEditorConfigurationPane.open();
......@@ -61,19 +56,19 @@ static void beforeAll(TestUtils setup)
}
@AfterEach
void afterEach(TestUtils setup, TestReference testReference)
void afterEach(TestUtils setup, MultiUserTestUtils multiUserSetup, TestReference testReference)
{
// Handle the edit mode leave confirmation modal (when ther are unsaved changes).
// Handle the edit mode leave confirmation modal (when there are unsaved changes).
setup.getDriver().getWindowHandles().forEach(handle -> {
setup.getDriver().switchTo().window(handle);
multiUserSetup.switchToBrowserTab(handle);
maybeLeaveEditMode(setup, testReference);
});
// Close all tabs except the first one.
setup.getDriver().getWindowHandles().stream().filter(handle -> !handle.equals(firstTabHandle))
.forEach(handle -> setup.getDriver().switchTo().window(handle).close());
multiUserSetup.closeTabs();
}
// Switch back to the first tab.
setup.getDriver().switchTo().window(firstTabHandle);
protected void loginAsBob(TestUtils setup)
{
setup.createUserAndLogin("Bob", "pass", "editor", "Wysiwyg");
}
}
......@@ -51,7 +51,8 @@
// Solr search is used to get suggestions for the link quick action.
"org.xwiki.platform:xwiki-platform-search-solr-query"
}
},
servletEngineNetworkAliases = RealtimeWYSIWYGEditorIT.XWIKI_ALIAS
)
class AllIT
{
......@@ -60,10 +61,4 @@ class AllIT
class NestedRealtimeWYSIWYGEditorIT extends RealtimeWYSIWYGEditorIT
{
}
@Nested
@DisplayName("Realtime Multi-User WYSIWYG Editor Tests")
class NestedRealtimeWYSIWYGMultiUserIT extends RealtimeWYSIWYGMultiUserIT
{
}
}
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.realtime.wysiwyg.test.ui;
import static org.junit.jupiter.api.Assertions.assertFalse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WindowType;
import org.xwiki.realtime.wysiwyg.test.po.RealtimeCKEditor;
import org.xwiki.realtime.wysiwyg.test.po.RealtimeRichTextAreaElement;
import org.xwiki.realtime.wysiwyg.test.po.RealtimeWYSIWYGEditPage;
import org.xwiki.test.docker.junit5.TestReference;
import org.xwiki.test.docker.junit5.UITest;
import org.xwiki.test.ui.TestUtils;
/**
* Multi User functional tests for the Realtime WYSIWYG Editor.
*
* @version $Id$
* @since 15.10.11
* @since 16.4.1
* @since 16.5.0RC1
*/
@UITest(properties = {"xwikiDbHbmCommonExtraMappings=notification-filter-preferences.hbm.xml",}, extraJARs = {
// The WebSocket end-point implementation based on XWiki components needs to be installed as core extension.
"org.xwiki.platform:xwiki-platform-websocket",
// It's currently not possible to install a JAR contributing a Hibernate mapping file as an Extension. Thus
// we need to provide the JAR inside WEB-INF/lib. See https://jira.xwiki.org/browse/XWIKI-8271
"org.xwiki.platform:xwiki-platform-notifications-filters-default",})
class RealtimeWYSIWYGMultiUserIT extends AbstractRealtimeWYSIWYGEditorIT
{
private static final String TEST_STRING = "Hello from Bob!";
// Note: There is currently no solution to have multiple browser sessions running at the same time with Selenium.
// The following tests take advantage of the fact that the user doesn't get disconnected of a realtime session
// when logging out on another tab.
@BeforeEach
void beforeEach(TestUtils setup, TestReference testReference)
{
// When working with different users during tests, there is a risk to end up logged in any account.
// We make sure that we are logged-in as alice when starting each test.
setup.createUserAndLogin("alice", "pass", "editor", "Wysiwyg");
}
@Test
@Order(1)
void noLockWarningSameEditor(TestUtils setup, TestReference testReference)
{
//
// First Tab
//
// We are already logged-in as alice, edit the page as alice, effectively locking it.
RealtimeWYSIWYGEditPage firstEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference);
RealtimeCKEditor firstEditor = firstEditPage.getContenEditor();
RealtimeRichTextAreaElement firstTextArea = firstEditor.getRichTextArea();
//
// Second Tab
//
setup.getDriver().switchTo().newWindow(WindowType.TAB).getWindowHandle();
// Log in as bob. This will log alice out but will not release the lock nor terminate her realtime session.
setup.createUserAndLogin("bob", "pass", "editor", "Wysiwyg");
// Edit the same page as bob, alice still has the lock, but because she is active in the realtime session
// no warning message should be handled.
RealtimeWYSIWYGEditPage secondEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference);
// If we get the editor, that means there was no warning.
RealtimeCKEditor secondEditor = secondEditPage.getContenEditor();
// Write some text to check that we joined the same session.
RealtimeRichTextAreaElement secondTextArea = secondEditor.getRichTextArea();
secondTextArea.sendKeys(TEST_STRING);
//
// First Tab
//
setup.getDriver().switchTo().window(firstTabHandle);
firstTextArea.waitUntilTextContains(TEST_STRING);
}
@Test
@Order(2)
void lockWarningSameEditor(TestUtils setup, TestReference testReference)
{
//
// First Tab
//
// We are already logged-in as alice, edit the page as alice, effectively locking it.
RealtimeWYSIWYGEditPage firstEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference);
firstEditPage.getContenEditor();
// Leaving the realtime session should not release the lock.
firstEditPage.leaveRealtimeEditing();
//
// Second Tab
//
setup.getDriver().switchTo().newWindow(WindowType.TAB).getWindowHandle();
// Log in as bob. This will log alice out but will not release the lock.
setup.createUserAndLogin("bob", "pass", "editor", "Wysiwyg");
// Edit the same page as bob, alice still has the lock, but because she is not active in the realtime session
// a warning message should appear.
setup.gotoPage(testReference, "edit", "editor=wysiwyg");
// Check that we did not get to the edit page.
assertFalse(setup.isInWYSIWYGEditMode());
}
@Test
@Order(3)
void lockWarningWysiwygAndWikiEditors(TestUtils setup, TestReference testReference)
{
//
// First Tab
//
// We are already logged-in as alice, edit the page as alice, effectively locking it.
RealtimeWYSIWYGEditPage firstEditPage = RealtimeWYSIWYGEditPage.gotoPage(testReference);
RealtimeCKEditor firstEditor = firstEditPage.getContenEditor();
//
// Second Tab
//
setup.getDriver().switchTo().newWindow(WindowType.TAB).getWindowHandle();
// Log in as bob. This will log alice out but will not release the lock nor terminate her realtime session.
setup.createUserAndLogin("bob", "pass", "editor", "Wysiwyg");
// Edit the same page as bob, alice still has the lock, but because she is using a different editor
// a warning message should appear.
setup.gotoPage(testReference, "edit", "editor=wiki");
// Check that we did not get to the edit page.
assertFalse(setup.isInWYSIWYGEditMode());
}
}
......@@ -125,8 +125,8 @@ public String getUserId()
*/
public boolean isEditingAlone()
{
return "Editing alone"
.equals(getDriver().findElementWithoutWaiting(getContainer(), By.className("rt-user-list")).getText());
return getCoeditors().isEmpty() && !getDriver()
.findElementWithoutWaiting(getContainer(), By.className("rt-user-list")).getText().contains(":");
}
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment