FluentLenium integration
dan.j.allen Nov 20, 2012 12:22 PMJan 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.