/* * 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.wysiwyg.test.ui; import java.net.URLDecoder; import org.junit.Assert; import org.junit.Test; import org.openqa.selenium.Keys; import org.openqa.selenium.WebElement; import org.xwiki.test.ui.browser.IgnoreBrowser; import org.xwiki.test.ui.browser.IgnoreBrowsers; import org.xwiki.test.ui.po.editor.wysiwyg.RichTextAreaElement; import org.xwiki.wysiwyg.test.po.EditorElement; import org.xwiki.wysiwyg.test.po.TableConfigPane; import org.xwiki.wysiwyg.test.po.UploadImagePane; import org.xwiki.wysiwyg.test.po.WYSIWYGEditPage; /** * Test WYSIWYG content editing. * * @version $Id: a695e9729ca16e9e706277f9a87619d97ceebf66 $ * @since 3.0M2 */ public class EditWYSIWYGTest extends AbstractWYSIWYGEditorTest { /** * Tests that images are uploaded fine after a preview. * * @see <a href="https://jira.xwiki.org/browse/XWIKI-5895">XWIKI-5895</a>: Adding an image in the WYSIWYG editor * and previewing it without saving the page first makes the XWiki page corrupt. **/ @Test @IgnoreBrowsers({ @IgnoreBrowser(value = "internet.*", version = "8\\.*", reason="See https://jira.xwiki.org/browse/XE-1146"), @IgnoreBrowser(value = "internet.*", version = "9\\.*", reason="See https://jira.xwiki.org/browse/XE-1177") }) public void testUploadImageAfterPreview() throws Exception { this.editPage.clickPreview().clickBackToEdit(); // Recreate the page object because the page has been reloaded. this.editPage = new WYSIWYGEditPage().waitUntilPageIsLoaded(); UploadImagePane uploadImagePane = this.editPage.insertAttachedImage().selectFromCurrentPage().uploadImage(); // URL#getPath() returns the path URL-encoded and the file upload input doesn't expect this. Normally we // shouldn't have special characters in the path but some CI jobs have spaces in their names // (e.g. "xwiki-platform Quality Checks") which are encoded as %20. The file upload hangs if we pass the path // with encoded spaces. String path = URLDecoder.decode(this.getClass().getResource("/image.png").getPath(), "UTF-8"); uploadImagePane.setImageToUpload(path); // Fails if the image configuration step doesn't load in a decent amount of time. uploadImagePane.configureImage(); } /** * @see "XWIKI:7028: Strange behaviour when pressing back and forward on a page that has 2 WYSIWYG editors * displayed." */ @Test @IgnoreBrowsers({ @IgnoreBrowser(value = "internet.*", version = "8\\.*", reason="See https://jira.xwiki.org/browse/XE-1146"), @IgnoreBrowser(value = "internet.*", version = "9\\.*", reason="See https://jira.xwiki.org/browse/XE-1177") }) public void testBackForwardCache() { StringBuilder code = new StringBuilder(); code.append("; Description\n"); code.append(": {{html}}<textarea id=\"description\">f>o**o**</textarea>{{/html}}\n"); code.append("; Summary\n"); code.append(": {{html}}<textarea id=\"summary\">b<a//r//</textarea>{{/html}}\n\n"); code.append("{{velocity}}\n"); code.append("{{html}}\n"); code.append("#wysiwyg_editProperty($tdoc 'description')\n"); code.append("#wysiwyg_editProperty($tdoc 'summary')\n"); code.append("{{/html}}\n"); code.append("{{/velocity}}"); // Create a page with 2 WYSIWYG editors. getUtil().createPage(getTestClassName(), getTestMethodName(), code.toString(), null); EditorElement descriptionEditor = new EditorElement("description").waitToLoad(); EditorElement summaryEditor = new EditorElement("summary").waitToLoad(); String description = descriptionEditor.getRichTextArea().getText(); String summary = summaryEditor.getRichTextArea().getText(); getDriver().navigate().back(); getDriver().navigate().forward(); descriptionEditor = new EditorElement("description").waitToLoad(); summaryEditor = new EditorElement("summary").waitToLoad(); Assert.assertEquals(description, descriptionEditor.getRichTextArea().getText()); Assert.assertEquals(summary, summaryEditor.getRichTextArea().getText()); } /** * Test that the content of the rich text area is preserved when the user refreshes the page. */ @Test @IgnoreBrowsers({ @IgnoreBrowser(value = "internet.*", version = "8\\.*", reason="See https://jira.xwiki.org/browse/XE-1146"), @IgnoreBrowser(value = "internet.*", version = "9\\.*", reason="See https://jira.xwiki.org/browse/XE-1177") }) public void testPreserveUnsavedRichContentAgainstRefresh() { // Type text and refresh the page. this.editPage.getContentEditor().getRichTextArea().sendKeys("2"); this.editPage.sendKeys(Keys.F5); this.editPage = new WYSIWYGEditPage(); EditorElement editor = this.editPage.getContentEditor(); editor.waitToLoad(); // Type more text and check the result. RichTextAreaElement textArea = editor.getRichTextArea(); textArea.sendKeys("1"); Assert.assertEquals("12", textArea.getText()); } /** * Test that the content of the source text area is preserved when the user refreshes the page. */ @Test @IgnoreBrowsers({ @IgnoreBrowser(value = "internet.*", version = "8\\.*", reason="See https://jira.xwiki.org/browse/XE-1146"), @IgnoreBrowser(value = "internet.*", version = "9\\.*", reason="See https://jira.xwiki.org/browse/XE-1177") }) public void testPreserveUnsavedSourceAgainstRefresh() { EditorElement editor = this.editPage.getContentEditor(); editor.switchToSource(); // Type text and refresh the page. editor.getSourceTextArea().sendKeys("1" + Keys.F5); this.editPage = new WYSIWYGEditPage(); editor = this.editPage.getContentEditor(); editor.waitToLoad(); editor.switchToSource(); // Type more text and check the result. editor.getSourceTextArea().sendKeys("2"); Assert.assertEquals("12", editor.getSourceTextArea().getAttribute("value")); } /** * Tests that the currently active editor (WYSIWYG or Source) is preserved when the user refreshes the page. */ @Test @IgnoreBrowsers({ @IgnoreBrowser(value = "internet.*", version = "8\\.*", reason="See https://jira.xwiki.org/browse/XE-1146"), @IgnoreBrowser(value = "internet.*", version = "9\\.*", reason="See https://jira.xwiki.org/browse/XE-1177") }) public void testPreserveSelectedEditorAgainstRefresh() { // The WYSIWYG editor should be initially active. EditorElement editor = this.editPage.getContentEditor(); Assert.assertFalse(editor.getSourceTextArea().isEnabled()); // Switch to Source editor and refresh the page. editor.switchToSource(); editor.getSourceTextArea().sendKeys(Keys.F5); this.editPage = new WYSIWYGEditPage(); editor = this.editPage.getContentEditor(); editor.waitToLoad(); // The Source editor should be active now because it was selected before the refresh. Assert.assertTrue(editor.getSourceTextArea().isEnabled()); // Switch to WYSIWYG editor and refresh the page again. editor.switchToWysiwyg(); this.editPage.sendKeys(Keys.F5); this.editPage = new WYSIWYGEditPage(); editor = this.editPage.getContentEditor(); editor.waitToLoad(); // The WYSIWYG editor should be active now because it was selected before the refresh. Assert.assertFalse(editor.getSourceTextArea().isEnabled()); } /** * Test if an undo step reverts only one paste operation from a sequence, and not all of them. */ @Test @IgnoreBrowsers({ @IgnoreBrowser(value = "internet.*", version = "8\\.*", reason="See https://jira.xwiki.org/browse/XE-1146"), @IgnoreBrowser(value = "internet.*", version = "9\\.*", reason="See https://jira.xwiki.org/browse/XE-1177") }) public void testUndoRepeatedPaste() { EditorElement editor = this.editPage.getContentEditor(); RichTextAreaElement textArea = editor.getRichTextArea(); // Type text, select it (Shift+LeftArrow) and copy it (Control+C). // NOTE: We don't use Control+A to select the text because it selects also the BR element. textArea.sendKeys("q", Keys.chord(Keys.SHIFT, Keys.ARROW_LEFT), Keys.chord(Keys.CONTROL, "c")); // Then paste it 4 times (Control+V). for (int i = 0; i < 4; i++) { // Release the key after each paste so that the history records an entry for each paste. In case the paste // content is cleaned automatically, the editor cleans consecutive paste events (that happen one after // another) together and so a single history entry is recorded for such a group of paste events. textArea.sendKeys(Keys.chord(Keys.CONTROL, "v")); } // Undo the last paste. editor.getToolBar().clickUndoButton(); Assert.assertEquals("qqq", textArea.getText()); } /** * @see "XWIKI-4230: 'Tab' doesn't work in the Table Dialog in FF 3.5.2" */ @Test public void testTabInTableConfigDialog() { TableConfigPane tableConfig = this.editPage.insertTable(); // Assert that the row count input has the focus. Assert.assertEquals(tableConfig.getRowCountInput(), getDriver().switchTo().activeElement()); getDriver().switchTo().defaultContent(); // Press Tab to move the focus to the next input. tableConfig.getRowCountInput().sendKeys(Keys.TAB); // Assert that the column count input has the focus. Assert.assertEquals(tableConfig.getColumnCountInput(), getDriver().switchTo().activeElement()); getDriver().switchTo().defaultContent(); } /** * Test that hitting the . (dot) key at the end of a list item does not act as delete. * * @see <a href="https://jira.xwiki.org/browse/XWIKI-3304">XWIKI-3304</a> */ @Test @IgnoreBrowser(value = "internet.*", version = "8\\.*", reason="See https://jira.xwiki.org/browse/XE-1146") public void testDotAtEndDoesNotDelete() { EditorElement editor = this.editPage.getContentEditor(); // Create a list with two items. editor.switchToSource(); WebElement sourceTextArea = editor.getSourceTextArea(); sourceTextArea.clear(); sourceTextArea.sendKeys("* foo\n* bar"); editor.switchToWysiwyg(); // Place the caret at the end of the first item and type dot. RichTextAreaElement textArea = editor.getRichTextArea(); textArea.sendKeys(Keys.ARROW_RIGHT, Keys.ARROW_RIGHT, Keys.ARROW_RIGHT, "."); Assert.assertEquals("foo.\nbar", textArea.getText()); } /** * @see "XWIKI-3039: Changes are lost if an exception is thrown during saving" * @see "XWIKI-9750: User Input in WYSIWYG Editor is lost after conversion error" */ @Test public void testRecoverAfterConversionException() { EditorElement editor = this.editPage.getContentEditor(); // We removed the startwikilink comment to force a parsing failure. String html = "<span class=\"wikiexternallink\"><a href=\"mailto:x@y.z\">xyz</a></span><!--stopwikilink-->"; editor.getRichTextArea().setContent(html); // Test to see if the HTML was accepted by the rich text area. Assert.assertEquals("xyz", editor.getRichTextArea().getText()); // Save & Continue should notify us about the conversion error. this.editPage.clickSaveAndContinue(false); this.editPage.waitForNotificationErrorMessage("Failed to save the page. " + "Reason: content: Exception while parsing HTML"); // Save & View should redirect us back to the edit mode, but the unsaved changes shouldn't be lost. this.editPage.clickSaveAndView(); this.editPage = new WYSIWYGEditPage().waitUntilPageIsLoaded(); Assert.assertEquals("xyz", this.editPage.getContentEditor().getRichTextArea().getText()); } }