5 Replies Latest reply on Nov 22, 2012 3:39 PM by mathilde_lemee

    FluentLenium integration

    dan.j.allen

      Jan Papousek and Mathilde Lemee hacked on an integration between Arquillian Drone and FluentLenium last week at Hackergarten. In the spirit of the event, they committed a working prototype by the days end! Nice work!

       

      You can find the integration in Jan's GitHub repository: https://github.com/papousek/arquillian-extension-fluentlenium (At the moment I have a pull request pending on that repository w/ some small corrections).

       

      Here's how a test case that uses this extension looks atm:

       

      Using the native API:

       

      RunWith(Arquillian.class)
      public class LoginScreenFluentLeniumTest {
          private static final String WEBAPP_SRC = "src/main/webapp";
      
          @Deployment(testable = false)
          public static WebArchive createDeployment() {
              return Deployments.createLoginScreenDeployment();
          }
      
          @Drone
          WebDriver browser;
      
          @ArquillianResource
          URL deploymentUrl;
      
          @ArquillianResource
          FluentLenium fluent;
      
          @Test
          public void should_login_with_valid_credentials_using_fluentlenium() throws Exception {
              fluent.withDefaultUrl(deploymentUrl.toString()).goTo("login.jsf");
              assertThat(fluent.title()).isEqualTo("Log in");
              fluent.fill("[id='loginForm:username']").with("user1");
              fluent.fill("[id='loginForm:password']").with("demo");
              fluent.click("[id='loginForm:login']");
      
              // alternate syntax to escape compound JSF ids
              // - see http://stackoverflow.com/questions/2142929/is-it-possible-to-change-the-element-id-separator-in-jsf
              // fluent.fill("#loginForm\\:username").with("user1");
              // fluent.fill("#loginForm\\:password").with("demo");
              // fluent.click("#loginForm\\:login");
      
              assertThat(fluent.title()).isEqualTo("Home");
              assertThat(fluent.find("li", withText().contains("Welcome!")).size()).isEqualTo(1);
              assertThat(fluent.find("p", withText().equalTo("You are signed in as user1.")).size()).isEqualTo(1);
          }
      }
      

       

      Using page objects:

       

      @RunWith(Arquillian.class)
      public class StructuredLoginScreenFluentLeniumTest {
      
          @Deployment(testable = false)
          public static WebArchive createDeployment() {
              return Deployments.createLoginScreenDeployment();
          }
      
          @Drone
          WebDriver browser;
      
          @ArquillianResource
          URL deploymentUrl;
      
          @ArquillianResource
          FluentLenium fluent;
      
          @Test
          public void should_login_with_valid_credentials_using_fluentlenium() throws Exception {
              fluent.withDefaultUrl(deploymentUrl.toString());
      
              LoginPage loginPage = fluent.goToPage(LoginPage.class);
              loginPage.isAt();
      
              loginPage.login("invalid", "invalid");
              loginPage.isAt();
              loginPage.hasMessage("Incorrect username and password combination.");
      
              loginPage.login("user1", "demo");
      
              HomePage homePage = fluent.createPage(HomePage.class);
              homePage.isAt();
              homePage.hasMessage("Welcome!");
              homePage.hasParagraph("You are signed in as user1.");
          }
      }
      

       

      Currently, it's not possible to use the @Page injections from FluentLenium.

       

      As you can see, this integration is going to be a real asset to the Arquillian community.

       

      There are some improvements that I'd liked to see. Some of these enhancments may require changes in either Arquillian or Arquillian Drone.

       

      • Is @ArquillianResource the right fit for injecting FluentLenium? Far better would be for @Drone to support a FluentLenium injection, hiding the WebDriver injection altogether
      • Injector needs a reference to the test class so that it can properly initialize the @Page injections
        • Perhaps switch to using a TestEnricher instead of a ResourceProvider?
      • If test class inherits from FluentAdapter, it would be nice to initialize the test class appropriately w/o needing to inject FluentLenium
        • This could be handled in the Before observer, however atm it requires quite a bit of reflection to get the job done
      • The baseUrl in Fluent could be set automatically by reading from the @ArquillianResource URL field (or equivalent)
        • Arquillian should expose a resolver to lookup the deploymentUrl easily from an extension, such as this one (ARQ-1205)
      • WebDriver chokes on By.cssSelector() (which FluentLenium uses) when id contains a colon character (typical in JSF pages)
        • UPDATE: The colon can be escaped using a backslash (typed as a double backslash in a Java string). Alternatively, a different separate can be specified in JSF 2.0 using the javax.faces.SEPARATOR_CHAR init-param in web.xml.

       

      Let's keep this integration moving forward. I'd like to see it merged upstream to the arquillian organization, but I first want to see if we can address the @Drone injection point and how that affects what we call the project.

        • 1. Re: FluentLenium integration
          lfryc

          This is really cool addition to Arquillian Galaxy, especially in space of client testing weapons. Great job Jan and Mathilde.

           

          What I would love to achieve is

           

          • FluentLenium's style of compact syntax without need of accessing FluentLenium object
            • either by abstract class extending or imported static methods from utility class (similarly to Graphene).
          • integration with Graphene
            • use of Page Fragments

           

          The combination of FluentLenium and Page Fragments might be big win for both compactness and robustness at once.

          • 2. Re: FluentLenium integration
            lfryc

            Dan Allen wrote:

             

            • WebDriver chokes on By.cssSelector() (which FluentLenium uses) when id contains a colon character (typical in JSF pages)
              • Likely this is an issue w/ FluentLenium or perhaps a WebDriver issue

            This is likely JSF / CSS mismatch issue - CSS use colons as pseudo-selectors.

             

            Regrettably, this can't be generically fixed by pre-processing / automatic escaping.

            • 3. Re: FluentLenium integration
              dan.j.allen

              Lukáš Fryč wrote:

               

              Dan Allen wrote:

               

              • WebDriver chokes on By.cssSelector() (which FluentLenium uses) when id contains a colon character (typical in JSF pages)
                • Likely this is an issue w/ FluentLenium or perhaps a WebDriver issue

              This is likely JSF / CSS mismatch issue - CSS use colons as pseudo-selectors.

               

              Regrettably, this can't be generically fixed by pre-processing / automatic escaping.

               

              Ah, of course. I was confused because the By.id() selector supports the use of a colon. As it turns out, it's possible to escape the colon in the CSS selector, which is what By.id() must be effectively doing. Another option is to change the separator used by JSF (available since JSF 2.0). I've updated the original post to outline these options.

              • 4. Re: FluentLenium integration
                dan.j.allen

                Lukáš Fryč wrote:

                 

                This is really cool addition to Arquillian Galaxy, especially in space of client testing weapons. Great job Jan and Mathilde.

                +1

                 

                What I would love to achieve is

                 

                • FluentLenium's style of compact syntax without need of accessing FluentLenium object
                  • either by abstract class extending or imported static methods from utility class (similarly to Graphene).
                • integration with Graphene
                  • use of Page Fragments

                 

                The combination of FluentLenium and Page Fragments might be big win for both compactness and robustness at once.

                 

                Totally. The two projects are definitely going to push each other forward, which is great! That's exactly the type of collaboration we like to see in the Arquillian Universe

                 

                I agree that the injection of the FluentLenium object is not ideal as it breaks the spirit of the natural language. I'm not a huge fan of static imports (mainly becuase the IDE is still so dumb about them), but let's play around with different ways of achieving the same experience as FluentTest.

                 

                Page Fragments FTW. I think that idea can be applied to FluentLenium equally as well as Graphene.

                • 5. Re: FluentLenium integration
                  mathilde_lemee

                  We discussed that a bit when writing FluentLenium and it appears to us that we want to have the availability to launch different test in parallel (class parallel for FluentTest and both method and class when using IsolatedTest). I think that's the main reason about the fact that we don't use static import.

                  But you've right, it's simplify the way of writing test.

                   

                  We also have kind of a page fragment that is just partially implemented. But yes, that's a good idea.