All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pawl.jbehave.step.BrowserSteps Maven / Gradle / Ivy

/*
 * Copyright 2014 Geeoz Software
 *
 * 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.
 */

package pawl.jbehave.step;

import org.hamcrest.Matchers;
import org.jbehave.core.annotations.Alias;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.web.selenium.WebDriverPage;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.InvalidSelectorException;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import pawl.jbehave.Pages;
import pawl.util.Resources;

import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
 * BrowserSteps a simple POJO, which will contain the Java methods
 * that are mapped to the textual steps. The methods need to annotated with
 * one of the JBehave annotations and the annotated value should contain
 * a regex pattern that matches the textual step.
 *
 * @author Alex Voloshyn
 * @author Mike Dolinin
 * @author Serge Voloshyn
 * @version 1.14 2/27/14
 */
public final class BrowserSteps extends Matchers {
    /**
     * Default logger.
     */
    private static final Logger LOG =
            Logger.getLogger(BrowserSteps.class.getName());
    /**
     * Web pages collection factory.
     */
    private final transient Pages browser;
    /**
     * Specifies the URL of the page.
     */
    private transient String url;

    /**
     * Create steps object that contains pages collection.
     *
     * @param pages factory of the page handlers
     */
    public BrowserSteps(final Pages pages) {
        super();
        browser = pages;
    }

    /**
     * Setup URL for next user actions.
     *
     * @param key for resources to get corresponding value
     */
    @Given("an '$key' link")
    public void setupLink(final String key) {
        if (Resources.context().containsKey(key)) {
            url = Resources.context().get(key);
        } else {
            url = Resources.base().string(key);
        }
    }

    /**
     * Action for open web link.
     */
    @When("I open the link")
    public void openUrl() {
        browser.base().get(url);
    }

    /**
     * Action for refresh web page.
     */
    @When("I refresh the page")
    @Alias("refresh the page")
    public void refreshPage() {
        browser.base().navigate().refresh();
    }

    /**
     * Action for open new web link by adding to opened link context path.
     *
     * @param contextPath path for add
     */
    @When("open context path '$contextPath'")
    public void openContextPath(final String contextPath) {
        String currentUrl = browser.base().getCurrentUrl();
        browser.base().get(currentUrl + contextPath);
    }

    /**
     * Action to wait some time and then wait for all ajax responses.
     *
     * @param sec seconds to wait
     */
    @When("I wait '$sec' seconds")
    @Alias("wait '$sec' seconds")
    public void wait(final String sec) {
        final int seconds = (int) (Double.parseDouble(sec) * 1000);
        if (seconds > 0) {
            try {
                Thread.sleep(seconds);
            } catch (InterruptedException e) {
                LOG.log(Level.FINE, e.getMessage(), e.getCause());
            }
        }

        Object callResult = browser.base().executeScript(
                "return activeAjaxRequests();");
        final long sleepTime = 100L;
        while (callResult != null
                && Long.parseLong(String.valueOf(callResult)) > 0) {
            try {
                Thread.sleep(sleepTime);
            } catch (final InterruptedException e) {
                LOG.log(Level.FINE, e.getMessage(), e.getCause());
            }
            callResult = browser.base().executeScript(
                    "return activeAjaxRequests();");
        }
    }

    /**
     * Action for click web element.
     *
     * @param identity element identity for search
     */
    @When("I click '$identity'")
    @Alias("click '$identity'")
    public void click(final String identity) {
        getVisibleElement(identity).click();
    }

    /**
     * Action for click the web element with XY offsets from center of element.
     *
     * @param identity element identity for search
     * @param xOffset  X-offset for click
     * @param yOffset  Y-offset for click
     */
    @When("I click '$identity' with '$xOffset' and '$yOffset' offsets")
    @Alias("click '$identity' with '$xOffset' and '$yOffset' offsets")
    public void clickWithOffset(final String identity,
                                final String xOffset,
                                final String yOffset) {
        final WebElement webElement = getVisibleElement(identity);
        final Actions builder = new Actions(browser.base());
        builder.moveToElement(webElement).moveByOffset(
                Integer.parseInt(xOffset), Integer.parseInt(yOffset))
                // change after
                // https://code.google.com/p/selenium/issues/detail?id=6141
                // fixed
                .click().perform();
    }

    /**
     * Action for click web element.
     *
     * @param className        element className for search
     * @param value            element text
     * @param parentIdentifier parent element identifier
     */
    @When("I choose '$className' with '$value' in '$parentIdentifier'")
    @Alias("choose '$className' with '$value' in '$parentIdentifier'")
    public void choose(final String className, final String value,
                       final String parentIdentifier) {
        final WebElement parent = getVisibleElement(parentIdentifier);
        final List elements
                = parent.findElements(By.className(className));
        assertThat("Page elements should exists: class name '" + className
                + "'", elements.size(),
                is(not(equalTo(0))));

        for (final WebElement element : elements) {
            if (element.getText().equals(value)) {
                element.click();
                return;
            }
        }
        fail("Cannot find element with class name '" + className + "' and "
                + "value '" + value + "'.");
    }

    /**
     * Fill input with local file absolute path.
     *
     * @param identity         input element id
     * @param fileRelativePath relative file path
     */
    @When("I fill '$identity' with '$fileRelativePath' file")
    @Alias("fill '$identity' with '$fileRelativePath' file")
    public void fillFile(final String identity, final String fileRelativePath) {
        final URL resource = Thread.currentThread().getContextClassLoader()
                .getResource(fileRelativePath);
        assert resource != null;
        final WebElement element = browser.base().findElement(By.id(identity));

        if (element.isDisplayed()) {
            element.clear();
            element.sendKeys(resource.getFile());
            return;
        }

        final String putValue = MessageFormat.format(
                "document.getElementById(\"{0}\")"
                        + ".setAttribute(\"value\", \"{1}\");"
                        + "if (\"createEvent\" in document) '{'"
                        + "var evt = document.createEvent(\"HTMLEvents\");"
                        + "evt.initEvent(\"change\", false, true);"
                        + "document.getElementById(\"{0}\").dispatchEvent(evt);"
                        + "'}' else '{'document.getElementById(\"{0}\"')'"
                        + ".fireEvent(\"onchange\");'}'",
                identity, resource.getFile());
        System.err.println("Put new value script: " + putValue);
        browser.base().executeScript(putValue);
    }

    /**
     * Retrieve an web elements that should be visible on current page.
     *
     * @param identity an identity of the element
     * @return a web elements that was found
     */
    private List getVisibleElements(final String identity) {
        By[] selectors = {
                By.id(identity),
                By.xpath(Resources.base().identityXpath(identity)),
                By.name(identity),
                By.className(identity),
                By.cssSelector(identity),
                By.xpath(identity)};
        if (identity.matches("^[0-9]")) {
            selectors = new By[]{
                    By.id(identity),
                    By.name(identity)};
            LOG.fine("Identity starts with numbers it could be only "
                    + "id or name.");
        }
        for (By selector : selectors) {
            List elements = browser.base().findElements(selector);
            if (!elements.isEmpty()) {
                List visibleElements = new ArrayList<>(
                        elements.size());
                for (WebElement element : elements) {
                    if (element.isDisplayed()) {
                        visibleElements.add(element);
                    }
                }
                if (!visibleElements.isEmpty()) {
                    return visibleElements;
                }
            }
        }
        throw new NoSuchElementException(
                "Could not find any visible element with identity - "
                        + identity);
    }

    /**
     * Retrieve web element which may be visible on current page.
     *
     * @param identity an identity of the element
     * @return web element which were found
     */
    private WebElement getVisibleElement(final String identity) {
        return getVisibleElements(identity).get(0);
    }

    /**
     * Action for click on link.
     *
     * @param href attribute for search link on page
     */
    @When("I click on link '$href'")
    public void clickOnLinkWithAttribute(final String href) {
        browser.base().findElement(
                By.xpath(".//a[@href='" + href + "']")).click();
    }

    /**
     * Action for filling text to input element.
     *
     * @param identity element identity for search
     * @param text     text that should be filled by step
     */
    @When("I fill '$identity' with '$value'")
    @Alias("fill '$identity' with '$value'")
    public void fill(final String identity, final String text) {
        final WebElement element = getVisibleElement(identity);
        element.clear();
        element.sendKeys(getTextFromStorageIfExist(
                Resources.base().string(text, text)));
    }

    /**
     * Action for press ENTER key.
     *
     * @param identity element identity for search
     */
    @When("I press ENTER on '$identity'")
    @Alias("press ENTER on '$identity'")
    public void pressEnter(final String identity) {
        final WebElement element = getVisibleElement(identity);
        element.clear();
        element.sendKeys(Keys.ENTER);
    }

    /**
     * Action for selection option in the select element.
     *
     * @param identity element identity for search
     * @param value    value that should be selected
     */
    @When("I select '$identity' with '$value'")
    @Alias("select '$identity' with '$value'")
    public void select(final String identity, final String value) {
        final WebElement element = getVisibleElement(identity);
        final Select select = new Select(element);
        select.selectByVisibleText(Resources.base().string(value, value));
    }

    /**
     * Retrieve text from test session store.
     *
     * @param text text key that used for storing
     * @return a text from test session store
     */
    private String getTextFromStorageIfExist(final String text) {
        final String storedText = Resources.context().get(text);
        if (storedText != null) {
            return storedText;
        }
        return text;
    }

    /**
     * Verify the current page title.
     *
     * @param title for check
     */
    @Then("I get title '$title'")
    public void verifyTitle(final String title) {
        assertThat("The page title should be as follow.",
                browser.base().getTitle(),
                equalTo(Resources.base().string(title, title)));
    }

    /**
     * Search text on the current page.
     *
     * @param text for search
     */
    @Then("I get text '$text'")
    @Alias("text '$text'")
    public void verifySource(final String text) {
        assertTrue("Page source should contains the text.",
                browser.base().getPageSource().contains(
                        Resources.base().string(text, text)));
    }

    /**
     * Search HTML element on the current page with specified identity.
     *
     * @param identity element identity for search
     */
    @Then("I get '$identity' element")
    @Alias("'$elementId' element")
    public void verifyElement(final String identity) {
        assertThat("Page element should exists: '" + identity,
                getVisibleElement(identity),
                is(notNullValue()));
    }

    /**
     * Verify that HTML element with specified identity does not exist
     * on the current page .
     *
     * @param identity element identity for search
     */
    @Then("I get no '$identity' element")
    @Alias("no '$elementId' element")
    public void verifyElementIsNotPresent(final String identity) {
        try {
            assertThat("Page element should not exists: '" + identity,
                    getVisibleElements(identity).size(), is(equalTo(0)));
        } catch (NoSuchElementException | InvalidSelectorException e) {
            LOG.fine(e.getMessage());
        }
    }

    /**
     * Search HTML element on the current page with specified identity and tag
     * name.
     *
     * @param identity element identity for search
     */
    @Then("I get '$identity' link")
    @Alias("'$identity' link")
    public void verifyLink(final String identity) {
        assertThat("Page element should exists: '" + identity,
                getVisibleElement(identity).getTagName(),
                is(equalTo("a")));
    }

    /**
     * Search HTML element on the current page with specified identity.
     *
     * @param identity element identity for search
     * @param text     the text for verification
     */
    @Then("I get '$identity' with '$text'")
    @Alias("'$identity' with '$text'")
    public void verifyElementText(final String identity, final String text) {
        final WebElement element = getVisibleElement(identity);
        assertThat("Page element should exists: '" + identity + "'",
                element, is(notNullValue()));
        final String value = Resources.base().string(text, text);
        assertThat(String.format(
                "Page element '%s' should have text: '%s'", identity, value),
                getTextFrom(element),
                is(equalTo(getTextFromStorageIfExist(value))));
    }

    /**
     * Retrieve a text from input/textarea/element.
     *
     * @param element an element for process
     * @return text value from input element
     */
    private String getTextFrom(final WebElement element) {
        final String elementTag = element.getTagName();
        final String elementText;
        switch (elementTag) {
            case "input":
            case "textarea":
                elementText = element.getAttribute("value");
                break;
            default:
                elementText = element.getText();
                break;
        }
        return elementText;
    }

    /**
     * Store text from element on the current page with specified identity,
     * to test session map.
     *
     * @param identity element identity for search and get text
     * @param key      for store and get text from session map
     */
    @When("I remember text from '$identity' to '$key' variable")
    public void storeTextFromElement(final String identity, final String key) {
        Resources.context().put(key, getTextFrom(getVisibleElement(identity)));
    }

    /**
     * Reopen browser with defined cookies from current session.
     *
     * @param keys of cookies to save and put in new session.
     */
    @When("reopen browser with $keys cookies from current session")
    public void reopenBrowserWithCookies(final List keys) {
        List cookies = new ArrayList<>(keys.size());
        WebDriverPage base = browser.base();
        for (String key : keys) {
            cookies.add(base.manage().getCookieNamed(key));
        }
        base = browser.renewBase();
        base.get("");
        for (Cookie cookie : cookies) {
            base.manage().addCookie(cookie);
        }
    }

    /**
     * Switch to a new window.
     */
    @When("I switch to new window")
    public void switchToNewWindow() {
        final WebDriver driver = browser.base();
        final String baseWindowHandle = driver.getWindowHandle();
        final Set opened = driver.getWindowHandles();
        String newWindow;
        if (opened.size() > 1 && opened.remove(baseWindowHandle)) {
            final Iterator iterator = opened.iterator();
            newWindow = iterator.next();
        } else {
            final WebDriverWait wait =
                    new WebDriverWait(driver, Resources.base().explicitWait());
            newWindow = wait.until(anyWindowOtherThan(opened));
        }
        driver.switchTo().window(newWindow);
    }

    /**
     * Retrieve an any other window than input.
     *
     * @param windows windows that should be excluded
     * @return expected condition result
     */
    public static ExpectedCondition anyWindowOtherThan(
            final Set windows) {
        return new ExpectedCondition() {
            public String apply(final WebDriver driver) {
                if (driver == null) {
                    throw new WebDriverException();
                }
                final Set all = driver.getWindowHandles();
                all.removeAll(windows);
                if (all.size() > 0) {
                    return all.iterator().next();
                }
                return null;
            }
        };
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy