/* Copyright 2014 Red Hat, Inc. and/or its affiliates. This file is part of darcy-webdriver. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.redhat.darcy.webdriver.internal; import com.redhat.darcy.ui.By; import com.redhat.darcy.ui.api.ParentContext; import com.redhat.darcy.ui.api.View; import com.redhat.darcy.ui.internal.FindsById; import com.redhat.darcy.util.Caching; import com.redhat.darcy.web.api.Browser; import org.hamcrest.Matcher; import org.openqa.selenium.NoSuchWindowException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver.TargetLocator; import org.openqa.selenium.WebElement; import java.util.Objects; /** * Factories for the various possible {@link WebDriverTarget}s. */ public abstract class WebDriverTargets { public static WebDriverTarget window(String nameOrHandle) { return new WindowWebDriverTarget(nameOrHandle); } public static WebDriverTarget frame(WebDriverTarget parent, int index) { return new FrameByIndexWebDriverTarget(parent, index); } public static WebDriverTarget frame(WebDriverTarget parent, String nameOrId) { return new FrameByNameOrIdWebDriverTarget(parent, nameOrId); } public static WebDriverTarget frame(WebDriverTarget parent, WebElement frameElement) { return new FrameByElementWebDriverTarget(parent, frameElement); } public static WebDriverTarget defaultContent() { return new DefaultContextWebDriverTarget(); } public static WebDriverTarget withViewLoaded(View view, ParentContext parentContext) { return new ViewWebDriverTarget(view, parentContext); } public static WebDriverTarget windowByTitle(String title) { return new WindowTitleWebDriverTarget(title); } public static WebDriverTarget windowByUrl(Matcher<? super String> urlMatcher) { return new WindowUrlWebDriverTarget(urlMatcher); } /** * Determines the parent target of the specified * {@link com.redhat.darcy.webdriver.internal.WebDriverTarget}. If the target has no parent * (that is, it is not a target to a frame), then this returns the same target that it was * passed. This matches the behavior of * {@link org.openqa.selenium.WebDriver.TargetLocator#parentFrame()}. */ public static WebDriverTarget parentOf(WebDriverTarget target) { if (target instanceof FrameTarget) { return ((FrameTarget) target).getParent(); } return target; } public static class WindowWebDriverTarget implements WebDriverTarget { private final String nameOrHandle; WindowWebDriverTarget(String nameOrHandle) { this.nameOrHandle = Objects.requireNonNull(nameOrHandle, "nameOrHandle"); } @Override public WebDriver switchTo(TargetLocator targetLocator) { return targetLocator.window(nameOrHandle); } @Override public int hashCode() { return Objects.hash(nameOrHandle); } @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof WindowWebDriverTarget)) { return false; } WindowWebDriverTarget other = (WindowWebDriverTarget) object; return this.nameOrHandle.equals(other.nameOrHandle); } @Override public String toString() { return "WindowWebDriverTarget: {nameOrHandle: " + nameOrHandle + "}"; } } public static class FrameByIndexWebDriverTarget implements FrameTarget { private final WebDriverTarget parent; private final int index; FrameByIndexWebDriverTarget(WebDriverTarget parent, int index) { this.parent = Objects.requireNonNull(parent, "parent"); this.index = index; } @Override public WebDriverTarget getParent() { return parent; } @Override public WebDriver switchTo(TargetLocator targetLocator) { // This is ugly :( if (targetLocator instanceof CachingTargetLocator) { return ((CachingTargetLocator) targetLocator).frame(parent, index); } parent.switchTo(targetLocator); return targetLocator.frame(index); } @Override public int hashCode() { return Objects.hash(parent, index); } @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof FrameByIndexWebDriverTarget)) { return false; } FrameByIndexWebDriverTarget other = (FrameByIndexWebDriverTarget) object; return this.index == other.index && this.parent.equals(other.parent); } } public static class FrameByNameOrIdWebDriverTarget implements FrameTarget { private final WebDriverTarget parent; private final String nameOrId; FrameByNameOrIdWebDriverTarget(WebDriverTarget parent, String nameOrId) { this.parent = Objects.requireNonNull(parent, "parent"); this.nameOrId = nameOrId; } @Override public WebDriverTarget getParent() { return parent; } @Override public WebDriver switchTo(TargetLocator targetLocator) { // This is ugly :( if (targetLocator instanceof CachingTargetLocator) { return ((CachingTargetLocator) targetLocator).frame(parent, nameOrId); } parent.switchTo(targetLocator); return targetLocator.frame(nameOrId); } @Override public int hashCode() { return Objects.hash(parent, nameOrId); } @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof FrameByNameOrIdWebDriverTarget)) { return false; } FrameByNameOrIdWebDriverTarget other = (FrameByNameOrIdWebDriverTarget) object; return this.nameOrId.equals(other.nameOrId) && this.parent.equals(other.parent); } @Override public String toString() { return "FrameByNameOrIdWebDriverTarget: {parent: " + parent + ", nameOrId: " + nameOrId + "}"; } } public static class FrameByElementWebDriverTarget implements FrameTarget { private final WebDriverTarget parent; private final WebElement frameElement; FrameByElementWebDriverTarget(WebDriverTarget parent, WebElement frameElement) { this.parent = Objects.requireNonNull(parent, "parent"); this.frameElement = Objects.requireNonNull(frameElement, "frameElement"); } @Override public WebDriverTarget getParent() { return parent; } @Override public WebDriver switchTo(TargetLocator targetLocator) { // This is ugly :( if (targetLocator instanceof CachingTargetLocator) { return ((CachingTargetLocator) targetLocator).frame(parent, frameElement); } parent.switchTo(targetLocator); return targetLocator.frame(frameElement); } @Override public int hashCode() { return Objects.hash(parent, frameElement); } @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof FrameByElementWebDriverTarget)) { return false; } FrameByElementWebDriverTarget other = (FrameByElementWebDriverTarget) object; return this.frameElement.equals(other.frameElement) && this.parent.equals(other.parent); } @Override public String toString() { return "FrameByElementWebDriverTarget: {parent: " + parent + ", frameElement: " + frameElement + "}"; } } public static class DefaultContextWebDriverTarget implements WebDriverTarget { @Override public WebDriver switchTo(TargetLocator targetLocator) { return targetLocator.defaultContent(); } @Override public int hashCode() { return toString().hashCode(); } @Override public boolean equals(Object object) { return object instanceof DefaultContextWebDriverTarget; } @Override public String toString() { return "DefaultContextWebDriverTarget"; } } public static final class ViewWebDriverTarget implements WebDriverTarget, Caching { private final View view; private final ParentContext parentContext; /** * Stores window handle of window which has view loaded so subsequent lookups always refer * to same window. */ private String windowHandle; public ViewWebDriverTarget(View view, ParentContext parentContext) { this.view = Objects.requireNonNull(view, "view"); this.parentContext = Objects.requireNonNull(parentContext, "parentContext"); if (!(parentContext instanceof FindsById)) { throw new IllegalArgumentException("Context must be able to find by id using " + "WebDriver window handles."); } } @Override public WebDriver switchTo(TargetLocator targetLocator) { if (windowHandle == null) { windowHandle = findWindow(targetLocator); } return targetLocator.window(windowHandle); } @Override public void invalidateCache() { windowHandle = null; } @Override public int hashCode() { return toString().hashCode(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ViewWebDriverTarget that = (ViewWebDriverTarget) o; return Objects.equals(view, that.view) && Objects.equals(parentContext, that.parentContext); } @Override public String toString() { return "ViewWebDriverTarget{" + "view=" + view + ", parentContext=" + parentContext + ", windowHandle='" + windowHandle + '\'' + '}'; } private String findWindow(TargetLocator targetLocator) { for (String windowHandle : targetLocator.defaultContent().getWindowHandles()) { Browser forWindowHandle = By.id(windowHandle).find(Browser.class, parentContext); view.setContext(forWindowHandle); if (view.isLoaded()) { return windowHandle; } } throw new NoSuchWindowException("No window in driver found which has " + view + " " + "currently loaded."); } } private static class WindowTitleWebDriverTarget implements WebDriverTarget, Caching { private final String title; /** * Stores window handle of window which has the title so subsequent lookups always refer to * the same window. */ private String windowHandle; public WindowTitleWebDriverTarget(String title) { this.title = Objects.requireNonNull(title, "title"); } @Override public WebDriver switchTo(TargetLocator targetLocator) { if (windowHandle == null) { windowHandle = findWindow(targetLocator); } return targetLocator.window(windowHandle); } @Override public void invalidateCache() { windowHandle = null; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } WindowTitleWebDriverTarget that = (WindowTitleWebDriverTarget) o; return Objects.equals(title, that.title); } @Override public int hashCode() { return Objects.hash(title); } @Override public String toString() { return "WindowTitleWebDriverTarget{" + "title='" + title + '\'' + ", windowHandle='" + windowHandle + '\'' + '}'; } private String findWindow(TargetLocator targetLocator) { for (String windowHandle : targetLocator.defaultContent().getWindowHandles()) { if (targetLocator.window(windowHandle).getTitle().equals(title)) { return windowHandle; } } throw new NoSuchWindowException("No window in driver found which has title: " + title); } } public static class WindowUrlWebDriverTarget implements WebDriverTarget, Caching { private final Matcher<? super String> urlMatcher; /** * Stores window handle of window which has the title so subsequent lookups always refer to * the same window. */ private String windowHandle; public WindowUrlWebDriverTarget(Matcher<? super String> urlMatcher) { this.urlMatcher = Objects.requireNonNull(urlMatcher, "urlMatcher"); } @Override public WebDriver switchTo(TargetLocator targetLocator) { if (windowHandle == null) { windowHandle = findWindow(targetLocator); } return targetLocator.window(windowHandle); } @Override public void invalidateCache() { windowHandle = null; } private String findWindow(TargetLocator targetLocator) { for (String windowHandle : targetLocator.defaultContent().getWindowHandles()) { if (urlMatcher.matches(targetLocator.window(windowHandle).getCurrentUrl())) { return windowHandle; } } throw new NoSuchWindowException("No window in driver found which has url matching: " + urlMatcher); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } WindowUrlWebDriverTarget that = (WindowUrlWebDriverTarget) o; return Objects.equals(urlMatcher, that.urlMatcher) && Objects.equals(windowHandle, that.windowHandle); } @Override public int hashCode() { return Objects.hash(urlMatcher, windowHandle); } @Override public String toString() { return "WindowUrlWebDriverTarget{" + "urlMatcher=" + urlMatcher + ", windowHandle='" + windowHandle + '\'' + '}'; } } }