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.