5. 50 Geb contributors
• Robert Fletcher
• Peter Niederwieser
• Alexander Zolotov
• Christoph Neuroth
• Antony Jones
• Jan-Hendrik Peters
• Tomás Lin
• Jason Cahoon
• Tomasz Kalkosiński
• Rich Douglas Evans
• Ian Durkan
• Colin Harrington
• Bob Herrmann
• George T Walters II
• Craig Atkinson
• Andy Duncan
• John Engelman
• Michael Legart
• Graeme Rocher
• Craig Atkinson
• Ken Geis
• Chris Prior
• Kelly Robinson
• Todd Gerspacher
• David M. Carr
• Tom Dunstan
• Brian Kotek
• David W Millar
• Ai-Lin Liou
• Varun Menon
• Anders D. Johnson
• Hiroyuki Ohnaka
• Erik Pragt
• Vijay Bolleypally
• Pierre Hilt
• Yotaro Takahashi
• Jochen Berger
• Matan Katz
• Victor Parmar
• Berardino la Torre
• Markus Schlichting
• Andrey Hitrin
• Leo Fedorov
• Chris Byrneham
• Aseem Bansal
• Tomasz Przybysz
• Brian Stewart
• Jacob Aae Mikkelsen
• Patrick Radtke
• Leonard Brünings
6. Breaking changes in 1.0
• Methods like isDisplayed() and
text() throw an exception when called
on multi element navigators
• Weakly typed module() and
moduleList() removed
• isDisabled() and isReadOnly()
removed from Navigator
9. Module is-a Navigator
• Modules wrap around their base
Navigator
• All Navigator methods can be called on
Module instances
• Navigator methods can be used in
module implementations
10. Module is-a Navigator
<html>
<form method="post" action="login">
...
</from>
</html>
class LoginFormModule extends Module {
static base = { $("form") }
}
class LoginPage extends Page {
static content = {
form { module LoginFormModule }
}
}
12. Overriding value() on module
Class DatepickerModule extends Module {
LocalDate value() {
...
}
Navigator value(LocalDate date) {
...
}
}
class DatepickerPage extends Page {
static content = {
date { module DatepickerModule }
}
}
13. Overriding value() on module
to DatepickerPage
date.value(LocalDate.of(2017, 4, 1))
assert date.value() == LocalDate.of(2017, 4, 1)
to DatepickerPage
date = LocalDate.of(2017, 4, 1)
assert date == LocalDate.of(2017, 4, 1)
14. Advanced navigation and types
• Page.convertToPath(Object[]) is
used to translate arguments passed to
Browser.to() into a path
• Consider overloading convertToPath()
for more expressive navigation
15. Advanced navigation and types
class PostPage extends Page {
static url = “posts”
String convertToPath(Post post) {
“${post.id}/${post.title.toLowerCase().replaceAll(“ ”, “-”)}”
}
}
def post = new Post(id: 1, title: “My first post”)
to PostPage, post
assert title == post.title
16. Parameterised pages
• Pages can be instantiated and passed to
via(), to() and at()
• Useful when you need to differ
implementation based on the object
represented by the page
17. Parameterised pages navigation
class PostPage extends Page {
Post post
String getPageUrl() {
“posts/${post.id}/${post.title.toLowerCase().replaceAll(“ ”, “-”)}”
}
}
def post = new Post(id: 1, title: “My first post”)
to(new PostPage(post: post))
assert title == post.title
18. Navigators are iterable
• Navigators are backed by a collection of
WebElements
• Navigators implement
Iterable<Navigator>
21. Injecting javascript into page
js.exec '''
var url = 'https://code.jquery.com/jquery-3.2.1.min.js';
var scriptElement = document.createElement('script');
scriptElement.setAttribute('src', url);
document.head.appendChild(scriptElement);
'''
22. Navigator’s elements in js
void waitForCssTransition(Navigator navigator, WaitingSupport waiting, JavascriptInterface js,
Closure trigger) {
js.exec(navigator.singleElement(), '''
var o = jQuery(arguments[0]);
window.setTransitionFinishedClass = function() {
$(this).addClass('transitionFinished');
}
o.bind('transitionend', window.setTransitionFinishedClass);
''')
try {
trigger.call()
waiting.waitFor { hasClass('transitionFinished') }
} finally {
js.exec(navigator.singleElement, '''
var o = jQuery(arguments[0]);
o.unbind('transitionend', window.setTransitionFinishedClass);
o.removeClass('transitionFinished')
window.setTransitionFinishedClass = undefined;
''')
}
}
23. Alternatives to inheritance
• Using inheritance for code sharing leads to
complex inheritance structures
• @Delegate can be used for code sharing
via delegation
• Traits can be used for code sharing
24. Alternatives to inheritance
class TransitionSupport {
private final Navigator navigator
private final WaitingSupport waiting
private final JavascriptInterface js
TransitionSupport(Navigator navigator, WaitingSupport waiting, JavascriptInterface js) {
...
}
void waitForCssTransition(Closure trigger) {
...
}
}
class TransitioningModule extends Module {
@Delegate
TransitionSupport transitionSupport = new TransitionSupport(this, this, js)
}
29. Strongly typed Geb code
• Better authoring, i.e. autocompletion
• Better navigation, i.e. going to method
definition, finding usages
• Refactoring support
30. What IntelliJ understands?
• Delegation to Browser methods in specs
• Delegation to Page methods in specs
• Content definitions
31. Tracking current page type
• Capture current page in a variable
• Use return values of via(), to() and
at()
• Return new page instance from methods
triggering page transitions
32. Tracking current page
def homePage = to HomePage
homePage.loginPageLink.click()
def loginPage = at LoginPage
def securePage = loginPage.login("user", "password")
36. What are modules good for?
• More than reuse
• Modeling logical components
• Hiding component structure from tests
• Hiding complex interactions from tests
37. Invest time in fixture builders
• Aim for easy, expressive and flexible data
setup
• Keep data setup close to tests
• Setup only the data necessary for the test
• Groovy Remote Control can be very helpful
• Fixtures are not not only about data
38. Geb’s test harness html fixture
def “can access element classes”() {
given:
html {
div(id: "a", 'class': "a1 a2 a3")
div(id: "b", 'class': "b1”)
}
expect:
$("#a").classes() == ["a1", "a2", "a3"]
$("#b").classes() == ["b1”]
}
39. Example data fixture DSL
bootstrapper.post {
id = “69f10bb6-118e-11e7-93ae-92361f002671”
title = “My first post”
tags = [“Groovy”, “Geb”]
author {
firstName = “Marcin”
lastName = “Erdmann”
}
}
40. Use real browser in CI
• Available headless drivers are either
limited, not developed anymore or not
properly tested
• Use a real browser with a virtual frame
buffer, i.e. Xvfb
• Use a cloud browser provider
49. Cloud browser providers
• Gradle plugins for driving browsers with
Geb in BrowserStack and Suace Labs
• Can test applications via a tunnel which are
not available on the Internet
50. Using attributes map selectors
• Map selectors are now translated to CSS
attribute selectors
• As quick as using css selectors but reads
better especially when using dynamic
values
52. Selecting by text is slow!
• Selecting by text is a Geb extension not
something that CSS supports
• Narrow down selected elements as much
as possible when selecting by text