/******************************************************************************* * Copyright 2014 JHC Systems Limited * * 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. *******************************************************************************/ /******************************************************************************* * Concept of Finders and inspiration for whole library from the "Selenium LiFT-style API" * https://code.google.com/p/selenium/wiki/LiftStyleApi * created by Robert Chatley (@rchatley) and included in Selenium WebDriver java distributions * in org.openqa.selenium.lift package * * This class has similar methods and role to org.openqa.selenium.lift.WebDriverTestContext * */ package jhc.redsniff.webdriver; import static jhc.redsniff.core.Describer.asString; import static jhc.redsniff.core.Describer.newDescription; import static jhc.redsniff.core.Describer.registerDescribaliser; import static jhc.redsniff.core.FindingExpectations.presenceOf; import static jhc.redsniff.pageError.NoPageErrorExpectation.noPageErrorExpected; import static jhc.redsniff.webdriver.Finders.body; import static jhc.redsniff.webdriver.Finders.only; import static jhc.redsniff.webdriver.WebDriverMatchers.hasText; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import java.io.File; import java.util.Collections; import java.util.List; import jhc.redsniff.action.ActionDriver; import jhc.redsniff.action.Controller; import jhc.redsniff.core.Finder; import jhc.redsniff.internal.core.CollectionOf; import jhc.redsniff.internal.core.Quantity; import jhc.redsniff.internal.core.RedsniffQuantityFindingTester; import jhc.redsniff.internal.core.TypedQuantity; import jhc.redsniff.internal.expectations.DefaultExpectationChecker; import jhc.redsniff.internal.expectations.ExpectationCheckResult; import jhc.redsniff.internal.expectations.ExpectationChecker; import jhc.redsniff.internal.expectations.FindingExpectation; import jhc.redsniff.pageError.PageErrorChecker; import jhc.redsniff.webdriver.describe.WebDriverDescribaliser; import jhc.redsniff.webdriver.describe.WebElementDescribaliser; import jhc.redsniff.webdriver.download.DownloadFactory; import jhc.redsniff.webdriver.download.FileDownloader; import org.hamcrest.Matcher; import org.openqa.selenium.SearchContext; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.FluentWait; import org.openqa.selenium.support.ui.WebDriverWait; public class RedsniffWebDriverTester extends RedsniffQuantityFindingTester<WebElement, SearchContext> { private static final long DEFAULT_TIMEOUT_SECS = Long.valueOf(System.getProperty("selenium.defaultTimeout","10")); private WebDriver driver; private FileDownloader fileDownloader; @SuppressWarnings({ "unchecked" }) protected DefaultExpectationChecker<WebElement, CollectionOf<WebElement>, SearchContext> defaultingChecker(){ return (DefaultExpectationChecker<WebElement, CollectionOf<WebElement>, SearchContext>)this.checker; } public RedsniffWebDriverTester(WebDriver driver) { this(driver,driver); } public RedsniffWebDriverTester(WebDriver driver, SearchContext context){ this(new SeleniumController(driver), defaultExpectationChecker(context)); this.driver=driver; this.fileDownloader=new FileDownloader(driver); } protected RedsniffWebDriverTester(Controller<WebElement> controller, DefaultExpectationChecker<WebElement, CollectionOf<WebElement>, SearchContext> checker) { super(controller, checker); addPreChecks(); addUltimateCauseChecks(); } /** * The wrapped {@link WebDriver} , for use if need to switch to WebDriver API * @return */ public WebDriver getDriver(){ return driver; } /** * Create a new tester rooted on a particular {@link WebElement} (that isn't expected to go stale) * @param context * @return */ public RedsniffWebDriverTester newTesterFrom(SearchContext context){ return new RedsniffWebDriverTester(driver, context); } /** * Wait until the supplied {@link FindingExpectation} becomes satisfied, or throw a {@link TimeoutException} * , using the supplied {@link FluentWait} * This should only be done using a wait obtained by calling {@link #waiting()} to ensure the correct arguments are passed. * * @param expectation * @param wait * @return */ public <T, E, Q extends TypedQuantity<E, T>> T waitFor( final FindingExpectation<E, Q, SearchContext> expectation, FluentWait<WebDriver> wait) { try { return new Waiter(wait).waitFor(expectation); } catch(FindingExpectationTimeoutException e){ //do a final check to get the message unoptimized ExpectationCheckResult<E, Q> resultOfChecking = checker.resultOfChecking(expectation); String reason; if(resultOfChecking.meetsExpectation()) reason = "Expectation met only just after timeout. At timeout was:\n" + e.getReason(); else { reason = resultOfChecking.toString(); //if still not found first check there isn't a page error or something defaultingChecker().assertNoUltimateCauseWhile(newDescription().appendText("expecting ").appendDescriptionOf(expectation)); } throw new FindingExpectationTimeoutException(e.getOriginalMessage(), reason, e); } } public <T, E, Q extends TypedQuantity<E, T>> T waitFor( final Finder<E, Q, SearchContext> finder) { return waitFor(presenceOf(finder)); } public <T, E, Q extends TypedQuantity<E, T>> T waitFor( final FindingExpectation<E, Q, SearchContext> expectation) { return waitFor(expectation, defaultWait()); } public <V> V waitFor(final ExpectedCondition<V> con, FluentWait<WebDriver> wait) { try { return new Waiter(wait).waitFor(con); } catch(TimeoutException e){ //first check there wasn't a page error or something defaultingChecker().assertNoUltimateCauseWhile(newDescription().appendText("expecting ").appendText(con.toString())); throw e; } } public <V> V waitFor(final ExpectedCondition<V> con) { return waitFor(con, defaultWait()); } public <V> V waitForOrContinueAnyway(ExpectedCondition<V> con, FluentWait<WebDriver> wait) { return new Waiter(wait).waitForOrContinueAnyway(con); } public <V> V waitForOrContinueAnyway(ExpectedCondition<V> con) { return waitForOrContinueAnyway(con, defaultWait()); } public <T, E, Q extends TypedQuantity<E, T>> T waitForOrContinueAnyway(FindingExpectation<E, Q, SearchContext> expectation, FluentWait<WebDriver> wait) { return new Waiter(wait).waitForOrContinueAnyway(expectation); } public <T, E, Q extends TypedQuantity<E, T>> T waitForOrContinueAnyway(FindingExpectation<E, Q, SearchContext> expectation) { return waitForOrContinueAnyway(expectation, defaultWait()); } public final FluentWait<WebDriver> waiting(){ return defaultWait(); } /** * Override to add {@link PageErrorChecker}s that will be run before every assertion/interaction made * @return */ protected List<PageErrorChecker<WebElement, CollectionOf<WebElement>, SearchContext>> preChecks() { return Collections.emptyList(); } /** * Override to add {@link PageErrorChecker}s that will be run after a failed assertion, to check if some other cause * could be responsible * <p/> * For example, searching for a textbox, and not found, not because the box is missing but * because there is a stack trace error on the screen * @return */ protected List<PageErrorChecker<WebElement, CollectionOf<WebElement>, SearchContext>> ultimateCauseChecks() { return Collections.emptyList(); } @Override protected void registerDescribalisers() { registerDescribaliser(WebElement.class, new WebElementDescribaliser()); registerDescribaliser(WebDriver.class, new WebDriverDescribaliser()); } private static DefaultExpectationChecker<WebElement, CollectionOf<WebElement>, SearchContext> defaultExpectationChecker(SearchContext context) { return DefaultExpectationChecker.defaultExpectationCheckingChecker(context); } protected RedsniffWebDriverTester(ExpectationChecker<SearchContext> checker, Controller<WebElement> controller, ActionDriver<WebElement, SearchContext> actionDriver) { super(checker, controller, actionDriver); } protected FluentWait<WebDriver> defaultWait() { return new WebDriverWait(driver, DEFAULT_TIMEOUT_SECS); } public <F, Q extends Quantity<String>> Download<F> getDownload( Finder<String, Q, SearchContext> linkFinder, DownloadFactory<F> downloadFactory) { try { String defaultExtension = downloadFactory.getDefaultFileExtension(); String downloadUrl = find(only(linkFinder)); File file = fileDownloader.downloadFrom(downloadUrl, defaultExtension); return downloadFactory.createDownloadFrom(file); } catch (Exception e) { throw new AssertionError(e); } } public <F, Q extends Quantity<String>> void assertDownload( Finder<String, Q, SearchContext> linkFinder, DownloadFactory<F> downloadFactory, Matcher<F> matcher) { Download<F> download = null; try { download = getDownload(linkFinder, downloadFactory); String downloadDescription = asString( newDescription() .appendText("download of ") .appendDescriptionOf(downloadFactory) .appendText(" (downloaded from ") .appendDescriptionOf(linkFinder).appendText(")")); assertThat(downloadDescription, download.getObject(), matcher); } catch (Exception e) { throw new AssertionError(e); } finally { if (download != null) download.doneWith(); } } public void assertTextPresent(String text) { assertThatThe(body(), hasText(containsString(text))); } public void assertTextAbsent(String text) { assertThatThe(body(), not(hasText(containsString(text)))); } public String getPageSource() { return getDriver().getPageSource(); } public void clearSession() { getDriver().manage().deleteAllCookies(); } /** * Called internally to remove preChecks */ protected void removePreChecks() { defaultingChecker().removePreChecks(); } /** * Called internally to add preChecks */ protected void addPreChecks() { for(PageErrorChecker<WebElement, CollectionOf<WebElement>, SearchContext> preCheck: preChecks()) defaultingChecker().addPreCheck(noPageErrorExpected(preCheck)); } private void addUltimateCauseChecks() { for(PageErrorChecker<WebElement, CollectionOf<WebElement>, SearchContext> ultimateCauseCheck: ultimateCauseChecks()) defaultingChecker().addUltimateCauseExpectationCheck(noPageErrorExpected(ultimateCauseCheck)); } }