/* * 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.ui.po.editor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.commons.lang3.LocaleUtils; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.ui.Select; import org.xwiki.test.ui.po.BasePage; import org.xwiki.test.ui.po.InlinePage; import org.xwiki.test.ui.po.ViewPage; /** * Represents the common actions possible on all Pages when using the "edit" action. * * @version $Id: b692f12b6bf71e57edb805ff6c3fbe92f9b7e75d $ * @since 3.2M3 */ public class EditPage extends BasePage { @FindBy(name = "action_saveandcontinue") protected WebElement saveandcontinue; @FindBy(name = "action_save") protected WebElement save; @FindBy(name = "action_cancel") protected WebElement cancel; @FindBy(id = "editcolumn") protected WebElement currentEditorDiv; @FindBy(id = "xwikidocsyntaxinput2") protected WebElement syntaxIdSelect; @FindBy(name = "parent") private WebElement parentInput; @FindBy(id = "xwikidoctitleinput") private WebElement titleField; /** * The top floating edit menu bar. */ @FindBy(id = "editmenu") private WebElement editMenuBar; /** * The entry on the edit menu bar that displays the current editor and allows us to switch the editor. */ @FindBy(id = "tmCurrentEditor") private WebElement currentEditorMenu; /** * Enumerates the available editors. */ public enum Editor { WYSIWYG("WYSIWYG"), WIKI("Wiki"), RIGHTS("Access Rights"), OBJECT("Objects"), CLASS("Class"); /** * The mapping between pretty names and editors. */ private static final Map<String, Editor> BY_PRETTY_NAME = new HashMap<String, Editor>(); static { // NOTE: We cannot refer to a static enum field within the initializer because enums are initialized before // any static initializers are run so we are forced to use a static block to build the map. for (Editor editor : values()) { BY_PRETTY_NAME.put(editor.getPrettyName(), editor); } } /** * The string used to display the name of the editor on the edit menu. */ private final String prettyName; /** * Defines a new editor with the given pretty name. * * @param prettyName the string used to display the name of the editor on the edit menu */ Editor(String prettyName) { this.prettyName = prettyName; } /** * @return the string used to display the name of the editor on the edit menu */ public String getPrettyName() { return this.prettyName; } /** * @param prettyName the string used to display the name of the editor on the edit menu * @return the editor corresponding to the given pretty name, {@code null} if no editor matches the given pretty * name */ public static Editor byPrettyName(String prettyName) { return BY_PRETTY_NAME.get(prettyName); } } public void clickSaveAndContinue() { this.clickSaveAndContinue(true); } /** * Clicks on the Save and Continue button. Use this instead of {@link #clickSaveAndContinue()} when you want to wait * for a different message (e.g. an error message). * * @param wait {@code true} to wait for the page to be saved, {@code false} otherwise */ public void clickSaveAndContinue(boolean wait) { this.getSaveAndContinueButton().click(); if (wait) { // Wait until the page is really saved. waitForNotificationSuccessMessage("Saved"); } } /** * Use this method instead of {@link #clickSaveAndContinue()} and call {@link WebElement#click()} when you know that * the next page is not a standard XWiki {@link InlinePage}. * * @return the save and continue button used to submit the form. */ public WebElement getSaveAndContinueButton() { return saveandcontinue; } public <T extends ViewPage> T clickSaveAndView() { clickSaveAndView(true); return (T) new ViewPage(); } /** * Useful when the save and view operation could fail on the client side and a reload (the view part) might thus not * take place. * * @param wait if we should wait for the page to be reloaded * @since 7.4M2 */ public void clickSaveAndView(boolean wait) { if (wait) { getDriver().addPageNotYetReloadedMarker(); } this.getSaveAndViewButton().click(); if (wait) { // Since we might have a loading step between clicking Save&View and the view page actually loading // (specifically when using templates that have child documents associated), we need to wait for the save to // finish and for the redirect to occur. getDriver().waitUntilPageIsReloaded(); } } /** * Use this method instead of {@link #clickSaveAndView()} and call {@link WebElement#click()} when you know that the * next page is not a standard XWiki {@link InlinePage}. * * @return the save and view button used to submit the form. */ public WebElement getSaveAndViewButton() { return save; } public ViewPage clickCancel() { this.cancel.click(); return new ViewPage(); } /** * @return the editor being used on this page */ public Editor getEditor() { String editor = ""; String[] CSSClasses = this.currentEditorDiv.getAttribute("class").split(" "); for (String cssClasse : CSSClasses) { if (cssClasse.startsWith("editor-")) { editor = cssClasse.substring(7); break; } } return Editor.valueOf(editor.toUpperCase()); } /** * @return the syntax if of the page * @since 3.2M3 */ public String getSyntaxId() { return this.syntaxIdSelect.getAttribute("value"); } /** * @since 3.2M3 */ public void setSyntaxId(String syntaxId) { Select select = new Select(this.syntaxIdSelect); select.selectByValue(syntaxId); } /** * @return the value of the parent field. * @since 7.2M2 */ public String getParent() { return this.parentInput.getAttribute("value"); } /** * @since 7.2M2 */ @Override public String getDocumentTitle() { return this.titleField.getAttribute("value"); } /** * @since 7.4M2 */ @Override public void waitUntilPageJSIsLoaded() { super.waitUntilPageJSIsLoaded(); // // Actionbuttons javascript for saving the page. getDriver().waitUntilJavascriptCondition( "return XWiki.actionButtons != undefined && " + "XWiki.actionButtons.EditActions != undefined && " + "XWiki.actionButtons.AjaxSaveAndContinue != undefined"); } protected List<Locale> getExistingLocales(List<WebElement> elements) { List<Locale> locales = new ArrayList<>(elements.size()); for (WebElement element : elements) { locales.add(LocaleUtils.toLocale(element.getText())); } return locales; } /** * @return a list of the locales already translated for this document * @since 9.0RC1 */ public List<Locale> getExistingLocales() { List<WebElement> elements = getDriver().findElementsWithoutWaiting(By.xpath("//p[starts-with(text(), 'Existing translations:')]//a")); return getExistingLocales(elements); } /** * @return a list of the supported locales not yet translated for this document * @since 9.0RC1 */ public List<Locale> getNotExistingLocales() { List<WebElement> elements = getDriver().findElementsWithoutWaiting(By.xpath("//p[starts-with(text(), 'Translate this page in:')]//a")); return getExistingLocales(elements); } /** * @param locale the locale to translate to * @return the target locale edit page * @since 9.0RC1 */ public WikiEditPage clickTranslate(String locale) { WebElement element = getDriver().findElementWithoutWaiting( By.xpath("//p[starts-with(text(), 'Translate this page in:')]//a[text()='" + locale + "']")); element.click(); return new WikiEditPage(); } }