001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 */
019package org.nuxeo.runtime.test.runner.web;
020
021import java.net.MalformedURLException;
022import java.net.URL;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import javax.inject.Inject;
028
029import org.nuxeo.runtime.test.runner.FeaturesRunner;
030import org.openqa.selenium.By;
031import org.openqa.selenium.NotFoundException;
032import org.openqa.selenium.WebDriver;
033import org.openqa.selenium.WebDriverException;
034import org.openqa.selenium.WebElement;
035import org.openqa.selenium.support.PageFactory;
036import org.openqa.selenium.support.ui.ExpectedCondition;
037import org.openqa.selenium.support.ui.WebDriverWait;
038
039/**
040 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
041 */
042public abstract class WebPage {
043
044    /**
045     * Can be used by tests as default timeouts. This way you can change these values to change all the timeout in the
046     * tests. DEFAULT_TIMEOUT is used for regular timeouts (loading an ajax page an ajax dialog etc.) while BIG_TIMEOUT
047     * should be used for pages that are slower (like the home of a GWT application)
048     */
049    public static int DEFAULT_TIMEOUT = 5;
050
051    public static int BIG_TIMEOUT = 15;
052
053    private static final Map<Class<?>, WebPage> pages = new HashMap<Class<?>, WebPage>();
054
055    @Inject
056    protected Configuration config;
057
058    @Inject
059    protected WebDriver driver;
060
061    @Inject
062    protected FeaturesRunner runner;
063
064    /**
065     * Should be overridden by dynamic page (using ajax) to wait until the page is completely loaded By default nothing
066     * is done (page is assumed to be loaded)
067     *
068     * @return the page itself
069     */
070    public WebPage ensureLoaded() {
071        return this; // do nothing by default
072    }
073
074    public Configuration getConfiguration() {
075        return config;
076    }
077
078    public WebDriver getDriver() {
079        return driver;
080    }
081
082    public FeaturesRunner getRunner() {
083        return runner;
084    }
085
086    public void home() {
087        driver.get(config.home);
088    }
089
090    public void to(String path) {
091        if (path.contains("://")) {
092            driver.get(path);
093        } else {
094            try {
095                URL url = new URL(new URL(config.home), path);
096                driver.navigate().to(url);
097            } catch (MalformedURLException e) {
098                throw new WebDriverException(e);
099            }
100        }
101    }
102
103    public boolean hasElement(final By by) {
104        try {
105            driver.findElement(by);
106            return true;
107        } catch (WebDriverException e) {
108            return false;
109        }
110    }
111
112    public boolean hasElement(final By by, int timeoutInSeconds) {
113        try {
114            findElement(by, timeoutInSeconds);
115            return true;
116        } catch (WebDriverException e) {
117            return false;
118        }
119    }
120
121    public WebElement findElement(final By by) {
122        return driver.findElement(by);
123    }
124
125    public List<WebElement> findElements(final By by) {
126        return driver.findElements(by);
127    }
128
129    public WebElement findElement(final By by, int timeOutInSeconds) {
130        return waitUntilElementFound(by, timeOutInSeconds);
131    }
132
133    public WebElement waitUntilElementFound(final By by, int timeOutInSeconds) {
134        try {
135            return findElement(by); // try once first
136        } catch (NotFoundException e) {
137            return new WebDriverWait(driver, timeOutInSeconds).until(new ExpectedCondition<WebElement>() {
138                @Override
139                public WebElement apply(WebDriver arg0) {
140                    return driver.findElement(by);
141                }
142            });
143        }
144    }
145
146    public void waitUntilElementNotFound(final By by, int timeOutInSeconds) {
147        try {
148            findElement(by); // try once first
149            new WebDriverWait(driver, timeOutInSeconds).until(new ExpectedCondition<Boolean>() {
150                @Override
151                public Boolean apply(WebDriver arg0) {
152                    try {
153                        driver.findElement(by);
154                        return Boolean.FALSE;
155                    } catch (NotFoundException e) {
156                        return Boolean.TRUE;
157                    }
158                }
159            });
160        } catch (NotFoundException e) {
161        }
162    }
163
164    @SuppressWarnings("unchecked")
165    public <T extends WebPage> T getPage(Class<T> type) {
166        T page = (T) pages.get(type);
167        if (page == null) {
168            synchronized (pages) {
169                page = (T) pages.get(type);
170                if (page != null) {
171                    return page;
172                }
173                page = PageFactory.initElements(driver, type);
174                runner.getInjector().injectMembers(page);
175                pages.put(type, page);
176            }
177        }
178        return (T) page.ensureLoaded(); // this will block until page is loaded
179        // (if implementation needs this)
180    }
181
182    public static void flushPageCache() {
183        synchronized (pages) {
184            pages.clear();
185        }
186    }
187
188}