Java tutorial
/* * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the * @author tags. See the copyright.txt file in the distribution for a full * listing of individual contributors. * * 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.zanata.page.webtrans; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; import org.zanata.page.BasePage; import org.zanata.page.editor.ReactEditorPage; import org.zanata.page.projectversion.VersionLanguagesPage; import org.zanata.util.WebElementUtil; import com.google.common.base.Joiner; import static org.assertj.core.api.Assertions.assertThat; /** * @author Patrick Huang * <a href="mailto:pahuang@redhat.com">pahuang@redhat.com</a> */ public class EditorPage extends BasePage { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EditorPage.class); public enum Validations { HTML, JAVAVARIABLES, NEWLINE, POSITIONAL, PRINTF, TABS, XML; } // first %d is row index, second %d is plural form index (i.e. 0 or 1) private static final String SOURCE_ID_FMT = "gwt-debug-%d-source-panel-%d-container"; // first %d is row index, second %d is plural form index (i.e. 0-6) private static final String TARGET_ID_FMT = "gwt-debug-%d-target-%d"; private static final String EDITOR_SYNTAXHIGHLIGHT = "gwt-debug-syntax-highlight-chk-input"; // buttons id format private static final String APPROVE_BUTTON_ID_FMT = "gwt-debug-target-%d-save-approve"; private static final String FUZZY_BUTTON_ID_FMT = "gwt-debug-target-%d-save-fuzzy"; private final By glossaryTable = By.id("gwt-debug-glossaryResultTable"); private final By glossaryNoResult = By.id("gwt-debug-glossaryNoResult"); private By glossarySearchInput = By.id("gwt-debug-glossaryTextBox"); private By transUnitTable = By.id("gwt-debug-transUnitTable"); private By editorFilterField = By.id("gwt-debug-editor-filter-box"); private By configurationPanel = By.className("i--settings"); private By validationBox = By.className("gwt-DisclosurePanel"); private By validationOptions = By.xpath("//a[@title=\'Validation options\']"); private By validationOptionsView = By.id("validationOptionsView"); private By alphaEditorButton = By.linkText("Try the new alpha editor"); public EditorPage(WebDriver driver) { super(driver); } public EditorPage searchGlossary(final String term) { log.info("Search glossary for {}", term); waitForAMoment().withMessage("glossary list is ready") .until(webDriver -> webDriver.findElements(glossaryNoResult).size() == 1 || webDriver.findElements(glossaryTable).size() == 1); readyElement(glossarySearchInput).clear(); enterText(readyElement(glossarySearchInput), term); clickElement(By.id("gwt-debug-glossarySearchButton")); return new EditorPage(getDriver()); } /** * There is usually a long poll waiting for GWTEventService events from the * server. * * @return */ @Override protected int getExpectedBackgroundRequests() { return 1; } /** * First row is header: SourceTerm, TargetTerm, Action, Details. * * @return a table representing the searchResultTable */ public List<List<String>> getGlossaryResultTable() { log.info("Query glossary results"); return waitForAMoment().withMessage("glossary results is not empty").until(webDriver -> { List<List<String>> resultTable; if (webDriver.findElements(glossaryNoResult).size() == 1) { resultTable = Collections.emptyList(); } else { resultTable = WebElementUtil.getTwoDimensionList(webDriver, glossaryTable); } log.info("glossary result: {}", resultTable); return resultTable; }); } /** * Get content of a text flow source at given row. This assumes the text * flow has singular form (i.e. no plural). If a test requires to access * plural content, this can be changed. * * @param rowIndex * row index * @return content of the source */ public String getMessageSourceAtRowIndex(final int rowIndex) { log.info("Query text flow source at {}", rowIndex); return getCodeMirrorContent(rowIndex, SOURCE_ID_FMT, Plurals.SourceSingular); } public String getMessageSourceAtRowIndex(int rowIndex, Plurals plural) { log.info("Query text flow source at {}", rowIndex); return getCodeMirrorContent(rowIndex, SOURCE_ID_FMT, plural); } /** * Get content of a text flow target at given row. This assumes the text * flow has singular form (i.e. no plural). If a test requires to access * plural content, this can be changed. * * @param rowIndex * row index * @return content of the target */ public String getMessageTargetAtRowIndex(final int rowIndex) { log.info("Query text flow target at {}", rowIndex); return getCodeMirrorContent(rowIndex, TARGET_ID_FMT, Plurals.TargetSingular); } private String getCodeMirrorContent(final long rowIndex, final String idFormat, final Plurals plurals) { return waitForAMoment().withMessage("gwt contents available").until(webDriver -> { // code mirror will turn text into list of <pre>. List<WebElement> cmTextLines = webDriver .findElement(By.id(String.format(idFormat, rowIndex, plurals.index()))) .findElements(By.tagName("pre")); List<String> contents = WebElementUtil.elementsToText(cmTextLines); return Joiner.on("\n").skipNulls().join(contents); }); } public EditorPage setSyntaxHighlighting(boolean option) { log.info("Set syntax highlight to {}", option); openConfigurationPanel(); WebElement highlight = readyElement(By.id(EDITOR_SYNTAXHIGHLIGHT)); if (highlight.isSelected() != option) { highlight.click(); } return new EditorPage(getDriver()); } private Boolean openConfigurationPanel() { log.info("Click to open Configuration options"); waitForAMoment().withMessage("config settings button is enabled") .until(webDriver -> getDriver().findElement(By.className("i--settings")).isEnabled()); new Actions(getDriver()).click(readyElement(configurationPanel)).perform(); return waitForAMoment().withMessage("config panel is displayed").until( driver -> driver.findElement(By.className("gwt-TabLayoutPanelContentContainer")).isDisplayed()); } /** * Get content from a target using the non-CodeMirror configuration * * @param rowIndex * @return row target content */ public String getBasicTranslationTargetAtRowIndex(final int rowIndex) { log.info("Query text flow source at {}", rowIndex); return getContentAtRowIndex(rowIndex, TARGET_ID_FMT, Plurals.TargetSingular); } public String getBasicTranslationTargetAtRowIndex(int rowIndex, Plurals plurals) { log.info("Query text flow source at {}", rowIndex); return getContentAtRowIndex(rowIndex, TARGET_ID_FMT, plurals); } public boolean expectBasicTranslationAtRowIndex(final int rowIndex, final String expected) { log.info("Wait for text flow target at {} to be {}", rowIndex, expected); return waitForAMoment().withMessage("expect translation in row ".concat(String.valueOf(rowIndex))) .until(webDriver -> getBasicTranslationTargetAtRowIndex(rowIndex).equals(expected)); } private String getContentAtRowIndex(final long rowIndex, final String idFormat, final Plurals plural) { return readyElement(By.id(String.format(idFormat, rowIndex, plural.index()))).getAttribute("value"); } /** * Translate a target using the non-CodeMirror field * * @param rowIndex * @param text * @return updated EditorPage */ public EditorPage translateTargetAtRowIndex(final int rowIndex, String text) { log.info("Enter at {} the text {}", rowIndex, text); setTargetContent(rowIndex, text, TARGET_ID_FMT, Plurals.SourceSingular); return new EditorPage(getDriver()); } private void setTargetContent(final long rowIndex, final String text, final String idFormat, final Plurals plural) { WebElement we = readyElement(By.id(String.format(idFormat, rowIndex, plural.index()))); we.click(); we.clear(); we.sendKeys(text); } /** * Simulate a paste from the user's clipboard into the indicated row * * @param rowIndex * row to enter text * @param text * text to be entered * @return new EditorPage */ public EditorPage pasteIntoRowAtIndex(final long rowIndex, final String text) { log.info("Paste at {} the text {}", rowIndex, text); WebElement we = readyElement(By.id(String.format(TARGET_ID_FMT, rowIndex, Plurals.SourceSingular.index()))); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(new StringSelection(text), null); we.click(); we.sendKeys(Keys.LEFT_CONTROL + "v"); return new EditorPage(getDriver()); } public EditorPage approveTranslationAtRow(int rowIndex) { log.info("Click Approve on row {}", rowIndex); readyElement(By.id(String.format(APPROVE_BUTTON_ID_FMT, rowIndex))).click(); slightPause(); return new EditorPage(getDriver()); } public EditorPage saveAsFuzzyAtRow(int rowIndex) { log.info("Click Fuzzy on row {}", rowIndex); readyElement(By.id(String.format(FUZZY_BUTTON_ID_FMT, rowIndex))).click(); return new EditorPage(getDriver()); } public String getMessageTargetAtRowIndex(int rowIndex, Plurals plurals) { log.info("Query text flow target at {}", rowIndex); return getCodeMirrorContent(rowIndex, TARGET_ID_FMT, plurals); } public String getStatistics() { log.info("Query statistics"); return readyElement(By.id("gwt-debug-statistics-label")).getText(); } public List<String> getMessageSources() { log.info("Query list of text flow sources"); return WebElementUtil.elementsToText(readyElement(transUnitTable).findElements(By.className("gwt-HTML"))); } /** * Get the validation error messages for the currently translated row * * @return error string */ public String getValidationMessageCurrentTarget() { log.info("Query validation messages on current item"); waitForAMoment().withMessage("validation messages are not empty") .until(webDriver -> !getTargetValidationBox().getText().isEmpty()); return getTargetValidationBox().getText(); } /** * Query whether the first validation messages box is displayed * * @return is/not displayed */ public boolean isValidationMessageCurrentTargetVisible() { log.info("Query is validation message panel displayed"); return getTargetValidationBox().isDisplayed(); } /** * Wait for a delayed validation error panel to display */ public void expectValidationErrorsVisible() { log.info("Wait for validation message panel displayed"); waitForPageSilence(); assertThat(isValidationMessageCurrentTargetVisible()).as("validation message panel displayed").isTrue(); } /** * Click on the validation error box to view details * * @return new EditorPage */ public EditorPage openValidationBox() { log.info("Click to open Validation panel"); clickElement(existingElement(getTargetValidationBox(), By.tagName("a"))); waitForAMoment().withMessage("contains 'Unexpected' or 'Target'").until( webDriver -> StringUtils.containsAny(getValidationMessageCurrentTarget(), "Unexpected", "Target")); return new EditorPage(getDriver()); } /** * Opens the validation options sidebar * * @return new EditorPage */ public EditorPage openValidationOptions() { log.info("Click to open Validation options panel"); new Actions(getDriver()).click(readyElement(existingElement(By.id("container")), validationOptions)) .perform(); existingElement(validationOptionsView); return new EditorPage(getDriver()); } /** * Check if a validation option is available * * @param validation * the option to check * @return new EditorPage */ public boolean isValidationOptionAvailable(Validations validation) { log.info("Query is validation option {} available", validation); return readyElement(By.xpath("//*[@title=\'" + getValidationTitle(validation) + "\']")) .findElement(By.tagName("input")).isEnabled(); } /** * Check if a validation option is selected * * @param validation * the option to check * @return new EditorPage */ public boolean isValidationOptionSelected(Validations validation) { log.info("Query is validation option {} selected", validation); return existingElement(existingElement(By.xpath("//*[@title=\'" + getValidationTitle(validation) + "\']")), By.tagName("input")).isSelected(); } /** * Click a validation option * * @param validation * the option to click * @return new EditorPage */ public EditorPage clickValidationCheckbox(Validations validation) { log.info("Click validation checkbox {}", validation); clickElement( readyElement(existingElement(By.xpath("//*[@title=\'" + getValidationTitle(validation) + "\']")), By.tagName("input"))); return new EditorPage(getDriver()); } private String getValidationTitle(Validations validation) { switch (validation) { case HTML: return "Check that XML/HTML tags are consistent"; case JAVAVARIABLES: return "Check that java style ({x}) variables are consistent"; case NEWLINE: return "Check for consistent leading and trailing newline (\\n)"; case POSITIONAL: return "Check that positional printf style (%n$x) variables are consistent"; case PRINTF: return "Check that printf style (%x) variables are consistent"; case TABS: return "Check whether source and target have the same number of tabs."; case XML: return "Check that XML entity are complete"; default: throw new RuntimeException("Unknown validation!"); } } public EditorPage inputFilterQuery(String query) { log.info("Enter filter query {}", query); readyElement(editorFilterField).clear(); enterText(readyElement(editorFilterField), query + Keys.ENTER, true, false, false); return new EditorPage(getDriver()); } public String getFilterQuery() { log.info("Query filter text"); return readyElement(editorFilterField).getAttribute("value"); } // Find the right side column for the selected row private WebElement getTranslationTargetColumn() { return // Right column readyElement(By.className("selected")).findElements(By.className("transUnitCol")).get(1); } // Find the validation messages / errors box private WebElement getTargetValidationBox() { return existingElement(getTranslationTargetColumn(), validationBox); } // Click the History button for the selected row - row id must be known public EditorPage clickShowHistoryForRow(int row) { log.info("Click history button on row {}", row); readyElement(getTranslationTargetColumn(), By.id("gwt-debug-target-" + row + "-history")).click(); waitForAMoment().withMessage("translation history box is displayed") .until(it -> getTranslationHistoryBox().isDisplayed()); return new EditorPage(getDriver()); } public String getHistoryEntryAuthor(int entry) { log.info("Query author, action on history entry {}", entry); return getTranslationHistoryList().get(entry).findElements(By.className("gwt-InlineHTML")).get(0) .findElement(By.className("txt--meta")).findElement(By.tagName("a")).getText(); } public String getHistoryEntryContent(int entry) { log.info("Query content on history entry {}", entry); return getTranslationHistoryList().get(entry).findElements(By.className("gwt-InlineHTML")).get(1) .findElement(By.className("cm-s-default")).getText(); } public EditorPage clickCompareOn(final int entry) { log.info("Click Compare on history entry {}", entry); waitForAMoment().withMessage("compare button is displayed").until(webDriver -> { try { return getTranslationHistoryList().get(entry).findElement(By.linkText("Compare")).isDisplayed(); } catch (IndexOutOfBoundsException ioobe) { return false; } }); clickElement(getTranslationHistoryList().get(entry).findElement(By.linkText("Compare"))); slightPause(); return new EditorPage(getDriver()); } public String getTranslationHistoryCompareTabtext() { log.info("Query history tab text"); return getCompareTab().getText(); } public EditorPage clickCompareVersionsTab() { log.info("Click on Compare versions tab"); getCompareTab().click(); readyElement(getTranslationHistoryBox(), By.className("html-face")); return new EditorPage(getDriver()); } public String getComparisonTextInRow(int row) { log.info("Query comparison text in row {}", row); return getCompareTabEntries().get(row).findElement(By.tagName("pre")).getText(); } public List<String> getComparisonTextDiff() { log.info("Query diff from history compare"); List<String> diffs = new ArrayList<>(); for (WebElement element : getCompareTabEntries()) { for (WebElement diffElement : element.findElements(By.className("diff-insert"))) { diffs.add("++" + diffElement.getText()); } for (WebElement diffElement : element.findElements(By.className("diff-delete"))) { diffs.add("--" + diffElement.getText()); } } return diffs; } public VersionLanguagesPage clickVersionBreadcrumb(String versionName) { readyElement(By.linkText(versionName)).click(); slightPause(); return new VersionLanguagesPage(getDriver()); } private WebElement getTranslationHistoryBox() { return existingElement(existingElement(By.id("gwt-debug-transHistory")), By.id("gwt-debug-transHistoryTabPanel")); } private List<WebElement> getTranslationHistoryList() { return getTranslationHistoryBox().findElement(By.className("gwt-TabLayoutPanelContent")) .findElement(By.className("list--slat")).findElements(By.className("l--pad-v-1")); } private WebElement getCompareTab() { return getTranslationHistoryBox().findElement(By.className("gwt-TabLayoutPanelTabs")) .findElements(By.className("gwt-TabLayoutPanelTabInner")).get(1); } private List<WebElement> getCompareTabEntries() { return // Second tab getTranslationHistoryBox().findElements(By.className("gwt-TabLayoutPanelContent")).get(1) .findElements(By.className("textFlowEntry")); } public ReactEditorPage pressAlphaEditorButton() { log.info("Pressing Alpha Editor button"); clickElement(alphaEditorButton); return new ReactEditorPage(getDriver()); } }