bobcat icon indicating copy to clipboard operation
bobcat copied to clipboard

Support for Applitools Eyes

Open yohannesalazar opened this issue 8 years ago • 10 comments

Hi Bobcat stars,

Am looking to integrate to Applitools for UI comparison...

can some one point me on which class I need to instantiate the webDriver as per Applitools example code below ?

  1. I tried to do it within my projects like ` @Inject private Eyes eyes; @Inject private WebDriver webDriver;

    public LandingPage open() { eyes.setApiKey("cR99frVt2NBrYG3NYaS9Xasasasas100N20CId5JI110"); eyes.open(webDriver, "Hello World!", "My first Appli Selenium Java test!", new RectangleSize(800, 600)); webDriver.get("http://www.bbc.co.uk/");`

But got error cucumber.runtime.CucumberException: Failed to instantiate public cucumber.runtime.java.JavaBackend(cucumber.runtime.io.ResourceLoader) with [cucumber.runtime.io.MultiLoader@13d9b21f]

Looks like I need it to be added at bobcat level rather than my project , so please if you could direct me as you may have done POC already

Below the hello world example that works non-bobcat plain selenium

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import com.applitools.eyes.selenium.Eyes;
import com.applitools.eyes.RectangleSize;

public class HelloWorld {

   public static void main(String[] args) {

       // Open a Chrome browser.
       WebDriver driver = new ChromeDriver();

       // Initialize the eyes SDK and set your private API key.
       Eyes eyes = new Eyes();
       eyes.setApiKey("YOUR_API_KEY");

       try{

           // Start the test and set the browser's viewport size to 800x600.
           eyes.open(driver, "Hello World!", "My first Selenium Java test!",
                   new RectangleSize(800, 600));

           // Navigate the browser to the "hello world!" web-site.
           driver.get("https://applitools.com/helloworld");

           // Visual checkpoint #1.
           eyes.checkWindow("Hello!");

           // Click the "Click me!" button.
           driver.findElement(By.tagName("button")).click();

           // Visual checkpoint #2.
           eyes.checkWindow("Click!");

           // End the test.
           eyes.close();

       } finally {

           // Close the browser.
           driver.quit();

           // If the test was aborted before eyes.close was called, ends the test as aborted.
           eyes.abortIfNotClosed();
       }

   }

}

yohannesalazar avatar Apr 18 '17 15:04 yohannesalazar

Further info !

I just tried only adding a dependency

  <dependency>
            <groupId>com.applitools</groupId>
            <artifactId>eyes-selenium-java3</artifactId>
            <version>3.1</version>
        </dependency>

To my project , and with out doing any code change what so ever existing bobcat scenarios don't run ... It was only just simply adding a dependency that breaks existing scenarios that run fine before it as added! the error message is again same as below!

hope that is a clue , can some one help why ?

cucumber.runtime.CucumberException: Failed to instantiate public cucumber.runtime.java.JavaBackend(cucumber.runtime.io.ResourceLoader) with [cucumber.runtime.io.MultiLoader@28ec166e]

	at cucumber.runtime.Reflections.newInstance(Reflections.java:44)
	at cucumber.runtime.Reflections.instantiateSubclasses(Reflections.java:30)
	at cucumber.runtime.Runtime.loadBackends(Runtime.java:98)
	at cucumber.runtime.Runtime.<init>(Runtime.java:65)
	at cucumber.api.junit.Cucumber.createRuntime(Cucumber.java:78)
	at cucumber.api.junit.Cucumber.<init>(Cucumber.java:58)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
	at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
	at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
	at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
	at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
	at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at cucumber.runtime.Reflections.newInstance(Reflections.java:41)
	... 19 more
Caused by: cucumber.runtime.CucumberException: Failed to instantiate public cucumber.runtime.java.guice.impl.GuiceFactory() throws java.io.IOException with []
	at cucumber.runtime.Reflections.newInstance(Reflections.java:44)
	at cucumber.runtime.Reflections.instantiateSubclasses(Reflections.java:30)
	at cucumber.runtime.Reflections.instantiateExactlyOneSubclass(Reflections.java:16)
	at cucumber.runtime.java.ObjectFactoryLoader.loadObjectFactory(ObjectFactoryLoader.java:28)
	at cucumber.runtime.java.JavaBackend.<init>(JavaBackend.java:65)
	... 24 more
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at cucumber.runtime.Reflections.newInstance(Reflections.java:41)
	... 28 more
Caused by: java.lang.NoClassDefFoundError: io/appium/java_client/TouchShortcuts
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.getDeclaredConstructors0(Native Method)
	at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
	at java.lang.Class.getDeclaredConstructors(Class.java:2020)
	at com.google.inject.spi.InjectionPoint.forConstructorOf(InjectionPoint.java:243)
	at com.google.inject.internal.ConstructorBindingImpl.create(ConstructorBindingImpl.java:96)
	at com.google.inject.internal.InjectorImpl.createUninitializedBinding(InjectorImpl.java:629)
	at com.google.inject.internal.InjectorImpl.createJustInTimeBinding(InjectorImpl.java:845)
	at com.google.inject.internal.InjectorImpl.createJustInTimeBindingRecursive(InjectorImpl.java:772)
	at com.google.inject.internal.InjectorImpl.getJustInTimeBinding(InjectorImpl.java:256)
	at com.google.inject.internal.InjectorImpl.getBindingOrThrow(InjectorImpl.java:205)
	at com.google.inject.internal.InjectorImpl.getInternalFactory(InjectorImpl.java:853)
	at com.google.inject.internal.BoundProviderFactory.notify(BoundProviderFactory.java:44)
	at com.google.inject.internal.ProcessedBindingData.runCreationListeners(ProcessedBindingData.java:50)
	at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:133)
	at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:106)
	at com.google.inject.Guice.createInjector(Guice.java:95)
	at com.google.inject.Guice.createInjector(Guice.java:72)
	at com.google.inject.Guice.createInjector(Guice.java:62)
	at com.rx.automation.CucumberInjectorSource.getInjector(CucumberInjectorSource.java:13)
	at cucumber.runtime.java.guice.impl.GuiceFactory.<init>(GuiceFactory.java:22)
	... 33 more
Caused by: java.lang.ClassNotFoundException: io.appium.java_client.TouchShortcuts
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 77 more

yohannesalazar avatar Apr 18 '17 16:04 yohannesalazar

Hi, @yohannesalazar!

Regarding your error, it seems like a problem with Appium dependencies: java.lang.ClassNotFoundException: io.appium.java_client.TouchShortcuts. I guess you have Appium added hooked up to your project? Taking a look at dependencies in Eyes, you can see that they are also including it, the RELEASE version actually - you would need to exclude that dependency.

I've integrated Applitools Eyes with Bobcat couple months ago as part of a PoC, though I was using version 1 of their SDK, which required using wrapped WebDriver instance. To achieve that, we actually improved Bobcat to be more extendable - as one of the steps, you had to implement WebDriverModifier interface (link). Since then, I see they have released two additional SDK versions and from what I've read here this is not the case anymore - which actually could simplify some issues I have encountered using version 1 :).

Let us know if the above will solve your problems. I'm more than happy to help you out in integrating Eyes with our cat ;)

mkrzyzanowski avatar Apr 19 '17 04:04 mkrzyzanowski

Hi Michał ,

Thanks for that and will give it a go based on your advise and will let you know how it goes:)

yohannesalazar avatar Apr 20 '17 09:04 yohannesalazar

Hi Michał,

I had some time today to look at this , and thanks removing the dependency was one step foreword..

The error I am getting now is something like

com.applitools.eyes.EyesException: Driver is not a RemoteWebDriver (com.cognifide.qa.bb.provider.selenium.webdriver.close.ClosingAwareWebDriverWrapper)
	at com.applitools.eyes.selenium.Eyes.open(Eyes.java:292)
	at com.applitools.eyes.selenium.Eyes.open(Eyes.java:242)

then I tried to add webDriver = new ChromeDriver();

this also has error

org.openqa.selenium.TimeoutException: Expected condition failed: waiting for com.cognifide.qa.bb.provider.selenium.BobcatWebDriverWait$$Lambda$44/156127720@70730db (tried for 30 second(s) with 500 MILLISECONDS interval)
	at org.openqa.selenium.support.ui.WebDriverWait.timeoutException(WebDriverWait.java:80)
	at org.openqa.selenium.support.ui.FluentWait.until(FluentWait.java:232)
	at com.cognifide.qa.bb.provider.selenium.BobcatWebDriverWait.until(BobcatWebDriverWait.java:70)
	at com.rx.automation.pageobjects.sitevisitorpage.LandingPage.acceptCookies(LandingPage.java:122)
	at com.cognifide.qa.bb.loadable.hierarchy.PageObjectInterceptor.invoke(PageObjectInterceptor.java:42)

My code looks like

    public LandingPage open() {
        //it need to be a remoteWebdriver
        //webDriver  = new ChromeDriver();

        Eyes eyes = new Eyes();
        eyes.setApiKey("cR99frVt2NBXXXXXXXXXXXXXXXXXXXX5JI110");
        webDriver = eyes.open(webDriver, "Applitools", "Test Web Page", new RectangleSize(1024, 768));
        webDriver.get(publishUrl);
        webDriver.manage().deleteAllCookies();
        return this;
    }

What it does is open two browsers , one with blank url only "data:," on url as selenium opens the page and the other have publishUrl but does not do anything , it looks like both bobcat and eye are opening one browser each ????

yohannesalazar avatar Apr 25 '17 11:04 yohannesalazar

Hi Bobcat star .. any update on this question ?

yohannesalazar avatar May 05 '17 15:05 yohannesalazar

Hi @yohannesalazar, sorry for late response!

I've investigated the issue and the problem is that Eyes require a RemoteWebDriver to be passed to the open() method (as you can see here: Eyes.java; similar issue: link). Bobcat wraps the WebDriver instance with CloseAwareWebdriver which leads to the instanceof failing.

Fortunately, we can instantiate Eyes before the wrapping is done (this capability was actually introduced as a result of the previous Eyes POC :)). The following is an example of how can this be achieved using WebDriverModifier:

public class EyesModifier implements WebDriverModifier {
  @Inject
  private Eyes eyes;

  @Override
  public boolean shouldModify() {
    return true;
  }

  @Override
  public WebDriver modify(WebDriver webDriver) {
    return eyes.open(webDriver, "APP_NAME", "TEST_NAME");
  }
}

and for providing the Eyes itself:

@ThreadScoped
public class EyesProvider implements Provider<Eyes> {
  private static final String API_KEY = "YOUR_API_KEY"; //or better, use properties for this

  private Eyes cachedEyes;

  @Override
  public Eyes get() {
    if (cachedEyes == null) {
      cachedEyes = new Eyes();
    }
    return cachedEyes;
  }

  private Eyes create() {
    Eyes eyes = new Eyes();
    eyes.setApiKey(API_KEY);
    //here you can apply other settings for Eyes
    return eyes;
  }
}

The above classes require following changes in your project's Guice module:

    Multibinder<WebDriverModifier> webDriverModifiers = Multibinder.newSetBinder(binder(), WebDriverModifier.class);
    webDriverModifiers.addBinding().to(EyesModifier.class);
    bind(Eyes.class).toProvider(EyesProvider.class);

Unfortunately, this leaves you with a problem that I've encountered during that initial POC: obtaining/generating the test name so it can be used during initialization.

mkrzyzanowski avatar May 09 '17 13:05 mkrzyzanowski

Hi Michał,

I have changed my project for a few sprints and back on my this project again , sorry did not see that you have responded to this issue on appliTools ... I will be looking at it soon and follow your advice and solution ... Many Thanks and will let you know when this task is picked..

yohannesalazar avatar Aug 16 '17 18:08 yohannesalazar

@mkrzyzanowski Can you pl prioritize this PR. We are planning to do a POC on Applitools in this quarter.

DeChrish avatar Jan 06 '20 18:01 DeChrish

@mkrzyzanowski Any ETA to merge the Aplitools module?

DeChrish avatar Jan 28 '20 21:01 DeChrish

@mkrzyzanowski Any ETA to merge the Aplitools module?

DeChrish avatar Feb 26 '20 00:02 DeChrish