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
Branches XWIKI-19119
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