com.cognifide.qa.bb.aem.ui.AemDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.cognifide.qa.bb.aem.ui.AemDialog.java

Source

/*-
 * #%L
 * Bobcat
 * %%
 * Copyright (C) 2016 Cognifide Ltd.
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package com.cognifide.qa.bb.aem.ui;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.ElementNotVisibleException;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cognifide.qa.bb.aem.ui.menu.AemContextMenu;
import com.cognifide.qa.bb.aem.ui.menu.MenuOption;
import com.cognifide.qa.bb.aem.ui.window.ValidationWindow;
import com.cognifide.qa.bb.aem.utils.FieldValuesSetter;
import com.cognifide.qa.bb.constants.ConfigKeys;
import com.cognifide.qa.bb.constants.HtmlTags;
import com.cognifide.qa.bb.constants.Timeouts;
import com.cognifide.qa.bb.expectedconditions.CommonExpectedConditions;
import com.cognifide.qa.bb.provider.selenium.BobcatWait;
import com.cognifide.qa.bb.qualifier.CurrentScope;
import com.cognifide.qa.bb.qualifier.FindPageObject;
import com.cognifide.qa.bb.qualifier.Frame;
import com.cognifide.qa.bb.qualifier.Global;
import com.cognifide.qa.bb.qualifier.PageObject;
import com.cognifide.qa.bb.scope.CurrentScopeHelper;
import com.google.inject.Inject;
import com.google.inject.name.Named;

/**
 * This class represents dialog window. It has methods for opening and closing the dialog and access
 * its fields.
 */
@PageObject
@Frame("$cq")
public class AemDialog {

    /**
     * Xpath expression for selecting the dialog element from the page.
     */
    public static final String DIALOG_XPATH = "//div[contains(@id, 'ext-comp-')"
            + " and contains(@style, 'display: block') and contains(@style, 'visibility: visible')]";

    public static final String ID_CQ_GEN = "[id*=cq-gen]";

    private static final Logger LOG = LoggerFactory.getLogger(AemDialog.class);

    private static final String FOOTER_BUTTON_XPATH_FORMATTED = "//div[contains(@class, 'x-window-footer')]//button[text()='%s']";

    private static final String CONFIG_WINDOW_TITLE_XPATH = "//span[contains(@class, 'x-window-header-text')]";

    private static final String CANCEL_BUTTON_TEXT = "Cancel";

    private static final String OK_BUTTON_TEXT = "OK";

    private static final By DIALOG_XPATH_COMPILED = By.xpath(DIALOG_XPATH);

    @Inject
    @CurrentScope
    private WebElement currentScope;

    @Inject
    private WebDriver webDriver;

    @Inject
    private AemDialogFieldResolver fieldResolver;

    @Inject
    private BobcatWait bobcatWait;

    @Inject
    private Actions actions;

    @Global
    @FindPageObject
    private ValidationWindow validationWindow;

    @Inject
    @Named(ConfigKeys.WEBDRIVER_DEFAULT_TIMEOUT)
    private int defaultTimeout;

    @Inject
    private AemContextMenu aemContextMenu;

    @Inject
    private CurrentScopeHelper webElementHelper;

    /**
     * Opens the dialog by double-clicking the component.
     *
     * @return Returns this dialog instance.
     */
    public AemDialog open() {
        open(currentScope);
        return this;
    }

    /**
     * Opens the dialog by double-clicking the component using clickable area.
     *
     * @param clickableArea area to click
     * @return Returns this dialog instance.
     */
    public AemDialog open(final By clickableArea) {
        open(currentScope.findElement(clickableArea));
        return this;
    }

    /**
     * Opens the dialog by edit option in context menu.
     *
     * @return Returns this dialog instance.
     */
    public AemDialog openByContextMenu() {
        openByContextMenu(currentScope);
        return this;
    }

    /**
     * Opens the dialog by edit option in context menu using clickable area.
     *
     * @param clickableArea area to click
     * @return Returns this dialog instance.
     */
    public AemDialog openByContextMenu(final By clickableArea) {
        openByContextMenu(currentScope.findElement(clickableArea));
        return this;
    }

    /**
     * Clicks OK button at the bottom of edit Window. Saves changes and closes the window (waits for
     * the dialog to disappear).
     *
     * @return Returns this dialog instance.
     */
    public AemDialog ok() {
        return clickDialogFooterButton(OK_BUTTON_TEXT);
    }

    /**
     * Finds the cancel button in the dialog and clicks it. All changes introduced in dialog's fields
     * are ignored.
     *
     * @return This dialog instance.
     */
    public AemDialog cancel() {
        return clickDialogFooterButton(CANCEL_BUTTON_TEXT);
    }

    /**
     * Clicks the specified tab in component's Edit Window.
     *
     * @param label Tab's label as seen by the user.
     * @return Returns this dialog instance.
     */
    public AemDialog clickTab(final String label) {
        bobcatWait.withTimeout(Timeouts.BIG).until((ExpectedCondition<Object>) input -> {
            getTabMap().get(label).click();
            return label.equals(getActiveTabName());
        }, 5);
        return this;
    }

    /**
     * @return Edit Window's title.
     */
    public String getTitle() {
        WebElement windowTitle = webDriver.findElement(By.xpath(DIALOG_XPATH + CONFIG_WINDOW_TITLE_XPATH));
        return windowTitle.getText();
    }

    /**
     * Clicks OK button at the bottom of edit Window. Assumes invalid data was entered and waits for
     * the validation message to appear.
     *
     * @return Returns instance of ValidationWindow.
     */
    public ValidationWindow okExpectingValidation() {
        final WebElement okButton = getFooterButtonWebElement(OK_BUTTON_TEXT);

        bobcatWait.withTimeout(Timeouts.BIG).until((ExpectedCondition<Object>) input -> {
            try {
                okButton.click();
                return webElementHelper.isCurrentScopeVisible(validationWindow);
            } catch (NoSuchElementException | StaleElementReferenceException e) {
                LOG.debug("Dialog footer button is not available: {}", e);
                return Boolean.FALSE;
            }
        }, 2);
        return validationWindow;
    }

    /**
     * @return Label of the active tab.
     */
    public String getActiveTabName() {
        return getActiveTab().getText();
    }

    /**
     * @return true if Dialog is visible, false otherwise.
     */
    public boolean isVisible() {
        webDriver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
        boolean isDialogVisible = !webDriver.findElements(DIALOG_XPATH_COMPILED).isEmpty();
        webDriver.manage().timeouts().implicitlyWait(defaultTimeout, TimeUnit.SECONDS);
        return isDialogVisible;
    }

    /**
     * Checks if the dialog is visible. If yes, closes it by clicking "cancel" button.
     *
     * @return This AemDialog instance.
     */
    public AemDialog closeIfVisible() {
        if (isVisible()) {
            cancel();
        }
        return this;
    }

    /**
     * Searches for the field of the given type, located on the given tab, identified by its index.
     *
     * @param tabName         Label of the tab where the field is located.
     * @param widgetIndex     Index of the widget within the given tab.
     * @param dialogFieldType Class that represents dialog's field.
     * @param <T>             dialog field class
     * @return A dialogFieldType's instance that represents dialog field.
     */
    public <T> T getField(String tabName, int widgetIndex, Class<T> dialogFieldType) {
        String widgetLabel = getWidgetLabelFromTab(tabName, widgetIndex);
        return fieldResolver.getField(widgetLabel, dialogFieldType);
    }

    /**
     * Searches for the field of the given type by its label, on the current tab.
     *
     * @param label           field label
     * @param dialogFieldType type of dialog field
     * @param <T>             dialog field class
     * @return A dialogFieldType's instance that represents dialog field.
     */
    public <T> T getFieldFromCurrentTab(String label, Class<T> dialogFieldType) {
        return fieldResolver.getFieldInActiveTab(label, dialogFieldType);
    }

    /**
     * Finds the dialog tab by its index and returns its name (label).
     *
     * @param index of the tab (starts from 0)
     * @return Tab's label.
     */
    public String getLabelFromTab(int index) {
        WebElement dialog = webDriver.findElement(DIALOG_XPATH_COMPILED);
        List<WebElement> items = dialog.findElements(By.className("x-tab-item"));
        WebElement widget = items.get(index);
        WebElement label = widget.findElement(By.xpath(".//label"));
        return label.getText();
    }

    /**
     * Clicks button at the bottom of edit Window and expect for dialog to disappear.
     *
     * @param buttonText button label
     * @return Returns this dialog instance.
     */
    public AemDialog clickDialogFooterButton(final String buttonText) {
        final WebElement footerButton = getFooterButtonWebElement(buttonText);

        bobcatWait.withTimeout(Timeouts.BIG).until((ExpectedCondition<Object>) input -> {
            try {
                footerButton.click();
                footerButton.isDisplayed();
                return Boolean.FALSE;
            } catch (NoSuchElementException | StaleElementReferenceException | ElementNotVisibleException e) {
                LOG.debug("Dialog footer button is not available: {}", e);
                return Boolean.TRUE;
            }
        }, 2);
        bobcatWait.withTimeout(Timeouts.MEDIUM).until(CommonExpectedConditions.noAemAjax());
        return this;
    }

    /**
     * Finds a field by its label on the current tab and sets the value.
     *
     * @param value           field value
     * @param label           field label
     * @param dialogFieldType type of dialog field
     * @return This AemDialog instance.
     */
    public AemDialog setValueOfItemInCurrentTab(String value, String label, Class<?> dialogFieldType) {
        FieldValuesSetter.setFieldValue(getFieldFromCurrentTab(label, dialogFieldType), value);
        return this;

    }

    /**
     * Gets value from field in current tab with provided label and field type.
     *
     * @param label           field label
     * @param dialogFieldType type of dialog field
     * @return field value as String
     */
    public String getValueOfItemInCurrentTab(String label, Class<?> dialogFieldType) {
        return FieldValuesSetter.getFieldValue(getFieldFromCurrentTab(label, dialogFieldType));
    }

    /**
     * Fills field with data if (and only if) the field's initial value is null. In other words if the
     * field has already been initialized then it will not be altered.
     *
     * @return Returns a hashmap that maps labels to webelements representing tabs.
     */
    private Map<String, WebElement> getTabMap() {
        List<WebElement> tabs = getAllTabs();
        Map<String, WebElement> windowTabs = new HashMap<>(tabs.size());
        for (WebElement tab : tabs) {
            String tabLabel = tab.getText();
            windowTabs.put(tabLabel, tab);
        }
        return windowTabs;
    }

    private List<WebElement> getAllTabs() {
        bobcatWait.withTimeout(Timeouts.BIG)
                .until(ExpectedConditions.visibilityOfElementLocated(DIALOG_XPATH_COMPILED));
        WebElement dialog = webDriver.findElement(DIALOG_XPATH_COMPILED);
        return dialog.findElements(
                By.cssSelector("div.x-tab-panel-header span.x-tab-strip-inner span.x-tab-strip-text"));
    }

    private WebElement getActiveTab() {
        List<WebElement> tabs = getAllTabs();

        for (WebElement tab : tabs) {
            WebElement tabState = tab.findElement(By.xpath(".//../../../.."));
            if (tabState.getAttribute(HtmlTags.Attributes.CLASS).contains("x-tab-strip-active")) {
                return tab;
            }
        }
        throw new IllegalStateException("There are no active tabs in the current dialog");
    }

    private void open(final WebElement element) {
        waitForComponentOnParsys();
        bobcatWait.withTimeout(Timeouts.BIG).until((ExpectedCondition<Object>) input -> {
            try {
                actions.doubleClick(element).perform();
            } catch (NoSuchElementException | StaleElementReferenceException e) {
                LOG.debug("Dialog open element is not available", e);
            }
            return isVisible();
        }, 5);
    }

    private void openByContextMenu(final WebElement element) {
        waitForComponentOnParsys();
        bobcatWait.withTimeout(Timeouts.BIG).until((ExpectedCondition<Object>) input -> {
            try {
                aemContextMenu.open(element);
                aemContextMenu.clickOption(MenuOption.EDIT);
            } catch (NoSuchElementException | StaleElementReferenceException | ElementNotVisibleException e) {
                LOG.debug("Dialog open element is not available: {}", e);
            }
            return isVisible();
        }, 5);
    }

    private void waitForComponentOnParsys() {
        bobcatWait.withTimeout(Timeouts.BIG)
                .until(CommonExpectedConditions.listSizeIsConstant(currentScope, By.cssSelector(ID_CQ_GEN)));
    }

    private String getWidgetLabelFromTab(String tabName, int widgetIndex) {
        clickTab(tabName);
        return getLabelFromTab(widgetIndex);
    }

    private WebElement getFooterButtonWebElement(String buttonText) {
        return webDriver
                .findElement(By.xpath(DIALOG_XPATH + String.format(FOOTER_BUTTON_XPATH_FORMATTED, buttonText)));
    }
}