Package org.xwiki.test.wysiwyg.framework

Source Code of org.xwiki.test.wysiwyg.framework.AbstractWysiwygTestCase

/*
* 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.test.wysiwyg.framework;

import java.util.Arrays;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.xwiki.test.selenium.framework.AbstractXWikiTestCase;

import com.thoughtworks.selenium.Wait;

/**
* All XWiki WYSIWYG tests must extend this class.
*
* @version $Id: 7c2b96acd3e12ad64b726f10e9e3074b72d7c63a $
*/
public class AbstractWysiwygTestCase extends AbstractXWikiTestCase
{
    private static final String WYSIWYG_LOCATOR_FOR_WYSIWYG_TAB = "//div[@role='tab'][@tabIndex=0]/div[.='WYSIWYG']";

    private static final String WYSIWYG_LOCATOR_FOR_SOURCE_TAB = "//div[@role='tab'][@tabIndex=0]/div[.='Source']";

    /**
     * The title of the indent tool bar button. This title is used in XPath locators to access the indent button.
     */
    public static final String TOOLBAR_BUTTON_INDENT_TITLE = "Increase Indent";

    /**
     * The title of the outdent tool bar button. This title is used in XPath locators to access the outdent button.
     */
    public static final String TOOLBAR_BUTTON_OUTDENT_TITLE = "Decrease Indent";

    /**
     * The title of the undo tool bar button.
     */
    public static final String TOOLBAR_BUTTON_UNDO_TITLE = "Undo (Ctrl+Z)";

    /**
     * The title of the redo tool bar button.
     */
    public static final String TOOLBAR_BUTTON_REDO_TITLE = "Redo (Ctrl+Y)";

    /**
     * The locator for the tool bar list box used to change the style of the current selection.
     */
    public static final String TOOLBAR_SELECT_STYLE = "//select[@title=\"Apply Style\"]";

    /**
     * Locates a menu item by its label.
     */
    public static final String MENU_ITEM_BY_LABEL =
        "//td[contains(@class, 'gwt-MenuItem')]/div[@class = 'gwt-MenuItemLabel' and . = '%s']";

    /**
     * Use this small interval when the operation you are waiting for doesn't execute instantly but pretty fast anyway.
     */
    public static final long SMALL_WAIT_INTERVAL = 50L;

    @Override
    protected void setUp() throws Exception
    {
        super.setUp();

        login();
        open(this.getClass().getSimpleName(), getName(), "edit", "editor=wysiwyg");
        waitForEditorToLoad();
    }

    /**
     * Logs in with the default user for this test case.
     */
    protected void login()
    {
        // Nothing here. Use the default login in the WYSIWYG test setup.
    }

    /**
     * @return the rich text area element
     */
    protected RichTextAreaElement getRichTextArea()
    {
        WebDriver driver = getDriver();
        return new RichTextAreaElement(driver, driver.findElement(By.className("gwt-RichTextArea")));
    }

    /**
     * @return the source text area
     */
    protected WebElement getSourceTextArea()
    {
        return getDriver().findElement(By.className("xPlainTextEditor"));
    }

    /**
     * Sets the content of the rich text area.
     *
     * @param html the new content of the rich text area
     */
    public void setContent(String html)
    {
        getRichTextArea().setContent(html);
    }

    /**
     * Resets the content of the rich text area by selecting all the text like CTRL+A and deleting it using Backspace.
     */
    public void resetContent()
    {
        // We try to mimic as much as possible the user behavior.
        // First, we select all the content.
        selectAllContent();
        // Delete the selected content.
        typeBackspace();
        // We select again all the content. In Firefox, the selection will include the annoying br tag. Further typing
        // will overwrite it. See XWIKI-2732.
        selectAllContent();
    }

    public void selectAllContent()
    {
        getRichTextArea().sendKeys(Keys.chord(Keys.CONTROL, "a"));
    }

    public void typeText(String text)
    {
        getRichTextArea().sendKeys(text);
    }

    public void typeTextThenEnter(String text)
    {
        getRichTextArea().sendKeys(text, Keys.RETURN);
    }

    /**
     * Presses the specified key for the given number of times in WYSIWYG rich text editor.
     *
     * @param key the key to be pressed
     * @param count the number of times to press the specified key
     * @param hold {@code false} if the key should be released after each key press, {@code true} if it should be hold
     *            down and released just at the end
     */
    public void sendKey(Keys key, int count, boolean hold)
    {
        Keys[] sequence = new Keys[count];
        Arrays.fill(sequence, key);
        if (hold) {
            getRichTextArea().sendKeys(Keys.chord(sequence));
        } else {
            getRichTextArea().sendKeys(sequence);
        }
    }

    public void typeEnter()
    {
        typeEnter(1);
    }

    public void typeEnter(int nb)
    {
        sendKey(Keys.RETURN, nb, false);
    }

    public void typeShiftEnter()
    {
        getRichTextArea().sendKeys(Keys.chord(Keys.SHIFT, Keys.RETURN));
    }

    public void typeControlEnter()
    {
        getRichTextArea().sendKeys(Keys.chord(Keys.CONTROL, Keys.RETURN));
    }

    public void typeMetaEnter()
    {
        getRichTextArea().sendKeys(Keys.chord(Keys.META, Keys.RETURN));
    }

    public void typeBackspace()
    {
        typeBackspace(1);
    }

    public void typeBackspace(int count)
    {
        typeBackspace(count, false);
    }

    public void typeBackspace(int count, boolean hold)
    {
        sendKey(Keys.BACK_SPACE, count, hold);
    }

    public void typeLeftArrow()
    {
        getRichTextArea().sendKeys(Keys.ARROW_LEFT);
    }

    public void typeUpArrow()
    {
        getRichTextArea().sendKeys(Keys.ARROW_UP);
    }

    public void typeRightArrow()
    {
        getRichTextArea().sendKeys(Keys.ARROW_RIGHT);
    }

    public void typeDownArrow()
    {
        getRichTextArea().sendKeys(Keys.ARROW_DOWN);
    }

    public void typeDelete()
    {
        typeDelete(1);
    }

    public void typeDelete(int count)
    {
        typeDelete(count, false);
    }

    public void typeDelete(int count, boolean hold)
    {
        sendKey(Keys.DELETE, count, hold);
    }

    public void typeTab()
    {
        typeTab(1);
    }

    public void typeTab(int count)
    {
        sendKey(Keys.TAB, count, false);
    }

    public void typeShiftTab()
    {
        getRichTextArea().sendKeys(Keys.chord(Keys.SHIFT, Keys.TAB));
    }

    public void typeShiftTab(int count)
    {
        for (int i = 0; i < count; i++) {
            typeShiftTab();
        }
    }

    public void clickUnorderedListButton()
    {
        pushToolBarButton("Bullets On/Off");
    }

    public void clickOrderedListButton()
    {
        pushToolBarButton("Numbering On/Off");
    }

    public void clickIndentButton()
    {
        pushToolBarButton(TOOLBAR_BUTTON_INDENT_TITLE);
    }

    public boolean isIndentButtonEnabled()
    {
        return isPushButtonEnabled(TOOLBAR_BUTTON_INDENT_TITLE);
    }

    public void clickOutdentButton()
    {
        pushToolBarButton(TOOLBAR_BUTTON_OUTDENT_TITLE);
    }

    public boolean isOutdentButtonEnabled()
    {
        return isPushButtonEnabled(TOOLBAR_BUTTON_OUTDENT_TITLE);
    }

    public void clickBoldButton()
    {
        pushToolBarButton("Bold (Ctrl+B)");
    }

    public void clickItalicsButton()
    {
        pushToolBarButton("Italic (Ctrl+I)");
    }

    public void clickUnderlineButton()
    {
        pushToolBarButton("Underline (Ctrl+U)");
    }

    public void clickStrikethroughButton()
    {
        pushToolBarButton("Strikethrough");
    }

    public void clickHRButton()
    {
        pushToolBarButton("Insert Horizontal Ruler");
    }

    public void clickSubscriptButton()
    {
        pushToolBarButton("Subscript");
    }

    public void clickSuperscriptButton()
    {
        pushToolBarButton("Superscript");
    }

    public void clickUndoButton()
    {
        pushToolBarButton(TOOLBAR_BUTTON_UNDO_TITLE);
    }

    public void clickUndoButton(int count)
    {
        for (int i = 0; i < count; i++) {
            clickUndoButton();
        }
    }

    public void clickRedoButton()
    {
        pushToolBarButton(TOOLBAR_BUTTON_REDO_TITLE);
    }

    public void clickRedoButton(int count)
    {
        for (int i = 0; i < count; i++) {
            clickRedoButton();
        }
    }

    public void clickSymbolButton()
    {
        pushToolBarButton("Insert Custom Character");
    }

    public void clickOfficeImporterButton()
    {
        pushToolBarButton("Import Office Content");
    }

    public void clickBackToEdit()
    {
        submit("//input[@type = 'submit' and @value = 'Back To Edit']");
        waitForEditorToLoad();
    }

    public void applyStyle(final String style)
    {
        // Wait until the given style is not selected (because the tool bar might not be updated).
        new Wait()
        {
            public boolean until()
            {
                return getSelenium().isEditable(TOOLBAR_SELECT_STYLE)
                    && (!getSelenium().isSomethingSelected(TOOLBAR_SELECT_STYLE) || !style.equals(getSelenium()
                        .getSelectedLabel(TOOLBAR_SELECT_STYLE)));
            }
        }.wait("The specified style, '" + style + "', is already applied!");
        getSelenium().select(TOOLBAR_SELECT_STYLE, style);
    }

    /**
     * Waits for the specified style to be detected.
     *
     * @param style the expected style
     */
    public void waitForStyleDetected(final String style)
    {
        new Wait()
        {
            public boolean until()
            {
                return getSelenium().isSomethingSelected(TOOLBAR_SELECT_STYLE)
                    && style.equals(getSelenium().getSelectedLabel(TOOLBAR_SELECT_STYLE));
            }
        }.wait("The specified style, '" + style + "', wasn't detected!");
    }

    public void applyStylePlainText()
    {
        applyStyle("Plain text");
    }

    public void applyStyleTitle1()
    {
        applyStyle("Title 1");
    }

    public void applyStyleTitle2()
    {
        applyStyle("Title 2");
    }

    public void applyStyleTitle3()
    {
        applyStyle("Title 3");
    }

    public void applyStyleTitle4()
    {
        applyStyle("Title 4");
    }

    public void applyStyleTitle5()
    {
        applyStyle("Title 5");
    }

    public void applyStyleTitle6()
    {
        applyStyle("Title 6");
    }

    public void pushButton(String locator)
    {
        // Can't use : selenium.click(locator);
        // A GWT PushButton is not a standard HTML <input type="submit" ...> or a <button ...>
        // rather it is a styled button constructed from DIV and other HTML tags.
        // Source :
        // http://www.blackpepper.co.uk/black-pepper-blog/Simulating-clicks-on-GWT-push-buttons-with-Selenium-RC.html
        getSelenium().mouseOver(locator);
        getSelenium().mouseDown(locator);
        getSelenium().mouseUp(locator);
        getSelenium().mouseOut(locator);
    }

    /**
     * Pushes the tool bar button with the specified title.
     *
     * @param title the title of the tool bar button to be pushed
     */
    public void pushToolBarButton(String title)
    {
        pushButton("//div[@title='" + title + "']");
    }

    public void clickButtonWithText(String buttonText)
    {
        getSelenium().click("//button[. = \"" + buttonText + "\"]");
    }

    /**
     * Clicks on the menu item with the specified label.
     *
     * @param menuLabel a {@link String} representing the label of a menu item
     */
    public void clickMenu(String menuLabel)
    {
        String selector = String.format(MENU_ITEM_BY_LABEL, menuLabel);
        // We select the menu item first.
        getSelenium().mouseOver(selector);
        // And then we click on it.
        getSelenium().click(selector);
    }

    /**
     * Waits for the specified menu to be present.
     *
     * @param menuLabel the menu label
     */
    public void waitForMenu(String menuLabel)
    {
        waitForElement(String.format(MENU_ITEM_BY_LABEL, menuLabel));
    }

    /**
     * Closes the menu containing the specified menu item by pressing the escape key.
     *
     * @param menuLabel a menu item from the menu to be closed
     */
    public void closeMenuContaining(String menuLabel)
    {
        getSelenium().typeKeys(String.format(MENU_ITEM_BY_LABEL, menuLabel), "\\27");
    }

    /**
     * Switch the WYSIWYG editor by clicking on the "WYSIWYG" tab item and waits for the rich text area to be
     * initialized.
     */
    public void switchToWysiwyg()
    {
        switchToWysiwyg(true);
    }

    /**
     * Switch the WYSIWYG editor by clicking on the "WYSIWYG" tab item.
     *
     * @param wait {@code true} to wait for the rich text area to be initialized, {@code false} otherwise
     */
    public void switchToWysiwyg(boolean wait)
    {
        ensureElementIsNotCoveredByFloatingMenu(By.xpath(WYSIWYG_LOCATOR_FOR_WYSIWYG_TAB));
        getSelenium().click(WYSIWYG_LOCATOR_FOR_WYSIWYG_TAB);
        if (wait) {
            new Wait()
            {
                public boolean until()
                {
                    // When switching between tabs, it sometimes takes longer for toggle buttons to become enabled. We
                    // need to wait for that before we can properly use the WYSIWYG.
                    return !getSourceTextArea().isEnabled()
                        && getSelenium().isElementPresent(
                            "//div[contains(@class, 'gwt-ToggleButton') and not(contains(@class, '-disabled'))]");
                }
            }.wait("Source text area is still editable or buttons are not usable!", Wait.DEFAULT_TIMEOUT, SMALL_WAIT_INTERVAL);
        }
    }

    /**
     * Switch the Source editor by clicking on the "Source" tab item and waits for the plain text area to be
     * initialized.
     */
    public void switchToSource()
    {
        switchToSource(true);
    }

    /**
     * Switch the Source editor by clicking on the "Source" tab item.
     *
     * @param wait {@code true} to wait for the plain text area to be initialized, {@code false} otherwise
     */
    public void switchToSource(boolean wait)
    {
        ensureElementIsNotCoveredByFloatingMenu(By.xpath(WYSIWYG_LOCATOR_FOR_SOURCE_TAB));
        getSelenium().click(WYSIWYG_LOCATOR_FOR_SOURCE_TAB);
        if (wait) {
            new Wait()
            {
                public boolean until()
                {
                    return getDriver().findElement(By.className("xPlainTextEditor")).isEnabled();
                }
            }.wait("Source text area is not editable!", Wait.DEFAULT_TIMEOUT, SMALL_WAIT_INTERVAL);
            // Focus the source text area.
            getSourceTextArea().sendKeys("");
        }
    }

    /**
     * Types the specified text in the input specified by its title.
     *
     * @param inputTitle the {@code title} attribute of the {@code} input element to type in
     * @param text the text to type in the input
     */
    public void typeInInput(String inputTitle, String text)
    {
        getSelenium().type("//input[@title=\"" + inputTitle + "\"]", text);
    }

    /**
     * @param inputTitle the title of the input whose value to return.
     * @return the value of an input specified by its title.
     */
    public String getInputValue(String inputTitle)
    {
        return getSelenium().getValue("//input[@title=\"" + inputTitle + "\"]");
    }

    public boolean isPushButtonEnabled(String pushButtonTitle)
    {
        return getSelenium().isElementPresent(
            "//div[@title='" + pushButtonTitle + "' and @class='gwt-PushButton gwt-PushButton-up']");
    }

    /**
     * @param toggleButtonTitle the tool tip of a toggle button from the WYSIWYG tool bar
     * @return {@code true} if the specified toggle button is enabled, {@code false} otherwise
     */
    public boolean isToggleButtonEnabled(String toggleButtonTitle)
    {
        return getSelenium().isElementPresent(
            "//div[@title='" + toggleButtonTitle
                + "' and contains(@class, 'gwt-ToggleButton') and not(contains(@class, '-disabled'))]");
    }

    /**
     * Waits for the specified push button to have the specified state i.e. enabled or disabled.
     *
     * @param pushButtonTitle identifies the button to wait for
     * @param enabled {@code true} to wait for the specified button to become enabled, {@code false} to wait for it to
     *            become disabled
     */
    public void waitForPushButton(final String pushButtonTitle, final boolean enabled)
    {
        new Wait()
        {
            public boolean until()
            {
                return enabled == isPushButtonEnabled(pushButtonTitle);
            }
        }.wait(pushButtonTitle + " button is not " + (enabled ? "enabled" : "disabled") + "!");
    }

    /**
     * Waits for the specified toggle button be enabled or disabled, based on the given state parameter.
     *
     * @param toggleButtonTitle identifies the button to wait for
     * @param enabled {@code true} to wait for the specified toggle button to become enabled, {@code false} to wait for
     *            it to become disabled
     */
    public void waitForToggleButton(final String toggleButtonTitle, final boolean enabled)
    {
        new Wait()
        {
            public boolean until()
            {
                return enabled == isToggleButtonEnabled(toggleButtonTitle);
            }
        }.wait(toggleButtonTitle + " button is not " + (enabled ? "enabled" : "disabled") + "!");
    }

    /**
     * Waits until the specified toggle button has the given state. This method is useful to wait until a toggle button
     * from the tool bar is updated.
     *
     * @param toggleButtonTitle the tool tip of a toggle button
     * @param down {@code true} to wait until the specified toggle button is down, {@code false} to wait until it is up
     */
    public void waitForToggleButtonState(final String toggleButtonTitle, final boolean down)
    {
        new Wait()
        {
            public boolean until()
            {
                return down == isToggleButtonDown(toggleButtonTitle);
            }
        }.wait("The state of the '" + toggleButtonTitle + "' toggle button didn't change!");
    }

    /**
     * @param toggleButtonTitle the tool tip of a toggle button
     * @return {@code true} if the specified toggle button is down, {@code false} otherwise
     */
    public boolean isToggleButtonDown(String toggleButtonTitle)
    {
        return getSelenium().isElementPresent(
            "//div[@title='" + toggleButtonTitle + "' and @class='gwt-ToggleButton gwt-ToggleButton-down']");
    }

    /**
     * Checks if a menu item is enabled or disabled. Menu items have {@code gwt-MenuItem} CSS class. Disabled menu items
     * have and additional {@code gwt-MenuItem-disabled} CSS class.
     *
     * @param menuLabel a {@link String} representing the label of a menu item
     * @return {@code true} if the menu with the specified label is enabled, {@code false} otherwise
     */
    public boolean isMenuEnabled(String menuLabel)
    {
        return getSelenium().isElementPresent(
            "//td[contains(@class, 'gwt-MenuItem') and not(contains(@class, 'gwt-MenuItem-disabled'))]"
                + "/div[@class = 'gwt-MenuItemLabel' and . = '" + menuLabel + "']");
    }

    /**
     * Asserts that the rich text area has the expected inner HTML.
     *
     * @param expectedHTML the expected inner HTML of the rich text area
     */
    public void assertContent(String expectedHTML)
    {
        assertEquals(expectedHTML, getRichTextArea().getContent());
    }

    /**
     * Places the caret in the specified container, at the specified offset.
     *
     * @param containerJSLocator the JavaScript code used to access the container node
     * @param offset the offset within the container node
     */
    public void moveCaret(String containerJSLocator, int offset)
    {
        StringBuilder script = new StringBuilder();
        script.append("var range = document.createRange();\n");
        script.append("range.setStart(");
        script.append(containerJSLocator);
        script.append(", ");
        script.append(offset);
        script.append(");\n");
        script.append("range.collapse(true);\n");
        script.append("window.getSelection().removeAllRanges();\n");
        script.append("window.getSelection().addRange(range);");
        getRichTextArea().executeScript(script.toString());
        triggerToolbarUpdate();
    }

    /**
     * Selects the content between the specified points in the DOM tree.
     *
     * @param startContainerJSLocator the node containing the start of the selection
     * @param startOffset the offset within the start container where the selection starts
     * @param endContainerJSLocator the node containing the end of the selection
     * @param endOffset the offset within the end container where the selection ends
     */
    public void select(String startContainerJSLocator, int startOffset, String endContainerJSLocator, int endOffset)
    {
        StringBuilder script = new StringBuilder();
        script.append("var range = document.createRange();\n");
        script.append("range.setStart(");
        script.append(startContainerJSLocator);
        script.append(", ");
        script.append(startOffset);
        script.append(");\n");
        script.append("range.setEnd(");
        script.append(endContainerJSLocator);
        script.append(", ");
        script.append(endOffset);
        script.append(");\n");
        script.append("window.getSelection().removeAllRanges();\n");
        script.append("window.getSelection().addRange(range);\n");
        getRichTextArea().executeScript(script.toString());
        triggerToolbarUpdate();
    }

    /**
     * Selects the specified DOM node.
     *
     * @param jsLocator a JavaScript locator for the node to be selected
     */
    public void selectNode(String jsLocator)
    {
        StringBuilder script = new StringBuilder();
        script.append("var range = document.createRange();\n");
        script.append("range.selectNode(");
        script.append(jsLocator);
        script.append(");\n");
        script.append("window.getSelection().removeAllRanges();\n");
        script.append("window.getSelection().addRange(range);\n");
        getRichTextArea().executeScript(script.toString());
        triggerToolbarUpdate();
    }

    /**
     * Selects the contents of the specified DOM node.
     *
     * @param jsLocator a JavaScript locator for the node whose content are to be selected
     */
    public void selectNodeContents(String jsLocator)
    {
        StringBuilder script = new StringBuilder();
        script.append("var range = document.createRange();\n");
        script.append("range.selectNodeContents(");
        script.append(jsLocator);
        script.append(");\n");
        script.append("window.getSelection().removeAllRanges();\n");
        script.append("window.getSelection().addRange(range);\n");
        getRichTextArea().executeScript(script.toString());
        triggerToolbarUpdate();
    }

    /**
     * Triggers the wysiwyg toolbar update by typing a key. To be used after programatically setting the selection (with
     * {@link AbstractWysiwygTestCase#select(String, int, String, int)} or
     * {@link AbstractWysiwygTestCase#moveCaret(String, int)}): it will not influence the selection but it will cause
     * the toolbar to update according to the new selection.
     */
    protected void triggerToolbarUpdate()
    {
        getRichTextArea().sendKeys(Keys.SHIFT);
    }

    /**
     * Wait for a WYSIWYG dialog to close. The test checks for a {@code div} element with {@code xDialogBox} value of
     * {@code class} to not be present.
     */
    public void waitForDialogToClose()
    {
        new Wait()
        {
            public boolean until()
            {
                return !getSelenium().isElementPresent("//div[contains(@class, 'xDialogBox')]");
            }
        }.wait("The dialog didn't close in a decent amount of time!");
    }

    /**
     * Waits until a WYSIWYG modal dialog is fully loaded. While loading, the body of the dialog has the {@code loading}
     * CSS class besides the {@code xDialogBody} one.
     */
    public void waitForDialogToLoad()
    {
        new Wait()
        {
            public boolean until()
            {
                return getSelenium().isElementPresent(
                    "//div[contains(@class, 'xDialogBody') and not(contains(@class, 'loading'))]");
            }
        }.wait("The dialog didn't load in a decent amount of time!");
    }

    /**
     * Close the dialog by clicking the close icon in the top right.
     */
    public void closeDialog()
    {
        getSelenium().click("//img[contains(@class, \"gwt-Image\") and contains(@class, \"xDialogCloseIcon\")]");
        waitForDialogToClose();
    }

    /**
     * Waits until the WYSIWYG editor detects the bold style on the current selection. The bold style is detected when
     * the associated tool bar button is updated. The update is delayed to increase the typing speed.
     */
    public void waitForBoldDetected(boolean down)
    {
        waitForToggleButtonState("Bold (Ctrl+B)", down);
    }

    /**
     * Waits until the WYSIWYG editor detects the underline style on the current selection. The underline style is
     * detected when the associated tool bar button is updated. The update is delayed to increase the typing speed.
     */
    public void waitForUnderlineDetected(boolean down)
    {
        waitForToggleButtonState("Underline (Ctrl+U)", down);
    }

    /**
     * Asserts that the specified error message exists, and the element passed through its XPath locator is marked as in
     * error.
     *
     * @param errorMessage the expected error message
     * @param fieldXPathLocator the XPath locator of the field which is in error
     */
    public void assertFieldErrorIsPresent(String errorMessage, String fieldXPathLocator)
    {
        // test that the error field is present through this method because the isVisible stops at first encouter of the
        // matching element and fails if it's not visible. However, multiple matching elements might exist and we're
        // interested in at least one of them visible
        assertTrue(getSelenium().getXpathCount(
            "//*[contains(@class, \"xErrorMsg\") and . = '" + errorMessage + "' and @style='']").intValue() > 0);
        assertElementPresent(fieldXPathLocator + "[contains(@class, 'xErrorField')]");
    }

    /**
     * Asserts that the specified error message does not exist and that the field passed through the XPath locator is
     * not in error. Note that this function checks that the passed field is present, but without an error marker.
     *
     * @param errorMessage the error message
     * @param fieldXPathLocator the XPath locator of the field to check that it's not in error
     */
    public void assertFieldErrorIsNotPresent(String errorMessage, String fieldXPathLocator)
    {
        assertFalse(getSelenium().isVisible("//*[contains(@class, \"xErrorMsg\") and . = \"" + errorMessage + "\"]"));
        assertTrue(isElementPresent(fieldXPathLocator + "[not(contains(@class, 'xFieldError'))]"));
    }

    /**
     * Asserts that no error message or field marked as in error is present.
     */
    public void assertFieldErrorIsNotPresent()
    {
        // no error is visible
        assertFalse(getSelenium().isVisible("//*[contains(@class, \"xErrorMsg\")]"));
        // no field with error markers should be present
        assertFalse(isElementPresent("//*[contains(@class, 'xFieldError')]"));
    }

    /**
     * Focuses the rich text area.
     * <p>
     * NOTE: The initial range CAN differ when the browser window is focused from when it isn't! Make sure you place the
     * caret where you want it to be at the beginning of you test and after switching back to WYSIWYG editor.
     */
    protected void focusRichTextArea()
    {
        getRichTextArea().sendKeys("");
    }

    /**
     * Simulates a blur event on the rich text area. We don't use the blur method because it fails to notify our
     * listeners when the browser window is not focused, preventing us from running the tests in background.
     */
    protected void blurRichTextArea()
    {
        getDriver().findElement(By.id("xwikidoctitleinput")).sendKeys("");
    }

    /**
     * Inserts a table in place of the current selection or at the caret position, using the default table settings.
     */
    protected void insertTable()
    {
        openInsertTableDialog();
        getSelenium().click("//button[text()=\"Insert Table\"]");
    }

    /**
     * Opens the insert table dialog.
     */
    protected void openInsertTableDialog()
    {
        clickMenu("Table");
        clickMenu("Insert Table...");
        waitForDialogToLoad();
    }

    /**
     * @return the text from the source text area
     */
    protected String getSourceText()
    {
        return getSourceTextArea().getAttribute("value");
    }

    /**
     * Sets the value of the source text area.
     *
     * @param sourceText the new value for the source text area
     */
    protected void setSourceText(String sourceText)
    {
        ((JavascriptExecutor) getDriver()).executeScript("arguments[0].value = arguments[1]", getSourceTextArea(),
            sourceText);
    }

    /**
     * Asserts that the source text area has the given value.
     *
     * @param expectedSourceText the expected value of the source text area
     */
    protected void assertSourceText(String expectedSourceText)
    {
        assertEquals(expectedSourceText, getSourceText());
    }

    /**
     * Waits for the WYSIWYG editor to load.
     */
    protected void waitForEditorToLoad()
    {
        final String sourceTabSelected = "//div[@class = 'gwt-TabBarItem gwt-TabBarItem-selected']/div[. = 'Source']";
        final String richTextArea = "//div[@class = 'xRichTextEditor']";
        final String richTextAreaLoader = richTextArea + "//div[@class = 'loading']";
        new Wait()
        {
            public boolean until()
            {
                // Either the source tab is present and selected and the plain text area can be edited or the rich text
                // area is not loading (with or without tabs).
                return (getSelenium().isElementPresent(sourceTabSelected) && getSourceTextArea().isEnabled())
                    || (getSelenium().isElementPresent(richTextArea) && !getSelenium().isElementPresent(
                        richTextAreaLoader));
            }
        }.wait("The WYSIWYG editor failed to load in a decent amount of time!");
    }

    /**
     * Switches to full screen editing mode.
     */
    protected void clickEditInFullScreen()
    {
        getSelenium().click("//img[@title = 'Maximize']");
        waitForElement("//div[@class = 'fullScreenWrapper']");
    }

    /**
     * Exists full screen editing mode.
     */
    protected void clickExitFullScreen()
    {
        getSelenium().click("//input[@value = 'Exit full screen']");
        waitForElementNotPresent("//div[@class = 'fullScreenWrapper']");
    }

    /**
     * Waits until the specified element is not present.
     *
     * @param locator specifies the element to wait for
     */
    protected void waitForElementNotPresent(final String locator)
    {
        new Wait()
        {
            public boolean until()
            {
                return !isElementPresent(locator);
            }
        }.wait("The specified element, " + locator + ", is still present!");
    }

    /**
     * Creates a new space with the specified name.
     *
     * @param spaceName the name of the new space to create
     */
    public void createSpace(String spaceName)
    {
        getSelenium().runScript("window.scrollTo(0, 0)");
        getSelenium().click("//div[@id='tmCreate']//button[contains(@class, 'dropdown-toggle')]");
        clickLinkWithLocator("tmCreateSpace");
        getSelenium().type("space", spaceName);
        clickLinkWithLocator("//input[@value='Create']");
        clickEditSaveAndView();
    }

    /**
     * Creates a page in the specified space, with the specified name.
     * <p>
     * NOTE: We overwrite the method from the base class because it creates the new page using the URL and thus requires
     * special characters in space and page name to be escaped. We use instead the create page form.
     *
     * @param spaceName the name of the space where to create the page
     * @param pageName the name of the page to create
     * @param content the content of the new page
     * @see AbstractXWikiTestCase#createPage(String, String, String)
     */
    public void createPage(String spaceName, String pageName, String content)
    {
        getSelenium().runScript("window.scrollTo(0, 0)");
        getSelenium().click("//div[@id='tmCreate']//a[contains(@class, 'btn')]");
        getSelenium().type("space", spaceName);
        getSelenium().type("page", pageName);
        clickLinkWithLocator("//input[@value='Create']");
        String location = getSelenium().getLocation();
        if (location.endsWith("?xpage=docalreadyexists")) {
            open(location.substring(0, location.length() - 23));
            clickEditPageInWysiwyg();
        }
        waitForEditorToLoad();
        switchToSource();
        setSourceText(content);
        clickEditSaveAndView();
    }

    /**
     * Selects the rich text area frame. Selectors are relative to the edited document after calling this method.
     */
    public void selectRichTextAreaFrame()
    {
        WebDriver driver = getDriver();
        driver.switchTo().frame(driver.findElement(By.className("gwt-RichTextArea"))).switchTo().activeElement();
    }

    /**
     * Selects the top frame.
     */
    public void selectTopFrame()
    {
        getSelenium().selectFrame("relative=top");
    }
}
TOP

Related Classes of org.xwiki.test.wysiwyg.framework.AbstractWysiwygTestCase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.