import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
import static util.ItUtils.projectDir;
import org.sonar.wsclient.services.PropertyQuery;
import org.sonar.wsclient.services.ResourceQuery;
import org.sonar.wsclient.user.UserParameters;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.projectDir;
import org.junit.Test;
import org.sonar.wsclient.services.PropertyQuery;
import org.sonar.wsclient.services.PropertyUpdateQuery;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.ClassRule;
import org.junit.Test;
import org.sonar.wsclient.services.PropertyQuery;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.projectDir;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
import static util.ItUtils.projectDir;
import org.junit.Test;
import org.sonar.wsclient.SonarClient;
import org.sonar.wsclient.user.UserParameters;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
import static util.ItUtils.projectDir;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
public class ManualRulesTest {
import org.sonar.wsclient.services.ResourceQuery;
import org.subethamail.wiser.Wiser;
import org.subethamail.wiser.WiserMessage;
-import selenium.SeleneseTest;
import util.ItUtils;
+import util.selenium.SeleneseTest;
import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.projectDir;
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import org.openqa.selenium.firefox.FirefoxDriver;
-
-public enum Browser {
- FIREFOX;
-
- private final ThreadLocal<SeleniumDriver> perThreadDriver = new ThreadLocal<SeleniumDriver>() {
- @Override
- protected SeleniumDriver initialValue() {
- return ThreadSafeDriver.makeThreadSafe(new FirefoxDriver());
- }
- };
-
- public SeleniumDriver getDriverForThread() {
- return perThreadDriver.get();
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import org.openqa.selenium.By;
-import org.openqa.selenium.SearchContext;
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.internal.FindsByCssSelector;
-import org.openqa.selenium.internal.FindsById;
-import org.openqa.selenium.internal.FindsByName;
-
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.List;
-
-public class ByCssSelectorOrByNameOrById extends By implements Serializable {
- private static final long serialVersionUID = -3910258723099459239L;
-
- private final String selector;
-
- public ByCssSelectorOrByNameOrById(String selector) {
- this.selector = selector;
- }
-
- @Override
- public WebElement findElement(SearchContext context) {
- WebElement element;
-
- if (validCssSelector(selector)) {
- element = ((FindsByCssSelector) context).findElementByCssSelector(quoteCss(selector));
- if (element != null) {
- return element;
- }
- }
-
- element = ((FindsByName) context).findElementByName(selector);
- if (element != null) {
- return element;
- }
-
- element = ((FindsById) context).findElementById(selector);
- if (element != null) {
- return element;
- }
-
- return null;
- }
-
- @Override
- public List<WebElement> findElements(SearchContext context) {
- List<WebElement> elements;
-
- if (validCssSelector(selector)) {
- elements = ((FindsByCssSelector) context).findElementsByCssSelector(quoteCss(selector));
- if ((elements != null) && (!elements.isEmpty())) {
- return elements;
- }
- }
-
- elements = ((FindsByName) context).findElementsByName(selector);
- if ((elements != null) && (!elements.isEmpty())) {
- return elements;
- }
-
- elements = ((FindsById) context).findElementsById(selector);
- if ((elements != null) && (!elements.isEmpty())) {
- return elements;
- }
-
- return Collections.emptyList();
- }
-
- protected boolean validCssSelector(String selector) {
- return !selector.endsWith("[]");
- }
-
- protected String quoteCss(String selector) {
- if (selector.startsWith(".")) {
- return selector;
- }
- if (selector.startsWith("#")) {
- return selector.replaceAll("(\\w)[.]", "$1\\\\.");
- }
- return selector;
- }
-
- @Override
- public String toString() {
- return selector;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-public interface Consumer<T> {
- void accept(T t);
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import com.google.common.base.Function;
-import org.openqa.selenium.WebElement;
-
-import java.util.Collection;
-
-class ElementFilter {
- private static final ElementFilter ANY = new ElementFilter("", new Function<Collection<WebElement>, Collection<WebElement>>() {
- @Override
- public Collection<WebElement> apply(Collection<WebElement> input) {
- return input;
- }
- });
-
- private final String description;
- private final Function<Collection<WebElement>, Collection<WebElement>> filter;
-
- ElementFilter(String description, Function<Collection<WebElement>, Collection<WebElement>> filter) {
- this.description = description;
- this.filter = filter;
- }
-
- public String getDescription() {
- return description;
- }
-
- public Function<Collection<WebElement>, Collection<WebElement>> getFilter() {
- return filter;
- }
-
- public static ElementFilter any() {
- return ANY;
- }
-
- public ElementFilter and(final ElementFilter second) {
- if (ANY == this) {
- return second;
- }
- if (ANY == second) {
- return this;
- }
- return new ElementFilter(description + ',' + second.description, new Function<Collection<WebElement>, Collection<WebElement>>() {
- @Override
- public Collection<WebElement> apply(Collection<WebElement> stream) {
- return second.filter.apply(filter.apply(stream));
- }
- });
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class Failure {
- private static final String PREFIX = Failure.class.getPackage().getName() + ".";
-
- private Failure() {
- // Static class
- }
-
- public static AssertionError create(String message) {
- AssertionError error = new AssertionError(message);
- removeSimpleleniumFromStackTrace(error);
- return error;
- }
-
- private static void removeSimpleleniumFromStackTrace(Throwable throwable) {
- List<StackTraceElement> filtered = new ArrayList<>();
-
- for (StackTraceElement element : throwable.getStackTrace()) {
- if (!element.getClassName().contains(PREFIX)) {
- filtered.add(element);
- }
- }
-
- throwable.setStackTrace(filtered.toArray(new StackTraceElement[filtered.size()]));
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.base.Supplier;
-import com.google.common.collect.FluentIterable;
-import org.openqa.selenium.By;
-import org.openqa.selenium.Keys;
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.ui.Select;
-
-import javax.annotation.Nullable;
-import java.util.Collection;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-class LazyDomElement {
- private final SeleniumDriver driver;
- private final By selector;
- private final ElementFilter filter;
- private final Retry retry;
-
- LazyDomElement(SeleniumDriver driver, By selector) {
- this(driver, selector, Retry._30_SECONDS);
- }
-
- LazyDomElement(SeleniumDriver driver, By selector, Retry retry) {
- this(driver, selector, ElementFilter.any(), retry);
- }
-
- private LazyDomElement(SeleniumDriver driver, By selector, ElementFilter filter, Retry retry) {
- this.driver = driver;
- this.selector = selector;
- this.filter = filter;
- this.retry = retry;
- }
-
- public LazyDomElement withText(final String text) {
- String fullDescription = " with text [" + text + "]";
-
- return with(new ElementFilter(fullDescription, new Function<Collection<WebElement>, Collection<WebElement>>() {
- @Override
- public Collection<WebElement> apply(Collection<WebElement> stream) {
- return FluentIterable.from(stream).filter(new Predicate<WebElement>() {
- @Override
- public boolean apply(@Nullable WebElement element) {
-// return Objects.equals(element.getText(), text);
- return element.getText().contains(text);
- }
- }).toList();
- }
- }));
- }
-
- public LazyShould should() {
- return new LazyShould(this, Retry._30_SECONDS, true);
- }
-
- public void fill(final CharSequence text) {
- execute("fill(" + text + ")", new Consumer<WebElement>() {
- @Override
- public void accept(WebElement element) {
- element.clear();
- element.sendKeys(text);
- }
- });
- }
-
- public void pressEnter() {
- execute("pressEnter", new Consumer<WebElement>() {
- @Override
- public void accept(WebElement element) {
- element.sendKeys(Keys.ENTER);
- }
- });
- }
-
- public void select(final String text) {
- executeSelect("select(" + text + ")", new Consumer<Select>() {
- @Override
- public void accept(Select select) {
- select.selectByVisibleText(text);
- }
- });
- }
-
- public void executeSelect(String description, final Consumer<Select> selectOnElement) {
- execute(description, new Consumer<WebElement>() {
- @Override
- public void accept(WebElement element) {
- selectOnElement.accept(new Select(element));
- }
- });
- }
-
- public void click() {
- execute("click", new Consumer<WebElement>() {
- @Override
- public void accept(WebElement element) {
- element.click();
- }
- });
- }
-
- public void check() {
- execute("check", new Consumer<WebElement>() {
- @Override
- public void accept(WebElement element) {
- if (!element.isSelected()) {
- element.click();
- }
- }
- });
- }
-
- public void execute(Consumer<WebElement> action) {
- execute("execute(" + action + ")", action);
- }
-
- private LazyDomElement with(ElementFilter filter) {
- return new LazyDomElement(driver, selector, this.filter.and(filter), retry);
- }
-
- private void execute(String message, Consumer<WebElement> action) {
- System.out.println(" - " + Text.toString(selector) + filter.getDescription() + "." + message);
-
- Supplier<Optional<WebElement>> findOne = new Supplier<Optional<WebElement>>() {
- @Override
- public Optional<WebElement> get() {
- List<WebElement> elements = stream();
- if (elements.isEmpty()) {
- return Optional.empty();
- }
- return Optional.of(elements.get(0));
- }
- };
-
- try {
- retry.execute(findOne, action);
- } catch (NoSuchElementException e) {
- throw new AssertionError("Element not found: " + Text.toString(selector));
- }
- }
-
- List<WebElement> stream() {
- return FluentIterable.from(filter.getFilter().apply(driver.findElements(selector))).toList();
- }
-
- @Override
- public String toString() {
- return Text.toString(selector) + filter.getDescription();
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import com.google.common.base.*;
-import com.google.common.collect.FluentIterable;
-import org.openqa.selenium.WebElement;
-
-import javax.annotation.Nullable;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.regex.Pattern;
-
-import static selenium.Text.plural;
-import static selenium.WebElementHelper.text;
-
-class LazyShould {
- private final LazyDomElement element;
- private final Retry retry;
- private final boolean ok;
-
- LazyShould(LazyDomElement element, Retry retry, boolean ok) {
- this.element = element;
- this.retry = retry;
- this.ok = ok;
- }
-
- public LazyShould beDisplayed() {
- return verify(
- isOrNot("displayed"),
- new Predicate<List<WebElement>>() {
- @Override
- public boolean apply(List<WebElement> elements) {
- return !elements.isEmpty() && FluentIterable.from(elements).allMatch(new Predicate<WebElement>() {
- @Override
- public boolean apply(WebElement element) {
- return element.isDisplayed();
- }
- });
- }
- },
- new Function<List<WebElement>, String>() {
- @Override
- public String apply(List<WebElement> elements) {
- return "It is " + statuses(elements, new Function<WebElement, String>() {
- @Override
- public String apply(WebElement element) {
- return displayedStatus(element);
- }
- });
- }
- });
- }
-
- public LazyShould match(final Pattern regexp) {
- return verify(
- doesOrNot("match") + " (" + regexp.pattern() + ")",
- new Predicate<List<WebElement>>() {
- @Override
- public boolean apply(List<WebElement> elements) {
- return !elements.isEmpty() && FluentIterable.from(elements).anyMatch(new Predicate<WebElement>() {
- @Override
- public boolean apply(WebElement element) {
- return regexp.matcher(text(element)).matches();
- }
- });
- }
- },
- new Function<List<WebElement>, String>() {
- @Override
- public String apply(List<WebElement> elements) {
- return "It contains " + statuses(elements, new Function<WebElement, String>() {
- @Nullable
- @Override
- public String apply(@Nullable WebElement element) {
- return text(element);
- }
- });
- }
- });
- }
-
- public LazyShould contain(final String text) {
- return verify(
- doesOrNot("contain") + "(" + text + ")",
- new Predicate<List<WebElement>>() {
- @Override
- public boolean apply(List<WebElement> elements) {
- return FluentIterable.from(elements).anyMatch(new Predicate<WebElement>() {
- @Override
- public boolean apply(@Nullable WebElement element) {
- if (text.startsWith("exact:")) {
- return text(element).equals(text.substring(6));
- }
- return text(element).contains(text);
- }
- });
- }
- },
- new Function<List<WebElement>, String>() {
- @Override
- public String apply(List<WebElement> elements) {
- return "It contains " + statuses(elements, new Function<WebElement, String>() {
- @Override
- public String apply(WebElement element) {
- return text(element);
- }
- });
- }
- });
- }
-
- public LazyShould exist() {
- return verify(
- doesOrNot("exist"),
- new Predicate<List<WebElement>>() {
- @Override
- public boolean apply(List<WebElement> elements) {
- return !elements.isEmpty();
- }
- },
- new Function<List<WebElement>, String>() {
- @Override
- public String apply(List<WebElement> elements) {
- return "It contains " + plural(elements.size(), "element");
- }
- });
- }
-
- private static String displayedStatus(WebElement element) {
- return element.isDisplayed() ? "displayed" : "not displayed";
- }
-
- private LazyShould verify(String message, Predicate<List<WebElement>> predicate, Function<List<WebElement>, String> toErrorMessage) {
- String verification = "verify that " + element + " " + message;
- System.out.println(" -> " + verification);
-
- try {
- if (!retry.verify(new Supplier<List<WebElement>>() {
- @Override
- public List<WebElement> get() {
- return LazyShould.this.findElements();
- }
- }, ok ? predicate : Predicates.not(predicate))) {
- throw Failure.create("Failed to " + verification + ". " + toErrorMessage.apply(findElements()));
- }
- } catch (NoSuchElementException e) {
- throw Failure.create("Element not found. Failed to " + verification);
- }
-
- return ok ? this : not();
- }
-
- private List<WebElement> findElements() {
- return element.stream();
- }
-
- private static String statuses(List<WebElement> elements, Function<WebElement, String> toStatus) {
- return "(" + FluentIterable.from(elements).transform(toStatus).join(Joiner.on(";")) + ")";
- }
-
- public LazyShould not() {
- return new LazyShould(element, retry, !ok);
- }
-
- private String doesOrNot(String verb) {
- return Text.doesOrNot(!ok, verb);
- }
-
- private String isOrNot(String state) {
- return Text.isOrNot(!ok, state);
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import java.util.NoSuchElementException;
-
-public final class Optional<T> {
- private static final Optional<?> EMPTY = new Optional<>();
-
- private final T value;
-
- private Optional() {
- this.value = null;
- }
-
- public static <T> Optional<T> empty() {
- @SuppressWarnings("unchecked")
- Optional<T> t = (Optional<T>) EMPTY;
- return t;
- }
-
- private Optional(T value) {
- this.value = value;
- }
-
- public static <T> Optional<T> of(T value) {
- return new Optional<>(value);
- }
-
- public T get() {
- if (value == null) {
- throw new NoSuchElementException("No value present");
- }
- return value;
- }
-
- public boolean isPresent() {
- return value != null;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Supplier;
-import org.openqa.selenium.InvalidElementStateException;
-import org.openqa.selenium.NotFoundException;
-import org.openqa.selenium.StaleElementReferenceException;
-import org.openqa.selenium.WebDriverException;
-
-import java.util.NoSuchElementException;
-import java.util.concurrent.TimeUnit;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-class Retry {
- public static final Retry _30_SECONDS = new Retry(30, SECONDS);
-
- private final long timeoutInMs;
-
- Retry(long duration, TimeUnit timeUnit) {
- this.timeoutInMs = timeUnit.toMillis(duration);
- }
-
- <T> void execute(Supplier<Optional<T>> target, Consumer<T> action) {
- WebDriverException lastError = null;
-
- boolean retried = false;
-
- long start = System.currentTimeMillis();
- while ((System.currentTimeMillis() - start) < timeoutInMs) {
- try {
- Optional<T> targetElement = target.get();
- if (targetElement.isPresent()) {
- action.accept(targetElement.get());
- if (retried) {
- System.out.println();
- }
- return;
- }
- } catch (StaleElementReferenceException e) {
- // ignore
- } catch (WebDriverException e) {
- lastError = e;
- }
-
- retried = true;
- System.out.print(".");
- }
-
- if (retried) {
- System.out.println();
- }
-
- if (lastError != null) {
- throw lastError;
- }
- throw new NoSuchElementException("Not found");
- }
-
- <T> void execute(Runnable action) {
- WebDriverException lastError = null;
-
- boolean retried = false;
-
- long start = System.currentTimeMillis();
- while ((System.currentTimeMillis() - start) < timeoutInMs) {
- try {
- action.run();
- if (retried) {
- System.out.println();
- }
- return;
- } catch (StaleElementReferenceException e) {
- // ignore
- } catch (WebDriverException e) {
- lastError = e;
- }
-
- retried = true;
- System.out.print(".");
- }
-
- if (retried) {
- System.out.println();
- }
-
- if (lastError != null) {
- throw lastError;
- }
- throw new NoSuchElementException("Not found");
- }
-
- <T> boolean verify(Supplier<T> targetSupplier, Predicate<T> predicate) throws NoSuchElementException {
- Error error = Error.KO;
-
- boolean retried = false;
-
- long start = System.currentTimeMillis();
- while ((System.currentTimeMillis() - start) < timeoutInMs) {
- try {
- if (predicate.apply(targetSupplier.get())) {
- if (retried) {
- System.out.println();
- }
- return true;
- }
-
- error = Error.KO;
- } catch (InvalidElementStateException e) {
- error = Error.KO;
- } catch (NotFoundException e) {
- error = Error.NOT_FOUND;
- } catch (StaleElementReferenceException e) {
- // ignore
- }
-
- retried = true;
- System.out.print(".");
- }
-
- if (retried) {
- System.out.println();
- }
-
- if (error == Error.NOT_FOUND) {
- throw new NoSuchElementException("Not found");
- }
- return false;
- }
-
- enum Error {
- NOT_FOUND, KO
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.selenium.Selenese;
-import org.assertj.core.util.Strings;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
-import org.openqa.selenium.By;
-import org.openqa.selenium.NotFoundException;
-import org.openqa.selenium.WebElement;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Objects.requireNonNull;
-import static java.util.regex.Pattern.DOTALL;
-import static org.assertj.core.api.Assertions.assertThat;
-import static selenium.Retry._30_SECONDS;
-
-public class SeleneseTest {
- private final Selenese suite;
-
- private Map<String, String> variables;
- private String baseUrl;
- private SeleniumDriver driver;
-
- public SeleneseTest(Selenese suite) {
- this.suite = suite;
- }
-
- public void runOn(Orchestrator orchestrator) {
- this.variables = new HashMap<>();
- this.baseUrl = orchestrator.getServer().getUrl();
- this.driver = Browser.FIREFOX.getDriverForThread();
-
- driver.manage().deleteAllCookies();
-
- for (File file : suite.getHtmlTests()) {
- System.out.println();
- System.out.println("============ " + file.getName() + " ============");
- Document doc = parse(file);
- for (Element table : doc.getElementsByTag("table")) {
- for (Element tbody : table.getElementsByTag("tbody")) {
- for (Element tr : tbody.getElementsByTag("tr")) {
- String action = tr.child(0).text();
- String param1 = tr.child(1).text();
- String param2 = tr.child(2).text();
-
- action(action, param1, param2);
- }
- }
- }
- }
- }
-
- private static Document parse(File file) {
- try {
- return Jsoup.parse(file, UTF_8.name());
- } catch (IOException e) {
- throw new RuntimeException("Unable to parse file: " + file, e);
- }
- }
-
- public SeleneseTest action(String action, String param1, String param2) {
- switch (action) {
- case "open":
- open(param1);
- return this;
- case "type":
- type(param1, param2);
- return this;
- case "keyPressAndWait":
- keyPressAndWait(param1, param2);
- return this;
- case "select":
- select(param1, param2);
- return this;
- case "clickAndWait":
- case "click":
- click(param1);
- return this;
- case "check":
- check(param1);
- return this;
- case "selectFrame":
- selectFrame(param1);
- return this;
- case "assertElementPresent":
- assertElementPresent(param1);
- return this;
- case "assertElementNotPresent":
- assertElementNotPresent(param1);
- return this;
- case "storeText":
- storeText(param1, param2);
- return this;
- case "storeEval":
- storeEval(param1, param2);
- return this;
- case "store":
- store(param1, param2);
- return this;
- case "assertText":
- case "waitForText":
- assertText(param1, param2);
- return this;
- case "assertNotText":
- case "waitForNotText":
- assertNotText(param1, param2);
- return this;
- case "assertTextPresent":
- assertTextPresent(param1);
- return this;
- case "assertTextNotPresent":
- assertTextNotPresent(param1);
- return this;
- case "assertLocation":
- assertLocation(param1);
- return this;
- case "waitForElementPresent":
- waitForElementPresent(param1, param2);
- return this;
- case "waitForVisible":
- waitForVisible(param1);
- return this;
- case "assertValue":
- case "waitForValue":
- case "verifyValue":
- assertInputValue(param1, param2);
- return this;
- case "assertConfirmation":
- confirm(param1);
- return this;
- case "setTimeout":
- case "pause":
- // Ignore
- return this;
- }
-
- throw new IllegalArgumentException("Unsupported action: " + action);
- }
-
- private void open(String url) {
- if (url.startsWith("/sonar/")) {
- goTo(url.substring(6));
- } else {
- goTo(url);
- }
- }
-
- private void goTo(String url) {
- requireNonNull(url, "The url cannot be null");
-
- url = replacePlaceholders(url);
-
- URI uri = URI.create(url.replace(" ", "%20"));
- if (!uri.isAbsolute()) {
- url = baseUrl + url;
- }
-
- System.out.println("goTo " + url);
- driver.get(url);
- System.out.println(" - current url " + driver.getCurrentUrl());
- }
-
- private LazyDomElement find(String selector) {
- selector = replacePlaceholders(selector);
-
- if (selector.startsWith("link=") || selector.startsWith("Link=")) {
- return find("a").withText(selector.substring(5));
- }
-
- By by;
- if (selector.startsWith("//")) {
- by = new By.ByXPath(selector);
- } else if (selector.startsWith("xpath=")) {
- by = new By.ByXPath(selector.substring(6));
- } else if (selector.startsWith("id=")) {
- by = new By.ById(selector.substring(3));
- } else if (selector.startsWith("name=")) {
- by = new By.ByName(selector.substring(5));
- } else if (selector.startsWith("css=")) {
- by = new By.ByCssSelector(selector.substring(4));
- } else if (selector.startsWith("class=")) {
- by = new By.ByCssSelector("." + selector.substring(6));
- } else {
- by = new ByCssSelectorOrByNameOrById(selector);
- }
-
- return new LazyDomElement(driver, by);
- }
-
- private void click(String selector) {
- find(selector).click();
- }
-
- private void check(String selector) {
- find(selector).check();
- }
-
- private void selectFrame(final String id) {
- if ("relative=parent".equals(id)) {
- return;
- }
-
- System.out.println(" - selectFrame(" + id + ")");
- _30_SECONDS.execute(new Runnable() {
- @Override
- public void run() {
- driver.switchTo().frame(id);
- }
- });
- }
-
- private void type(String selector, String text) {
- find(selector).fill(replacePlaceholders(text));
- }
-
- private void keyPressAndWait(String selector, String key) {
- if (!key.equals("\\13")) {
- throw new IllegalArgumentException("Invalid key: " + key);
- }
- find(selector).pressEnter();
- }
-
- private void select(String selector, String text) {
- if (text.startsWith("label=")) {
- find(selector).select(text.substring(6));
- } else {
- find(selector).select(text);
- }
- }
-
- private void assertElementPresent(String selector) {
- find(selector).should().beDisplayed();
- }
-
- private void assertElementNotPresent(String selector) {
- find(selector).should().not().beDisplayed();
- }
-
- private void storeText(String selector, String name) {
- find(selector).execute(new ExtractVariable(name));
- }
-
- private void storeEval(final String expression, final String name) {
- // Retry until it's not null and doesn't fail
- _30_SECONDS.execute(new Runnable() {
- @Override
- public void run() {
- Object result = driver.executeScript("return " + expression);
- if (result == null) {
- throw new NotFoundException(expression);
- }
- String value = result.toString();
- variables.put(name, value);
- }
- });
- }
-
- private void store(String expression, String name) {
- if (expression.startsWith("javascript{") && expression.endsWith("}")) {
- storeEval(expression.substring(11, expression.length() - 1), name);
- } else {
- throw new IllegalArgumentException("Invalid store expression: " + expression);
- }
- }
-
- private class ExtractVariable implements Consumer<WebElement> {
- private final String name;
-
- ExtractVariable(String name) {
- this.name = name;
- }
-
- @Override
- public void accept(WebElement webElement) {
- variables.put(name, webElement.getText());
- }
-
- public String toString() {
- return "read value into " + name;
- }
- }
-
- private void assertText(String selector, String pattern) {
- pattern = replacePlaceholders(pattern);
-
- if (pattern.startsWith("exact:")) {
- String expectedText = pattern.substring(6);
- find(selector).withText(expectedText).should().exist();
- return;
- }
-
- if (pattern.startsWith("regexp:")) {
- find(selector).should().match(regex(pattern));
- return;
- }
-
- find(selector).should().match(glob(pattern));
- }
-
- private void assertNotText(String selector, String pattern) {
- pattern = replacePlaceholders(pattern);
-
- if (pattern.startsWith("exact:")) {
- String expectedText = pattern.substring(6);
- find(selector).withText(expectedText).should().not().exist();
- return;
- }
-
- if (pattern.startsWith("regexp:")) {
- find(selector).should().not().match(regex(pattern));
- return;
- }
-
- find(selector).should().not().match(glob(pattern));
- }
-
- private static Pattern glob(String pattern) {
- String regexp = pattern.replaceFirst("glob:", "");
- regexp = regexp.replaceAll("([\\]\\[\\\\{\\}$\\(\\)\\|\\^\\+.])", "\\\\$1");
- regexp = regexp.replaceAll("\\*", ".*");
- regexp = regexp.replaceAll("\\?", ".");
- return Pattern.compile(regexp, DOTALL | Pattern.CASE_INSENSITIVE);
- }
-
- private static Pattern regex(String pattern) {
- String regexp = pattern.replaceFirst("regexp:", ".*") + ".*";
- return Pattern.compile(regexp, DOTALL | Pattern.CASE_INSENSITIVE);
- }
-
- private void assertTextPresent(String text) {
- find("body").should().contain(text);
- }
-
- private void assertTextNotPresent(String text) {
- find("body").should().not().contain(text);
- }
-
- private void waitForElementPresent(String selector, String text) {
- if (Strings.isNullOrEmpty(text)) {
- find(selector).should().exist();
- } else {
- find(selector).withText(text).should().exist();
- }
- }
-
- private void waitForVisible(String selector) {
- find(selector).should().beDisplayed();
- }
-
- private void assertInputValue(String selector, String text) {
- find(selector).should().contain(text);
- }
-
- private void confirm(final String message) {
- System.out.println(" - confirm(" + message + ")");
-
- _30_SECONDS.execute(new Runnable() {
- @Override
- public void run() {
- driver.switchTo().alert().accept();
- }
- });
- }
-
- private void assertLocation(String urlPattern) {
- assertThat(driver.getCurrentUrl()).matches(glob(urlPattern));
- }
-
- private String replacePlaceholders(String text) {
- for (Map.Entry<String, String> entry : variables.entrySet()) {
- text = text.replace("${" + entry.getKey() + "}", entry.getValue());
- }
- return text;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-public interface SeleniumDriver extends org.openqa.selenium.WebDriver, org.openqa.selenium.JavascriptExecutor, org.openqa.selenium.internal.FindsById, org.openqa.selenium.internal.FindsByClassName, org.openqa.selenium.internal.FindsByLinkText, org.openqa.selenium.internal.FindsByName, org.openqa.selenium.internal.FindsByCssSelector, org.openqa.selenium.internal.FindsByTagName, org.openqa.selenium.internal.FindsByXPath, org.openqa.selenium.interactions.HasInputDevices, org.openqa.selenium.HasCapabilities, org.openqa.selenium.TakesScreenshot {
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import com.google.common.base.Joiner;
-import org.openqa.selenium.By;
-
-public abstract class Text {
- private Text() {
- // Static utility class
- }
-
- public static String doesOrNot(boolean not, String verb) {
- if (!verb.contains(" ")) {
- if (not) {
- return "doesn't " + verb;
- } else if (verb.endsWith("h")) {
- return verb + "es";
- } else {
- return verb + "s";
- }
- }
-
- String[] verbs = verb.split(" ");
- verbs[0] = doesOrNot(not, verbs[0]);
-
- return Joiner.on(" ").join(verbs);
- }
-
- public static String isOrNot(boolean not, String state) {
- return (not ? "is not " : "is ") + state;
- }
-
- public static String plural(int n, String word) {
- return (n + " " + word) + (n <= 1 ? "" : "s");
- }
-
- public static String toString(By selector) {
- return selector.toString().replace("By.selector: ", "").replace("By.cssSelector: ", "");
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import org.openqa.selenium.remote.RemoteWebDriver;
-import org.openqa.selenium.remote.UnreachableBrowserException;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-class ThreadSafeDriver {
- private ThreadSafeDriver() {
- // Static class
- }
-
- static SeleniumDriver makeThreadSafe(final RemoteWebDriver driver) {
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- driver.quit();
- } catch (UnreachableBrowserException e) {
- // Ignore. The browser was killed properly
- }
- }
- }));
-
- return (SeleniumDriver) Proxy.newProxyInstance(
- Thread.currentThread().getContextClassLoader(),
- findInterfaces(driver),
- new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- if (method.getName().equals("quit")) {
- return null; // We don't want anybody to quit() our (per thread) driver
- }
-
- try {
- return method.invoke(driver, args);
- } catch (InvocationTargetException e) {
- throw e.getCause();
- }
- }
- });
- }
-
- private static Class[] findInterfaces(Object driver) {
- Set<Class<?>> interfaces = new LinkedHashSet<>();
-
- interfaces.add(SeleniumDriver.class);
-
- for (Class<?> parent = driver.getClass(); parent != null; ) {
- Collections.addAll(interfaces, parent.getInterfaces());
- parent = parent.getSuperclass();
- }
-
- return interfaces.toArray(new Class[interfaces.size()]);
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package selenium;
-
-import org.openqa.selenium.WebElement;
-
-class WebElementHelper {
- WebElementHelper() {
- // Static class
- }
-
- public static String text(WebElement element) {
- String text = element.getText();
- if (!"".equals(text)) {
- return nullToEmpty(text);
- }
-
- return nullToEmpty(element.getAttribute("value"));
- }
-
- private static String nullToEmpty(String text) {
- return (text == null) ? "" : text;
- }
-}
import org.junit.rules.ExpectedException;
import org.sonar.wsclient.services.Server;
import org.sonar.wsclient.services.ServerQuery;
-import selenium.SeleneseTest;
import util.ItUtils;
+import util.selenium.SeleneseTest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.sonar.wsclient.services.Server;
import org.sonar.wsclient.services.ServerQuery;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import org.sonar.wsclient.base.HttpException;
import org.sonar.wsclient.services.PropertyDeleteQuery;
import org.sonar.wsclient.services.PropertyUpdateQuery;
-import selenium.SeleneseTest;
import util.ItUtils;
+import util.selenium.SeleneseTest;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.sonar.wsclient.services.Plugin;
import org.sonar.wsclient.services.UpdateCenterQuery;
-import selenium.SeleneseTest;
+import util.selenium.SeleneseTest;
import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.pluginArtifact;
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import org.openqa.selenium.firefox.FirefoxDriver;
+
+public enum Browser {
+ FIREFOX;
+
+ private final ThreadLocal<SeleniumDriver> perThreadDriver = new ThreadLocal<SeleniumDriver>() {
+ @Override
+ protected SeleniumDriver initialValue() {
+ return ThreadSafeDriver.makeThreadSafe(new FirefoxDriver());
+ }
+ };
+
+ public SeleniumDriver getDriverForThread() {
+ return perThreadDriver.get();
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+import org.openqa.selenium.By;
+import org.openqa.selenium.SearchContext;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.internal.FindsByCssSelector;
+import org.openqa.selenium.internal.FindsById;
+import org.openqa.selenium.internal.FindsByName;
+
+public class ByCssSelectorOrByNameOrById extends By implements Serializable {
+ private static final long serialVersionUID = -3910258723099459239L;
+
+ private final String selector;
+
+ public ByCssSelectorOrByNameOrById(String selector) {
+ this.selector = selector;
+ }
+
+ @Override
+ public WebElement findElement(SearchContext context) {
+ WebElement element;
+
+ if (validCssSelector(selector)) {
+ element = ((FindsByCssSelector) context).findElementByCssSelector(quoteCss(selector));
+ if (element != null) {
+ return element;
+ }
+ }
+
+ element = ((FindsByName) context).findElementByName(selector);
+ if (element != null) {
+ return element;
+ }
+
+ element = ((FindsById) context).findElementById(selector);
+ if (element != null) {
+ return element;
+ }
+
+ return null;
+ }
+
+ @Override
+ public List<WebElement> findElements(SearchContext context) {
+ List<WebElement> elements;
+
+ if (validCssSelector(selector)) {
+ elements = ((FindsByCssSelector) context).findElementsByCssSelector(quoteCss(selector));
+ if ((elements != null) && (!elements.isEmpty())) {
+ return elements;
+ }
+ }
+
+ elements = ((FindsByName) context).findElementsByName(selector);
+ if ((elements != null) && (!elements.isEmpty())) {
+ return elements;
+ }
+
+ elements = ((FindsById) context).findElementsById(selector);
+ if ((elements != null) && (!elements.isEmpty())) {
+ return elements;
+ }
+
+ return Collections.emptyList();
+ }
+
+ protected boolean validCssSelector(String selector) {
+ return !selector.endsWith("[]");
+ }
+
+ protected String quoteCss(String selector) {
+ if (selector.startsWith(".")) {
+ return selector;
+ }
+ if (selector.startsWith("#")) {
+ return selector.replaceAll("(\\w)[.]", "$1\\\\.");
+ }
+ return selector;
+ }
+
+ @Override
+ public String toString() {
+ return selector;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+public interface Consumer<T> {
+ void accept(T t);
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import com.google.common.base.Function;
+import java.util.Collection;
+import org.openqa.selenium.WebElement;
+
+class ElementFilter {
+ private static final ElementFilter ANY = new ElementFilter("", new Function<Collection<WebElement>, Collection<WebElement>>() {
+ @Override
+ public Collection<WebElement> apply(Collection<WebElement> input) {
+ return input;
+ }
+ });
+
+ private final String description;
+ private final Function<Collection<WebElement>, Collection<WebElement>> filter;
+
+ ElementFilter(String description, Function<Collection<WebElement>, Collection<WebElement>> filter) {
+ this.description = description;
+ this.filter = filter;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Function<Collection<WebElement>, Collection<WebElement>> getFilter() {
+ return filter;
+ }
+
+ public static ElementFilter any() {
+ return ANY;
+ }
+
+ public ElementFilter and(final ElementFilter second) {
+ if (ANY == this) {
+ return second;
+ }
+ if (ANY == second) {
+ return this;
+ }
+ return new ElementFilter(description + ',' + second.description, new Function<Collection<WebElement>, Collection<WebElement>>() {
+ @Override
+ public Collection<WebElement> apply(Collection<WebElement> stream) {
+ return second.filter.apply(filter.apply(stream));
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class Failure {
+ private static final String PREFIX = Failure.class.getPackage().getName() + ".";
+
+ private Failure() {
+ // Static class
+ }
+
+ public static AssertionError create(String message) {
+ AssertionError error = new AssertionError(message);
+ removeSimpleleniumFromStackTrace(error);
+ return error;
+ }
+
+ private static void removeSimpleleniumFromStackTrace(Throwable throwable) {
+ List<StackTraceElement> filtered = new ArrayList<>();
+
+ for (StackTraceElement element : throwable.getStackTrace()) {
+ if (!element.getClassName().contains(PREFIX)) {
+ filtered.add(element);
+ }
+ }
+
+ throwable.setStackTrace(filtered.toArray(new StackTraceElement[filtered.size()]));
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.FluentIterable;
+import java.util.Collection;
+import java.util.List;
+import java.util.NoSuchElementException;
+import javax.annotation.Nullable;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
+
+class LazyDomElement {
+ private final SeleniumDriver driver;
+ private final By selector;
+ private final ElementFilter filter;
+ private final Retry retry;
+
+ LazyDomElement(SeleniumDriver driver, By selector) {
+ this(driver, selector, Retry._30_SECONDS);
+ }
+
+ LazyDomElement(SeleniumDriver driver, By selector, Retry retry) {
+ this(driver, selector, ElementFilter.any(), retry);
+ }
+
+ private LazyDomElement(SeleniumDriver driver, By selector, ElementFilter filter, Retry retry) {
+ this.driver = driver;
+ this.selector = selector;
+ this.filter = filter;
+ this.retry = retry;
+ }
+
+ public LazyDomElement withText(final String text) {
+ String fullDescription = " with text [" + text + "]";
+
+ return with(new ElementFilter(fullDescription, new Function<Collection<WebElement>, Collection<WebElement>>() {
+ @Override
+ public Collection<WebElement> apply(Collection<WebElement> stream) {
+ return FluentIterable.from(stream).filter(new Predicate<WebElement>() {
+ @Override
+ public boolean apply(@Nullable WebElement element) {
+// return Objects.equals(element.getText(), text);
+ return element.getText().contains(text);
+ }
+ }).toList();
+ }
+ }));
+ }
+
+ public LazyShould should() {
+ return new LazyShould(this, Retry._30_SECONDS, true);
+ }
+
+ public void fill(final CharSequence text) {
+ execute("fill(" + text + ")", new Consumer<WebElement>() {
+ @Override
+ public void accept(WebElement element) {
+ element.clear();
+ element.sendKeys(text);
+ }
+ });
+ }
+
+ public void pressEnter() {
+ execute("pressEnter", new Consumer<WebElement>() {
+ @Override
+ public void accept(WebElement element) {
+ element.sendKeys(Keys.ENTER);
+ }
+ });
+ }
+
+ public void select(final String text) {
+ executeSelect("select(" + text + ")", new Consumer<Select>() {
+ @Override
+ public void accept(Select select) {
+ select.selectByVisibleText(text);
+ }
+ });
+ }
+
+ public void executeSelect(String description, final Consumer<Select> selectOnElement) {
+ execute(description, new Consumer<WebElement>() {
+ @Override
+ public void accept(WebElement element) {
+ selectOnElement.accept(new Select(element));
+ }
+ });
+ }
+
+ public void click() {
+ execute("click", new Consumer<WebElement>() {
+ @Override
+ public void accept(WebElement element) {
+ element.click();
+ }
+ });
+ }
+
+ public void check() {
+ execute("check", new Consumer<WebElement>() {
+ @Override
+ public void accept(WebElement element) {
+ if (!element.isSelected()) {
+ element.click();
+ }
+ }
+ });
+ }
+
+ public void execute(Consumer<WebElement> action) {
+ execute("execute(" + action + ")", action);
+ }
+
+ private LazyDomElement with(ElementFilter filter) {
+ return new LazyDomElement(driver, selector, this.filter.and(filter), retry);
+ }
+
+ private void execute(String message, Consumer<WebElement> action) {
+ System.out.println(" - " + Text.toString(selector) + filter.getDescription() + "." + message);
+
+ Supplier<Optional<WebElement>> findOne = new Supplier<Optional<WebElement>>() {
+ @Override
+ public Optional<WebElement> get() {
+ List<WebElement> elements = stream();
+ if (elements.isEmpty()) {
+ return Optional.empty();
+ }
+ return Optional.of(elements.get(0));
+ }
+ };
+
+ try {
+ retry.execute(findOne, action);
+ } catch (NoSuchElementException e) {
+ throw new AssertionError("Element not found: " + Text.toString(selector));
+ }
+ }
+
+ List<WebElement> stream() {
+ return FluentIterable.from(filter.getFilter().apply(driver.findElements(selector))).toList();
+ }
+
+ @Override
+ public String toString() {
+ return Text.toString(selector) + filter.getDescription();
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.collect.FluentIterable;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+import org.openqa.selenium.WebElement;
+
+class LazyShould {
+ private final LazyDomElement element;
+ private final Retry retry;
+ private final boolean ok;
+
+ LazyShould(LazyDomElement element, Retry retry, boolean ok) {
+ this.element = element;
+ this.retry = retry;
+ this.ok = ok;
+ }
+
+ public LazyShould beDisplayed() {
+ return verify(
+ isOrNot("displayed"),
+ new Predicate<List<WebElement>>() {
+ @Override
+ public boolean apply(List<WebElement> elements) {
+ return !elements.isEmpty() && FluentIterable.from(elements).allMatch(new Predicate<WebElement>() {
+ @Override
+ public boolean apply(WebElement element) {
+ return element.isDisplayed();
+ }
+ });
+ }
+ },
+ new Function<List<WebElement>, String>() {
+ @Override
+ public String apply(List<WebElement> elements) {
+ return "It is " + statuses(elements, new Function<WebElement, String>() {
+ @Override
+ public String apply(WebElement element) {
+ return displayedStatus(element);
+ }
+ });
+ }
+ });
+ }
+
+ public LazyShould match(final Pattern regexp) {
+ return verify(
+ doesOrNot("match") + " (" + regexp.pattern() + ")",
+ new Predicate<List<WebElement>>() {
+ @Override
+ public boolean apply(List<WebElement> elements) {
+ return !elements.isEmpty() && FluentIterable.from(elements).anyMatch(new Predicate<WebElement>() {
+ @Override
+ public boolean apply(WebElement element) {
+ return regexp.matcher(WebElementHelper.text(element)).matches();
+ }
+ });
+ }
+ },
+ new Function<List<WebElement>, String>() {
+ @Override
+ public String apply(List<WebElement> elements) {
+ return "It contains " + statuses(elements, new Function<WebElement, String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable WebElement element) {
+ return WebElementHelper.text(element);
+ }
+ });
+ }
+ });
+ }
+
+ public LazyShould contain(final String text) {
+ return verify(
+ doesOrNot("contain") + "(" + text + ")",
+ new Predicate<List<WebElement>>() {
+ @Override
+ public boolean apply(List<WebElement> elements) {
+ return FluentIterable.from(elements).anyMatch(new Predicate<WebElement>() {
+ @Override
+ public boolean apply(@Nullable WebElement element) {
+ if (text.startsWith("exact:")) {
+ return WebElementHelper.text(element).equals(text.substring(6));
+ }
+ return WebElementHelper.text(element).contains(text);
+ }
+ });
+ }
+ },
+ new Function<List<WebElement>, String>() {
+ @Override
+ public String apply(List<WebElement> elements) {
+ return "It contains " + statuses(elements, new Function<WebElement, String>() {
+ @Override
+ public String apply(WebElement element) {
+ return WebElementHelper.text(element);
+ }
+ });
+ }
+ });
+ }
+
+ public LazyShould exist() {
+ return verify(
+ doesOrNot("exist"),
+ new Predicate<List<WebElement>>() {
+ @Override
+ public boolean apply(List<WebElement> elements) {
+ return !elements.isEmpty();
+ }
+ },
+ new Function<List<WebElement>, String>() {
+ @Override
+ public String apply(List<WebElement> elements) {
+ return "It contains " + Text.plural(elements.size(), "element");
+ }
+ });
+ }
+
+ private static String displayedStatus(WebElement element) {
+ return element.isDisplayed() ? "displayed" : "not displayed";
+ }
+
+ private LazyShould verify(String message, Predicate<List<WebElement>> predicate, Function<List<WebElement>, String> toErrorMessage) {
+ String verification = "verify that " + element + " " + message;
+ System.out.println(" -> " + verification);
+
+ try {
+ if (!retry.verify(new Supplier<List<WebElement>>() {
+ @Override
+ public List<WebElement> get() {
+ return LazyShould.this.findElements();
+ }
+ }, ok ? predicate : Predicates.not(predicate))) {
+ throw Failure.create("Failed to " + verification + ". " + toErrorMessage.apply(findElements()));
+ }
+ } catch (NoSuchElementException e) {
+ throw Failure.create("Element not found. Failed to " + verification);
+ }
+
+ return ok ? this : not();
+ }
+
+ private List<WebElement> findElements() {
+ return element.stream();
+ }
+
+ private static String statuses(List<WebElement> elements, Function<WebElement, String> toStatus) {
+ return "(" + FluentIterable.from(elements).transform(toStatus).join(Joiner.on(";")) + ")";
+ }
+
+ public LazyShould not() {
+ return new LazyShould(element, retry, !ok);
+ }
+
+ private String doesOrNot(String verb) {
+ return Text.doesOrNot(!ok, verb);
+ }
+
+ private String isOrNot(String state) {
+ return Text.isOrNot(!ok, state);
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import java.util.NoSuchElementException;
+
+public final class Optional<T> {
+ private static final Optional<?> EMPTY = new Optional<>();
+
+ private final T value;
+
+ private Optional() {
+ this.value = null;
+ }
+
+ public static <T> Optional<T> empty() {
+ @SuppressWarnings("unchecked")
+ Optional<T> t = (Optional<T>) EMPTY;
+ return t;
+ }
+
+ private Optional(T value) {
+ this.value = value;
+ }
+
+ public static <T> Optional<T> of(T value) {
+ return new Optional<>(value);
+ }
+
+ public T get() {
+ if (value == null) {
+ throw new NoSuchElementException("No value present");
+ }
+ return value;
+ }
+
+ public boolean isPresent() {
+ return value != null;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import java.util.NoSuchElementException;
+import java.util.concurrent.TimeUnit;
+import org.openqa.selenium.InvalidElementStateException;
+import org.openqa.selenium.NotFoundException;
+import org.openqa.selenium.StaleElementReferenceException;
+import org.openqa.selenium.WebDriverException;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+class Retry {
+ public static final Retry _30_SECONDS = new Retry(30, SECONDS);
+
+ private final long timeoutInMs;
+
+ Retry(long duration, TimeUnit timeUnit) {
+ this.timeoutInMs = timeUnit.toMillis(duration);
+ }
+
+ <T> void execute(Supplier<Optional<T>> target, Consumer<T> action) {
+ WebDriverException lastError = null;
+
+ boolean retried = false;
+
+ long start = System.currentTimeMillis();
+ while ((System.currentTimeMillis() - start) < timeoutInMs) {
+ try {
+ Optional<T> targetElement = target.get();
+ if (targetElement.isPresent()) {
+ action.accept(targetElement.get());
+ if (retried) {
+ System.out.println();
+ }
+ return;
+ }
+ } catch (StaleElementReferenceException e) {
+ // ignore
+ } catch (WebDriverException e) {
+ lastError = e;
+ }
+
+ retried = true;
+ System.out.print(".");
+ }
+
+ if (retried) {
+ System.out.println();
+ }
+
+ if (lastError != null) {
+ throw lastError;
+ }
+ throw new NoSuchElementException("Not found");
+ }
+
+ <T> void execute(Runnable action) {
+ WebDriverException lastError = null;
+
+ boolean retried = false;
+
+ long start = System.currentTimeMillis();
+ while ((System.currentTimeMillis() - start) < timeoutInMs) {
+ try {
+ action.run();
+ if (retried) {
+ System.out.println();
+ }
+ return;
+ } catch (StaleElementReferenceException e) {
+ // ignore
+ } catch (WebDriverException e) {
+ lastError = e;
+ }
+
+ retried = true;
+ System.out.print(".");
+ }
+
+ if (retried) {
+ System.out.println();
+ }
+
+ if (lastError != null) {
+ throw lastError;
+ }
+ throw new NoSuchElementException("Not found");
+ }
+
+ <T> boolean verify(Supplier<T> targetSupplier, Predicate<T> predicate) throws NoSuchElementException {
+ Error error = Error.KO;
+
+ boolean retried = false;
+
+ long start = System.currentTimeMillis();
+ while ((System.currentTimeMillis() - start) < timeoutInMs) {
+ try {
+ if (predicate.apply(targetSupplier.get())) {
+ if (retried) {
+ System.out.println();
+ }
+ return true;
+ }
+
+ error = Error.KO;
+ } catch (InvalidElementStateException e) {
+ error = Error.KO;
+ } catch (NotFoundException e) {
+ error = Error.NOT_FOUND;
+ } catch (StaleElementReferenceException e) {
+ // ignore
+ }
+
+ retried = true;
+ System.out.print(".");
+ }
+
+ if (retried) {
+ System.out.println();
+ }
+
+ if (error == Error.NOT_FOUND) {
+ throw new NoSuchElementException("Not found");
+ }
+ return false;
+ }
+
+ enum Error {
+ NOT_FOUND, KO
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.selenium.Selenese;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+import org.assertj.core.util.Strings;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NotFoundException;
+import org.openqa.selenium.WebElement;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+import static java.util.regex.Pattern.DOTALL;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.selenium.Retry._30_SECONDS;
+
+public class SeleneseTest {
+ private final Selenese suite;
+
+ private Map<String, String> variables;
+ private String baseUrl;
+ private SeleniumDriver driver;
+
+ public SeleneseTest(Selenese suite) {
+ this.suite = suite;
+ }
+
+ public void runOn(Orchestrator orchestrator) {
+ this.variables = new HashMap<>();
+ this.baseUrl = orchestrator.getServer().getUrl();
+ this.driver = Browser.FIREFOX.getDriverForThread();
+
+ driver.manage().deleteAllCookies();
+
+ for (File file : suite.getHtmlTests()) {
+ System.out.println();
+ System.out.println("============ " + file.getName() + " ============");
+ Document doc = parse(file);
+ for (Element table : doc.getElementsByTag("table")) {
+ for (Element tbody : table.getElementsByTag("tbody")) {
+ for (Element tr : tbody.getElementsByTag("tr")) {
+ String action = tr.child(0).text();
+ String param1 = tr.child(1).text();
+ String param2 = tr.child(2).text();
+
+ action(action, param1, param2);
+ }
+ }
+ }
+ }
+ }
+
+ private static Document parse(File file) {
+ try {
+ return Jsoup.parse(file, UTF_8.name());
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to parse file: " + file, e);
+ }
+ }
+
+ public SeleneseTest action(String action, String param1, String param2) {
+ switch (action) {
+ case "open":
+ open(param1);
+ return this;
+ case "type":
+ type(param1, param2);
+ return this;
+ case "keyPressAndWait":
+ keyPressAndWait(param1, param2);
+ return this;
+ case "select":
+ select(param1, param2);
+ return this;
+ case "clickAndWait":
+ case "click":
+ click(param1);
+ return this;
+ case "check":
+ check(param1);
+ return this;
+ case "selectFrame":
+ selectFrame(param1);
+ return this;
+ case "assertElementPresent":
+ assertElementPresent(param1);
+ return this;
+ case "assertElementNotPresent":
+ assertElementNotPresent(param1);
+ return this;
+ case "storeText":
+ storeText(param1, param2);
+ return this;
+ case "storeEval":
+ storeEval(param1, param2);
+ return this;
+ case "store":
+ store(param1, param2);
+ return this;
+ case "assertText":
+ case "waitForText":
+ assertText(param1, param2);
+ return this;
+ case "assertNotText":
+ case "waitForNotText":
+ assertNotText(param1, param2);
+ return this;
+ case "assertTextPresent":
+ assertTextPresent(param1);
+ return this;
+ case "assertTextNotPresent":
+ assertTextNotPresent(param1);
+ return this;
+ case "assertLocation":
+ assertLocation(param1);
+ return this;
+ case "waitForElementPresent":
+ waitForElementPresent(param1, param2);
+ return this;
+ case "waitForVisible":
+ waitForVisible(param1);
+ return this;
+ case "assertValue":
+ case "waitForValue":
+ case "verifyValue":
+ assertInputValue(param1, param2);
+ return this;
+ case "assertConfirmation":
+ confirm(param1);
+ return this;
+ case "setTimeout":
+ case "pause":
+ // Ignore
+ return this;
+ }
+
+ throw new IllegalArgumentException("Unsupported action: " + action);
+ }
+
+ private void open(String url) {
+ if (url.startsWith("/sonar/")) {
+ goTo(url.substring(6));
+ } else {
+ goTo(url);
+ }
+ }
+
+ private void goTo(String url) {
+ requireNonNull(url, "The url cannot be null");
+
+ url = replacePlaceholders(url);
+
+ URI uri = URI.create(url.replace(" ", "%20"));
+ if (!uri.isAbsolute()) {
+ url = baseUrl + url;
+ }
+
+ System.out.println("goTo " + url);
+ driver.get(url);
+ System.out.println(" - current url " + driver.getCurrentUrl());
+ }
+
+ private LazyDomElement find(String selector) {
+ selector = replacePlaceholders(selector);
+
+ if (selector.startsWith("link=") || selector.startsWith("Link=")) {
+ return find("a").withText(selector.substring(5));
+ }
+
+ By by;
+ if (selector.startsWith("//")) {
+ by = new By.ByXPath(selector);
+ } else if (selector.startsWith("xpath=")) {
+ by = new By.ByXPath(selector.substring(6));
+ } else if (selector.startsWith("id=")) {
+ by = new By.ById(selector.substring(3));
+ } else if (selector.startsWith("name=")) {
+ by = new By.ByName(selector.substring(5));
+ } else if (selector.startsWith("css=")) {
+ by = new By.ByCssSelector(selector.substring(4));
+ } else if (selector.startsWith("class=")) {
+ by = new By.ByCssSelector("." + selector.substring(6));
+ } else {
+ by = new ByCssSelectorOrByNameOrById(selector);
+ }
+
+ return new LazyDomElement(driver, by);
+ }
+
+ private void click(String selector) {
+ find(selector).click();
+ }
+
+ private void check(String selector) {
+ find(selector).check();
+ }
+
+ private void selectFrame(final String id) {
+ if ("relative=parent".equals(id)) {
+ return;
+ }
+
+ System.out.println(" - selectFrame(" + id + ")");
+ _30_SECONDS.execute(new Runnable() {
+ @Override
+ public void run() {
+ driver.switchTo().frame(id);
+ }
+ });
+ }
+
+ private void type(String selector, String text) {
+ find(selector).fill(replacePlaceholders(text));
+ }
+
+ private void keyPressAndWait(String selector, String key) {
+ if (!key.equals("\\13")) {
+ throw new IllegalArgumentException("Invalid key: " + key);
+ }
+ find(selector).pressEnter();
+ }
+
+ private void select(String selector, String text) {
+ if (text.startsWith("label=")) {
+ find(selector).select(text.substring(6));
+ } else {
+ find(selector).select(text);
+ }
+ }
+
+ private void assertElementPresent(String selector) {
+ find(selector).should().beDisplayed();
+ }
+
+ private void assertElementNotPresent(String selector) {
+ find(selector).should().not().beDisplayed();
+ }
+
+ private void storeText(String selector, String name) {
+ find(selector).execute(new ExtractVariable(name));
+ }
+
+ private void storeEval(final String expression, final String name) {
+ // Retry until it's not null and doesn't fail
+ _30_SECONDS.execute(new Runnable() {
+ @Override
+ public void run() {
+ Object result = driver.executeScript("return " + expression);
+ if (result == null) {
+ throw new NotFoundException(expression);
+ }
+ String value = result.toString();
+ variables.put(name, value);
+ }
+ });
+ }
+
+ private void store(String expression, String name) {
+ if (expression.startsWith("javascript{") && expression.endsWith("}")) {
+ storeEval(expression.substring(11, expression.length() - 1), name);
+ } else {
+ throw new IllegalArgumentException("Invalid store expression: " + expression);
+ }
+ }
+
+ private class ExtractVariable implements Consumer<WebElement> {
+ private final String name;
+
+ ExtractVariable(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public void accept(WebElement webElement) {
+ variables.put(name, webElement.getText());
+ }
+
+ public String toString() {
+ return "read value into " + name;
+ }
+ }
+
+ private void assertText(String selector, String pattern) {
+ pattern = replacePlaceholders(pattern);
+
+ if (pattern.startsWith("exact:")) {
+ String expectedText = pattern.substring(6);
+ find(selector).withText(expectedText).should().exist();
+ return;
+ }
+
+ if (pattern.startsWith("regexp:")) {
+ find(selector).should().match(regex(pattern));
+ return;
+ }
+
+ find(selector).should().match(glob(pattern));
+ }
+
+ private void assertNotText(String selector, String pattern) {
+ pattern = replacePlaceholders(pattern);
+
+ if (pattern.startsWith("exact:")) {
+ String expectedText = pattern.substring(6);
+ find(selector).withText(expectedText).should().not().exist();
+ return;
+ }
+
+ if (pattern.startsWith("regexp:")) {
+ find(selector).should().not().match(regex(pattern));
+ return;
+ }
+
+ find(selector).should().not().match(glob(pattern));
+ }
+
+ private static Pattern glob(String pattern) {
+ String regexp = pattern.replaceFirst("glob:", "");
+ regexp = regexp.replaceAll("([\\]\\[\\\\{\\}$\\(\\)\\|\\^\\+.])", "\\\\$1");
+ regexp = regexp.replaceAll("\\*", ".*");
+ regexp = regexp.replaceAll("\\?", ".");
+ return Pattern.compile(regexp, DOTALL | Pattern.CASE_INSENSITIVE);
+ }
+
+ private static Pattern regex(String pattern) {
+ String regexp = pattern.replaceFirst("regexp:", ".*") + ".*";
+ return Pattern.compile(regexp, DOTALL | Pattern.CASE_INSENSITIVE);
+ }
+
+ private void assertTextPresent(String text) {
+ find("body").should().contain(text);
+ }
+
+ private void assertTextNotPresent(String text) {
+ find("body").should().not().contain(text);
+ }
+
+ private void waitForElementPresent(String selector, String text) {
+ if (Strings.isNullOrEmpty(text)) {
+ find(selector).should().exist();
+ } else {
+ find(selector).withText(text).should().exist();
+ }
+ }
+
+ private void waitForVisible(String selector) {
+ find(selector).should().beDisplayed();
+ }
+
+ private void assertInputValue(String selector, String text) {
+ find(selector).should().contain(text);
+ }
+
+ private void confirm(final String message) {
+ System.out.println(" - confirm(" + message + ")");
+
+ _30_SECONDS.execute(new Runnable() {
+ @Override
+ public void run() {
+ driver.switchTo().alert().accept();
+ }
+ });
+ }
+
+ private void assertLocation(String urlPattern) {
+ assertThat(driver.getCurrentUrl()).matches(glob(urlPattern));
+ }
+
+ private String replacePlaceholders(String text) {
+ for (Map.Entry<String, String> entry : variables.entrySet()) {
+ text = text.replace("${" + entry.getKey() + "}", entry.getValue());
+ }
+ return text;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+public interface SeleniumDriver extends org.openqa.selenium.WebDriver, org.openqa.selenium.JavascriptExecutor, org.openqa.selenium.internal.FindsById, org.openqa.selenium.internal.FindsByClassName, org.openqa.selenium.internal.FindsByLinkText, org.openqa.selenium.internal.FindsByName, org.openqa.selenium.internal.FindsByCssSelector, org.openqa.selenium.internal.FindsByTagName, org.openqa.selenium.internal.FindsByXPath, org.openqa.selenium.interactions.HasInputDevices, org.openqa.selenium.HasCapabilities, org.openqa.selenium.TakesScreenshot {
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import com.google.common.base.Joiner;
+import org.openqa.selenium.By;
+
+public abstract class Text {
+ private Text() {
+ // Static utility class
+ }
+
+ public static String doesOrNot(boolean not, String verb) {
+ if (!verb.contains(" ")) {
+ if (not) {
+ return "doesn't " + verb;
+ } else if (verb.endsWith("h")) {
+ return verb + "es";
+ } else {
+ return verb + "s";
+ }
+ }
+
+ String[] verbs = verb.split(" ");
+ verbs[0] = doesOrNot(not, verbs[0]);
+
+ return Joiner.on(" ").join(verbs);
+ }
+
+ public static String isOrNot(boolean not, String state) {
+ return (not ? "is not " : "is ") + state;
+ }
+
+ public static String plural(int n, String word) {
+ return (n + " " + word) + (n <= 1 ? "" : "s");
+ }
+
+ public static String toString(By selector) {
+ return selector.toString().replace("By.selector: ", "").replace("By.cssSelector: ", "");
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.openqa.selenium.remote.RemoteWebDriver;
+import org.openqa.selenium.remote.UnreachableBrowserException;
+
+class ThreadSafeDriver {
+ private ThreadSafeDriver() {
+ // Static class
+ }
+
+ static SeleniumDriver makeThreadSafe(final RemoteWebDriver driver) {
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ driver.quit();
+ } catch (UnreachableBrowserException e) {
+ // Ignore. The browser was killed properly
+ }
+ }
+ }));
+
+ return (SeleniumDriver) Proxy.newProxyInstance(
+ Thread.currentThread().getContextClassLoader(),
+ findInterfaces(driver),
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getName().equals("quit")) {
+ return null; // We don't want anybody to quit() our (per thread) driver
+ }
+
+ try {
+ return method.invoke(driver, args);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+ });
+ }
+
+ private static Class[] findInterfaces(Object driver) {
+ Set<Class<?>> interfaces = new LinkedHashSet<>();
+
+ interfaces.add(SeleniumDriver.class);
+
+ for (Class<?> parent = driver.getClass(); parent != null; ) {
+ Collections.addAll(interfaces, parent.getInterfaces());
+ parent = parent.getSuperclass();
+ }
+
+ return interfaces.toArray(new Class[interfaces.size()]);
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package util.selenium;
+
+import org.openqa.selenium.WebElement;
+
+class WebElementHelper {
+ WebElementHelper() {
+ // Static class
+ }
+
+ public static String text(WebElement element) {
+ String text = element.getText();
+ if (!"".equals(text)) {
+ return nullToEmpty(text);
+ }
+
+ return nullToEmpty(element.getAttribute("value"));
+ }
+
+ private static String nullToEmpty(String text) {
+ return (text == null) ? "" : text;
+ }
+}